1. 程式人生 > >socket套接字編程(1)——基本函數

socket套接字編程(1)——基本函數

變量 建立 strong 請求 進入 一個 就會 con 編程

TCP交互流程:

服務器:1. 創建socket;2. 綁定socket和端口號;3. 監聽端口號;4. 接收來自客戶端的連接請求;5. 從socket中讀取字符;6. 關閉socket。

客戶端:1. 創建socket;2. 連接制定計算機的端口;3. 向socket中寫入信息;4. 關閉socket。

創建socket:

socket函數

int socket (int __family, int __type, int __protocol);

__family是協議域,也稱協議族。常見的有AF_INET(ipv4)。

__type指定socket類型。SOCK_STREAM即TCP協議,SOCK_DGRAM即UDP協議。

__protocol指定協議。

該函數返回的socket描述字存在於協議族空間中,但是並沒有一個具體的地址。如果想要給它賦予一個地址,就必須調用bind()函數,否則系統就在調用connect()和listen()時自動隨機分配一個端口。

這裏註意:type和protocol並不能隨意組合。當protocol為0時,會自動選擇type類型對應的默認協議。

創建socket的樣例代碼如下:

    //創建TCP套接字
    //AF_INET:網絡連接,ipv4
    //SOCK_STREAM:TCP連接
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    
if (fd<0) { std::cout<<"create socket error!"<<std::endl; return 0; } std::cout<<"create socket: "<<fd<<std::endl;

綁定socket和端口號:

bind函數

int bind (int, const struct sockaddr *__my_addr, socklen_t __addrlen);

第一個參數是socket描述字。(我不理解為啥這兒沒有參數名)

__my_addr是指向要綁定給該socket的協議地址。這個地址結構根據socket創建時的地址協議族(family)的不同而不同。

__addrlen對應的是地址的長度。

如果該函數執行成功,就返回0,否則為SOCKET_ERROR。

    //命名套接字
    struct sockaddr_in myaddr;
    memset((void *)&myaddr, 0, sizeof(myaddr));
    //關於htonl和htons,參考以下網頁:ntohs, ntohl, htons,htonl的比較和詳解
    //https://blog.csdn.net/haoxiaodao/article/details/73162663
    myaddr.sin_family = AF_INET;
    myaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    myaddr.sin_port = htons(6666);
    if (bind(fd, (struct sockaddr*)&myaddr, sizeof(myaddr)) < 0) {
        std::cout<<"name socket error!"<<std::endl;
        return 0;
    }
    std::cout<<"name socket"<<std::endl;

監聽端口號:

作為一個服務器,在調用socket()和bind()之後就會調用listen()來監聽這個socket。為了能夠在套接字上接受進入的連接,服務器程序必須創建一個隊列來保存未處理的請求。

listen函數

int listen (int, int __n);

第一個參數即socket描述子

__n為隊列的大小。

    //創建監聽隊列
    if (listen(fd, 5) < 0) {
        std::cout<<"listen failed"<<std::endl;
        return 0;
    }

接收來自客戶端的連接請求:

當TCP服務器監聽到了連接請求之後,就會調用accept()函數接收請求,這樣連接就建立好了。

accept函數

int accept (int, struct sockaddr *__peer, socklen_t *);

第一個是socket描述子,第二個是用來接收的客戶端地址,第三個是地址的大小。註意第三個是指針類型,所以要事先構造好大小的變量,然後傳地址進去。

另外,《後臺開發核心技術與應用實踐》中的例子,第二個和第三個都傳的NULL。我的理解是,如果不需要接收這兩個量,就可以傳一個空值進去。

accept函數會返回一個新的socket描述子,這個新的描述子代表了服務端和客戶端的連接。後面可以用於讀取數據以及關閉連接。

   //等待並接受連接
    const int MAXBUF = 4096;
    char buff[MAXBUF];
    struct sockaddr_in client_addr;
    int client_addr_len = sizeof(client_addr);
    int client_fd;
    while (1) {
        client_fd = accept(fd, (struct sockaddr*)&client_addr, &client_addr_len);
        if (client_fd < 0) {
            std::cout<<"connect error"<<std::endl;
            continue;
        }
        //接收數據
        //關閉套接字
    }

未完,待補全。

socket套接字編程(1)——基本函數