Socket

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.

  1. 三种类型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端。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章