1.相关理论
socket:顾名思义接口,套接字,他不是协议是而是一个抽象层,位置在服务层下与传输层之上的中间。socket主要解决计算机系统多个程序占用同一个端口问题。当多个程序公用一个80端口时,当有数据传输是不知道具体哪个程序接受数据的难题。如果说tcp/udp定义了数据怎么传输,那么socket就是具体执行者。socekt定义了点到点双向传输机制。他是成对出现 点a一个socket 点b一个socekt.
2.使用socket前提
2.1)协议 两端定义 比如TCP/UDP
2.2)点A端口号
2.3. 点A的IP地址
2.4) 点B端口号
2.5) 点B的IP地址
3.ios端socket类型
iOS端socket框架中有以下几种类型的socekt 层次有高向低
1.CFNetWork
CFNetWrok是ios中比较底层的网络框架,c语言编写,可以控制更底层的东西,如常用的网络协议和socket通讯等,CFNetWork已经近于UNIX的socket通信。是CoreFoundation框架对BDSsocket的封装。但他本质上还是应用层上的封装。
2.CFSocket
CFSocket是在系统CFNetWork.framework框架中,他是基于伯克利进行抽象和封装,CFSocket中包含了少数开销,几乎可以提供伯克利的一切功能,并把socket集成进一个少数循环当中。CFsocket不仅限于流的socket.他可以处理任意类型的socket.
3.BSDsocket
伯克利套接字,纯c编写进程间通信库,可以跨平台。他允许不同主机或同一计算机上不同进程之间的通信,支持多种I/O设备和驱动。UNIX最底层的socket就是基于BSDSocekt的,苹果系统底层便是基于UNIX.
- 三种类型socekt相关函数
需要导入框架
#import <sys/socket.h> 核心函数库
#import <netinet/in.h> //AF_INET和AF_INET6地址家族和他们对应的协议家族PF_INET和PF_INET6.在互联网变成中广发使用,包含ip地址以及tcp/udp端口号。//<sys/un.h>PF_UNIX/PF_LOCAL地址家族,用于运行在一台计算机上的程序间的本地通信,不用在网络中
#import <arpa/inet.h> //和ip地址相关的一些函数
<netdb.h>//把协议名和主机名转化为数字的一些函数
#import <ifaddrs.h>
#import <CoreFoundation/CoreFoundation.h>
1.CFNetWork
CFNetwork结构:CFFTP,CFHTSTPAuthentication,CFHTTP,CFSocketStream
(CFStream,CFSocket)Core Foundation
CFFTP:对用FTP协议通信的封装,能下载,上传文件和目录到FTP服务器,
CFFHTTP:是对HTTP协议的抽象,主要对象是CFHTTPMessageRef(类似于NSURLRequest)我们构建CFHTTPMessageRef同样包含几个元素
必须元素:请求方法(类型CFStringRef):POST,GET,DELETE等
请求URL地址(类型CFURLRef):http://www.baidu.com
请求的HTTP版本(类型CFStringRef):通常使用kCFHTTPVersion1_1
kCFAllocatorDefault用于创建消息引用的指定默认系统内存分配起
可选参数
body体(类型CFDataRef)
消息头部,如User-Agent等
demo
-(void)CFNetwok{
NSURL *url = [NSURL URLWithString:@"url"];
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(loadDataFormServerWithURL:) object:url];
[thread start];
NSString *host = url.host;
long long port = [[url port] longLongValue];
//流上下文
CFStreamClientContext ctx = {0,(__bridge void *)(self),NULL,NULL,NULL};
CFOptionFlags registeredEvents = (kCFStreamEventHasBytesAvailable|kCFStreamEventEndEncountered|kCFStreamEventErrorOccurred);
CFReadStreamRef readStream;
CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, (__bridge CFStringRef)host, (UInt32)port, &readStream,NULL);
if (CFReadStreamSetClient(readStream, registeredEvents,socketCallback,&ctx)) {
CFReadStreamScheduleWithRunLoop(readStream, CFRunLoopGetCurrent(), kCFRunLoopCommonModes);
}else{
// [self netwokFailedWithErrorMessage:@"fffffff"];
return;
}
if (CFReadStreamOpen(readStream) == NO) {
return;
}
CFErrorRef error =CFReadStreamCopyError(readStream);
if (CFErrorGetCode(error) != 0) {
NSString *error = [NSString stringWithFormat:@"errorcode %@",error];
}
CFRelease(error);
return;
CFRunLoopRun();
}
回调函数
void socketCallback(CFReadStreamRef stream,CFStreamEventType event,void *myPtr){
ViewController *controller = (__bridge ViewController *)myPtr;
switch (event) {
case kCFStreamEventHasBytesAvailable:{
while (CFReadStreamHasBytesAvailable(stream)) {
CFIndex bufferSize = 0;
UInt8 buffer[bufferSize];
//int numBytesRead = CFReadStreamRead(stream, buffer, kBuffsrSize);
long long numBytesRead = CFReadStreamRead(stream, buffer, bufferSize);
// [controller didReceiveData:[NSData dataWithBytes:buffer length:numBytesRead]];
NSLog(@"%@",[NSData dataWithBytes:buffer length:numBytesRead]);
}
break;
}
default:
break;
}
}
2.CFSocket
-(void)CFsocket{
//创建socket 参数一:版本号必须0,参数二:info 一个指向任意程序定义数据的指针,
CFSocketContext socketContext = {0,(__bridge void *)(self),NULL,NULL,NULL};
CFSocketRef socketRef = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCF, kCFSocketConnectCallBack, ServerConnectCallBack, &socketContext);
struct sockaddr_in Socketaddr;
//memst:将addr中所有字节用0替代并返回addr,作用是一段内存块中填充某个给定的值,他是对较大结构题或数组进行清零操作的一种最快方式
memset(&Socketaddr, 0, sizeof(Socketaddr));
//长度
Socketaddr.sin_len = sizeof(Socketaddr);
//协议族 用AF_INET-互联网,tcp/udp等
Socketaddr.sin_family = AF_INET;
//d端口号(使用网络字节顺序)
Socketaddr.sin_port = htons(111);//@"这个端口随便写"
//存储ip地址。 inet_addr()的功能是将一点分十进制ipd地址转换成一个长整型数
Socketaddr.sin_addr.s_addr = inet_addr("192.168.103.244");//这个时ip地址,不能随便乱写
//将地址转化为CFDataRef
CFDataRef dataRef = CFDataCreate(kCFAllocatorDefault, (UInt8 *)&Socketaddr, sizeof(Socketaddr));
//连接
//参数一:连接的socket,参数二:连接的socket包含的地址参数,参数三:连接超时时间,如果负,则不尝试连接而是连接放在后台,如果socket消息类型为kCFSocketConnectCallBack,将会在连接成功或失败的时候在后台触发回调函数
CFSocketConnectToAddress(socketRef, dataRef, -1);
//加入循环中 获取当前线程的runloop
CFRunLoopRef runloopRef = CFRunLoopGetCurrent();
//把socket包装成CFRunloopSource 最后一个参数是指有多个runloopsource通过一个unloop把顺势顺序,
CFRunLoopSourceRef sourceRef = CFSocketCreateRunLoopSource(kCFAllocatorDefault, socketRef, 0);
//加入循环 参数1:运行循环管,参数2:增加的运行循环源,他会被retain一次,参数三:用什么模式把source加入到runloop里面
CFRunLoopAddSource(runloopRef, sourceRef, kCFRunLoopCommonModes);
//之前倍retain一次,这里释放掉
CFRelease(sourceRef);
//发送数据
const char *data = [@"abcdefg" UTF8String];
//成功则返回实际传送出去的字符数,失败返回-1
long sendData = send(CFSocketGetNative(socketRef), data, strlen(data) + 1, 0);
NSLog(@"%ld",sendData);
}
回调函数
void ServerConnectCallBack(CFSocketRef s, CFSocketCallBackType callbackType,CFDataRef address,const void *data,void *info){
ViewController *vc = (__bridge ViewController *)(info);
//判断是不是NULL
if (data !=NULL) {
printf("连接失败");
[vc performSelector:@selector(_releseSocket) withObject:nil];
}else{
printf("连接成功");
[vc performSelector:@selector(_readStreamData) withObject:nil];
}
}
//清空socket
-(void)_releseSocket{
CFSocketRef socektRef = NULL;//这句假的要把上面的socketRef设置成全局变量
if (socektRef) {
CFRelease(socektRef);
}
socektRef = NULL;
}
//连接成功 读取数据
-(void)_readStreamData{
CFSocketRef socektRef = NULL;//这句假的要把上面的socketRef设置成全局变量
//接受数据
char buffer[512];
long readData;
while ((readData = recv(CFSocketGetNative(socektRef), buffer, sizeof(buffer), 0))) {
NSString *conet = [[NSString alloc]initWithBytes:buffer length:readData encoding:NSUTF8StringEncoding];
NSLog(@"%@",conet);
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"aaaa");
});
}
}
3.BSDSocket伯克利套接字
相关函数
socket(PF_INET,SOCK_STREAM,0);函数原型
函数讲解
socket(确定协议族如PF_INET是ipv4 PF_INET6 是ipv6 PF_UNIX是本地,SOCK_STREAM(可靠面向连接的服务流)/SOCK_DGRAM(数据包服务)/SOCK_SEQPACKET(可靠的有序的分组服务)/SOCK_RAW(网络层原始协议),IPPROTO_TCP/SCTP/UDP/DCCP确定使用的运输层,用0可以默认一个协议有系统选择) 创造某种类型的套接字,分配一些系统资源,用返回的整数识别
connect() 用在客户端,给套接字分配一个空闲的端口号,比如一个tcp套接字
send() and recv(),or write() and read(), or sendto() and recvfrom() 用来接送和发送数据
close()关闭连接,系统释放资源
gethostbyname() and gethostbyaddr()用来解析主机名和地址
gethostbyname(指定主机名如www.baidu.com)
gethostbyaddr(主机地址,len给出addr的长度以字节为单位,指定地址类型比如AF_INET)
bind() 一般永在服务器,和一个套接字地址结构相连,比如说一个特定的本地端口号和一个ip地址
liosten()永在服务器,导致一个绑定的TCP套接字进入监听状态
accept() 永在服务器,从客户端那接受请求试图创造一个新的tcp连接
demo
-(void)BSDSocket{
//创建
//参数一:ip协议类型AF_INET指IPV4,参数二:socket的类型,一半字节流stream(SOCK_STREAM)h或数据报文(SOCK_DGRAM,参数三:协议 一般填0 系统自动选取最佳协议)
//tcp
int clientSocket = socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in stSockAddr;
int Res;
if (clientSocket>0) {
NSLog(@"socke创建成功 %d",clientSocket);
}else{
NSLog(@"创建失败");
}
memset(&stSockAddr, 0, sizeof(struct sockaddr_in));
stSockAddr.sin_family = AF_INET;
stSockAddr.sin_port = htons(1100);
Res = inet_pton(AF_INET, "192.168.1.7", &stSockAddr.sin_addr);
if (0>Res) {
NSLog(@"连接失败");
//关闭
close(clientSocket);
exit(EXIT_FAILURE);
}else if (0==Res){
close(clientSocket);
exit(EXIT_FAILURE);
}
if (-1 == connect(clientSocket, (const struct sockaddr *)&stSockAddr, sizeof(struct sockaddr_in))) {
close(clientSocket);
exit(EXIT_FAILURE);
}
//udp
int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
struct sockaddr_in sa;
char buffer[1024];
ssize_t recsize;
socklen_t fromlen;
memset(&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = INADDR_ANY;
sa.sin_port = htons(7654);
if (-1 == bind(sock,(struct sockaddr *)&sa, sizeof(struct sockaddr)))
{
perror("error bind failed");
close(sock);
exit(EXIT_FAILURE);
}
for (;;)
{
printf ("recv test....\n");
recsize = recvfrom(sock, (void *)buffer, 1024, 0, (struct sockaddr *)&sa, &fromlen);
if (recsize < 0)
fprintf(stderr, "%s\n", strerror(errno));
printf("recsize: %zd\n ",recsize);
sleep(1);
printf("datagram: %s\n",buffer);
}
}
5.socket相关第三方库
cocoAsyncSocket(两个类GCDAsyncSocekt/GCDAsyncUdpSocket)
WebSocket(Socket.IO,SocketRocket)三端可以双全工socket.主要用于web端。也可以用于ios端。