iOS —— Socket

Socket其實就是在傳輸層對TCP/UDP封裝的一層API,俗稱套接字。 就是爲網絡服務提供的一種機制,通信的兩端都是Socket(用戶端和服務端)兩個Socket IO傳輸,Socket是純C語言的,是跨平臺的。下圖:

UDP :(用戶數據報文協議) 只管發送,不確認對方是否接收,將數據及源和目的的封裝成數據包中,不需要建立連接。每個數據報的大小限制在64K之內。因爲無需連接,因此是不可靠協議,不需要建立連接,特點就是:速度快。

TCP(Transmission Control Protocol,傳輸控制協議)是一種面向連接的、可靠的、基於字節流的通信協議,數據在傳輸前要建立連接,傳輸完畢後還要斷開連接,客戶端在收發數據前要使用 connect() 函數和服務器建立連接。建立連接的目的是保證IP地址、端口、物理鏈路等正確無誤,爲數據的傳輸開闢通道。
TCP建立連接時要傳輸三個數據包,俗稱三次握手(Three-way Handshaking),上一篇文章中有講到。

關於客戶端和服務端,服務端只多了bind、listen、accept 這三個函數。

客戶端代碼:

創建:

    /**
     1: 創建socket
     參數
     domain:協議域,又稱協議族(family)。常用的協議族有AF_INET、AF_INET6、AF_LOCAL(或稱AF_UNIX,Unix域Socket)、AF_ROUTE等。協議族決定了socket的地址類型,在通信中必須採用對應的地址,如AF_INET決定了要用ipv4地址(32位的)與端口號(16位的)的組合、AF_UNIX決定了要用一個絕對路徑名作爲地址。
     type:指定Socket類型。常用的socket類型有SOCK_STREAM、SOCK_DGRAM、SOCK_RAW、SOCK_PACKET、SOCK_SEQPACKET等。流式Socket(SOCK_STREAM)是一種面向連接的Socket,針對於面向連接的TCP服務應用。數據報式Socket(SOCK_DGRAM)是一種無連接的Socket,對應於無連接的UDP服務應用。
     protocol:指定協議。常用協議有IPPROTO_TCP、IPPROTO_UDP、IPPROTO_STCP、IPPROTO_TIPC等,分別對應TCP傳輸協議、UDP傳輸協議、STCP傳輸協議、TIPC傳輸協議。
     注意:1.type和protocol不可以隨意組合,如SOCK_STREAM不可以跟IPPROTO_UDP組合。當第三個參數爲0時,會自動選擇第二個參數類型對應的默認協議。
     返回值:
     如果調用成功就返回新創建的套接字的描述符,如果失敗就返回INVALID_SOCKET(Linux下失敗返回-1)
     */
    
 
    int socketId = socket(AF_INET, SOCK_STREAM, 0);
    self.clinenId = socketId;
    if (socketId == -1) {
        NSLog(@"創建失敗....");
        return;
    }
    
    /**
     __uint8_t    sin_len;          假如沒有這個成員,其所佔的一個字節被併入到sin_family成員中
     sa_family_t    sin_family;     一般來說AF_INET(地址族)PF_INET(協議族)
     in_port_t    sin_port;         // 端口
     struct    in_addr sin_addr;    // ip
     char        sin_zero[8];       沒有實際意義,只是爲了 跟SOCKADDR結構在內存中對齊
     */
    
    struct sockaddr_in socketAdd;
    socketAdd.sin_family = AF_INET;
    socketAdd.sin_port   = SocketPort;
    struct in_addr socket_idAdd;
    socket_idAdd.s_addr  = SocketIP;
    socketAdd.sin_addr   = socket_idAdd;
    
    //
    /**
     參數
     參數一:套接字描述符
     參數二:指向數據結構sockaddr的指針,其中包括目的端口和IP地址
     參數三:參數二sockaddr的長度,可以通過sizeof(struct sockaddr)獲得
     返回值
     成功則返回0,失敗返回非0,錯誤碼GetLastError()。
     */
    
    // 連接通道
    int result = connect(socketId,(const struct sockaddr *)&socketAdd, sizeof(socketAdd));
    if (result != 0) {
        NSLog(@"連接失敗....");
        return;
    }
    NSLog(@"連接成功");
    // 接受消息
    self.index = 0;
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        
        // 這裏寫接收消息的代碼, 如下:
    });

接收消息 :

 // 4. 接收數據
    /**
     參數
     1> 客戶端socket
     2> 接收內容緩衝區地址
     3> 接收內容緩存區長度
     4> 接收方式,0表示阻塞,必須等待服務器返回數據
     
     返回值
     如果成功,則返回讀入的字節數,失敗則返回SOCKET_ERROR
     */
 
    while (1) {
        
        uint8_t buffer[1024];
        ssize_t recLen = recv(self.clinenId, buffer, sizeof(buffer), 0);
        NSLog(@"接受到了%ld 字節",recLen);
        
        if (recLen == 0) {
            self.index ++;
            if (self.index > 5) {
                return ;
            }
        }
        
        NSData *data = [NSData dataWithBytes:buffer length:recLen];
        NSString *str= [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
        NSLog(@"%@",str);
        self.index = 0;
        dispatch_async(dispatch_get_main_queue(), ^{
            
            // 在主線程更新UI, 消息:str
        });
        
    }

發送:

 /**
     3: 發送消息
     s:一個用於標識已連接套接口的描述字。
     buf:包含待發送數據的緩衝區。
     len:緩衝區中數據的長度。
     flags:調用執行方式。
     
     返回值
     如果成功,則返回發送的字節數,失敗則返回SOCKET_ERROR
     一箇中文對應 3 個字節!UTF8 編碼!
     */
    if (self.sendMsgContent_tf.text.length==0) {
        NSLog(@"消息爲空,無法發送");
        return;
    }
    
    const char *msg = self.sendMsgContent_tf.text.UTF8String;
    // 監控發送數據長度
    ssize_t sendLen = send(self.clinenId, msg, strlen(msg), 0);

服務端代碼:

#pragma mark - 創建socket建立連接
#define SocketPort htons(8041)
#define SocketIP   inet_addr("192.168.0.102")

static int const kMaxConnectCount = 5;
- (void)socketConnetAction {
    
    // 1: 創建socket
    self.serverId = socket(AF_INET, SOCK_STREAM, 0);
    
    if (self.serverId == -1) {
        NSLog(@"創建socket 失敗");
        return;
    }
    NSLog(@"創建socket 成功");

    struct sockaddr_in socketAddr;
    socketAddr.sin_family   = AF_INET;
    socketAddr.sin_port     = SocketPort;
    struct in_addr  socketIn_addr;
    socketIn_addr.s_addr    = SocketIP;
    socketAddr.sin_addr     = socketIn_addr;
    bzero(&(socketAddr.sin_zero), 8);
    
    // 2: 綁定socket
    int bind_result = bind(self.serverId, (const struct sockaddr *)&socketAddr, sizeof(socketAddr));
    if (bind_result == -1) {
        NSLog(@"綁定socket 失敗");
        return;
    }

    NSLog(@"綁定socket成功");
    
    // 3: 監聽socket
    int listen_result = listen(self.serverId, kMaxConnectCount);
    if (listen_result == -1) {
        NSLog(@"監聽失敗");
        return;
    }
    NSLog(@"監聽成功");
    // 4: 接受客戶端的鏈接
    for (int i = 0; i < kMaxConnectCount; i++) {
        [self acceptClientConnet];
    }
    
     // 發送消息同客服端一樣
}

#pragma mark - 接受客戶端的鏈接

- (void)acceptClientConnet{
    
    // 阻塞線程
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        struct sockaddr_in client_address;
        socklen_t address_len;
        // accept函數
        int client_socket = accept(self.serverId, (struct sockaddr *)&client_address, &address_len);
        self.client_socket = client_socket;
        
        if (client_socket == -1) {
            NSLog(@"接受 %u 客戶端錯誤",address_len);
            
        }else{
            NSString *acceptInfo = [NSString stringWithFormat:@"客戶端 in,socket:%d",client_socket];
            NSLog(@" 接收到的消息%@",acceptInfo);
        }

    });
    
}

 

 

 


 

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