淺談TCP socket



Socket,用來實現應用的通信,是應用非常廣的一個api,今天就來揭開它的神秘面紗。

總體流程圖

客戶端

  1. 引入頭文件

    #include <winsock2.h>
    
  2. 初始化socket的DLL

    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData
    
  3. 創建套接字

        SOCKET socket(int domain, int type, int protocol);
    
  • domain 是協議域,包括

    • AF_INET 對應 ipv4
    • AF_INET6 對應 ipv6
  • type 是連接類型

    - SOCK_STREAM,提供面向連接的穩定數據傳輸,即TCP協議。
    - SOCK_DGRAM,提供的是數據報(datagram),使用UDP協議。
    - protocol,一般設為0,內核會自動匹配。
    
  1. 存儲服務器信息

    客戶端要去連接服務器,所以應該存儲服務器的IP地址和端口號。

    這邊已經在 netinet/in.h 幫我們實現了一個結構體 struct sockaddr_in 來存儲服務器信息。作為函數參數時強制轉換為sockaddr。

    struct sockaddr_in {
        short   sin_family;   // 必須為AF_INET,因為是IPv4;
        unsigned short   sin_port;    // 存儲port No
        struct in_addr   sin_addr;    //存儲IP地址
        char             sin_zero[8];  
    };
    

    網絡的字節順序為大端法,我們要想正確通信,就必須統一格式。

    那么什么是大小端呢?

    大端是高位字節存在低地址中,小端是低位字節存在低地址中。

    因此如果本地端口格式是小端要轉為大端。

    htons(PORT)就是將本機的字節序轉化為網絡的字節序。

  2. 連接服務器

    int connect(SOCKET sockfd, const struct sockaddr *serv_addr,socklen_t addrlen)
    
  • SOCKET sockfd,本地的socket
  • const struct sockaddr *serv_addr, 將SOCKADDR_IN的對象轉為 sockaddr 指針
  • socklen_t address_len 就是sockaddr的大小
  1. 收發消息

    ssize_t send(SOCKET sockfd, const void *buf, size_t len, int flags)
        
    ssize_t recv(SOCKET sockfd, void *buf, size_t len, int flags)
    
    
    • SOCKET sockfd,本地的socket描述字
    • const void *buf,字符串指針,數據緩沖區
    • size_t len,接受的長度
    • int flags,通常從設為0
  2. 斷開

    int PASCAL FAR closesocket(SOCKET s);
    

    關閉套接字

服務器

  1. 創建服務器套接字

  2. 存儲服務器信息

  3. bind綁定

    將服務器套接字與服務器信息綁定在一起

    int bind(SOCKET socket, const struct sockaddr* address, socklen_t address_len);
    
    • SOCKET socket,服務器本地的socket
    • const struct sockaddr *address, 將SOCKADDR_IN的對象轉為 sockaddr 指針
    • socklen_t address_len 就是sockaddr的大小
  4. 監聽

    int listen(SOCKET sockfd, int backlog)
    

    listen()函數可以讓套接字進入被動監聽狀態

    sockfd為需要進入監聽狀態的套接字,backlog 為請求隊列的最大長度。

    所謂被動監聽,是指當沒有客戶端請求時,套接字就會處於睡眠狀態,只有當接收到客戶端請求時,套接字才會被喚醒來響應請求。

  5. accept

    用accept來接受客戶端的請求,這時候會產生一個新的套接字,記重點,新的,之后的通信全部用這個新的套接字,原來的套接字還在監聽客戶端的請求。

    int accept(SOCKET sockfd, struct sockaddr *addr, socklen_t *addrlen)
    
    
    • SOCKET sockfd,服務器本地的socket
    • struct sockaddr *addr,用於存儲客戶端的IP和端口號等
    • socklen_t address_len,描述addr的大小

    注意這邊的accept是會阻塞的,那么什么是阻塞,就是如果沒有接收到,那么我就會一直等在這邊,比如我們剛學c語言的scanf函數的時候,它就會阻塞到那里等待用戶輸入。

    包括send和recv也是會阻塞的。

  6. 新套接字用來收發信息

  7. 關閉套接字

參考資料:

1.簡單的聊天室實現(上):通信-SOCKET

2. TCP Socket Programming 學習筆記

3. 使用listen()和accept()函數


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM