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端。

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