TCP套接字編程實現


一、基於TCP的套接字編程實現流程:
1.  服務器端流程簡介:
            (1)創建套接字(socket)
            (2)將套接字綁定到一個本地地址和端口上(bind)
            (3)將套接字設定為監聽模式,准備接受客戶端請求(listen)
            (4)阻塞等待客戶端請求到來。當請求到來后,接受連接請求,返回一個新的對應於此客戶端連接的套接字sockClient(accept)
            (5)用返回的套接字sockClient和客戶端進行通信(send/recv);
            (6)返回,等待另一個客戶端請求(accept)
            (7)關閉套接字(close)
2.  客戶端流程簡介:
            (1)  創建套接字(socket)
            (2)  向服務器發出連接請求(connect)
            (3)  和服務器進行通信(send/recv)
            (4)  關閉套接字(close)
                                                             
二、 send和recv函數的理解:
  當調用socket創建套接字時,同時在內核中生成發送和接收緩沖區。
  • 設置為connect模式時(客戶端模式),調用send會將用戶自定義的buff中的數據拷貝到發送緩沖區,緩沖區數據的發送由TCP/IP模型完成;

  • 設置為listen模式時(服務器端模式),發送緩沖區不再使用,接收緩沖區只存放客戶端的連接請求。而accpet函數返回的新建套接字sockfd會再生成兩個新緩沖區,發送和接收緩沖區。當調用recv時,recv先等待sockfd的發送緩沖區中數據按協議傳送完畢,再檢查sockfd的接收緩沖區,如果接收緩沖區沒有數據或正在傳送,則recv等待;否則recv將接收緩沖區中的數據拷貝到用戶定義的buff中(ps:當接收緩沖區中數據長度大於buff長度時,recv要調用多次才能完全拷貝完成)。recv返回的是每次實際拷貝的數據長度,若拷貝出錯則返回SOCKET_ERROR,若網絡中斷則返回0。

  • send和recv只是從發送/接收緩沖區中拷貝數據,真正的讀寫數據是由TCP/IP協議完成的

C++客戶端/服務器端的簡單實現:
(1)Sever實現:
#include <Winsock2.h>
#include <cstdio>
#include <iostream>
#pragma comment(lib,"ws2_32.lib")
int main()
{
        //1. 加載socket函數庫
        WSADATA wsaData;
        SOCKET sockServer;
        SOCKADDR_IN addrServer;
        SOCKET sockClient;
        SOCKADDR_IN addrClient;
        WSAStartup(MAKEWORD(2,2),&wsaData);     //加載套接字庫

        //2. 創建套接字
        addrServer.sin_addr.S_un.S_addr = htonl(INADDR_ANY);      //INADDR_ANY表示任何IP,即允許所有的客戶端連接到本地服務端
        addrServer.sin_family = AF_INET;          //設置為IP通信
        addrServer.sin_port = htons(6000);        //綁定端口6000
        sockServer = socket(AF_INET,SOCK_STREAM,0);       //創建流式套接字
        std::cout<<"創建套接字成功"<<std::endl;

        //3. 將套接字綁定到 固定IP和固定端口
        bind(sockServer, (SOCKADDR*)&addrServer,  sizeof(SOCKADDR));    //進行端口和IP地址的綁定
        std::cout<<"綁定套接字成功"<<std::endl;

        //4. 將套接字設置為監聽模式,等待連接到來
        listen(sockServer,5);           //監聽隊列為5
        std::cout<<"監聽連接中......"<<std::endl;
        int len = sizeof(SOCKADDR);
        char sendBuf[100];      //發送至客戶端的字符串
        char recvBuf[100];       //接受客戶端返回的字符串

        //5. 阻塞服務端的進程,直到有客戶端連接為止
        sockClient = accept(sockServer,  (SOCKADDR*)&addrClient,  &len);  //sockClinet為返回的新的客戶端連接
        std::cout<<"收到連接:"<<inet_ntoa(addrClient.sin_addr)<<std::endl;

        //6. 接收並打印客戶端數據
        int recvlen = 0;
        if((recvlen = recv(sockClient,recvBuf,100,0)) > 0)
        {
        std::cout<<recvBuf<<std::endl;
        }
        send(sockClient, "Receive the Client Data", 23, 0);

        //7. 關閉socket
        closesocket(sockClient);
        WSACleanup();
        return 0;
}

(2)Client實現:

#include <WinSock2.h>  
#include <stdio.h>
#include <iostream>  
#include <cstring>
#pragma comment(lib, “ws2_32.lib”)  
  
int main()  
{  
    //1. 加載套接字  
    WSADATA wsaData;   
    if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)  
    {  
        printf(”Failed to load Winsock”);  
        return;  
    }  
  
    //2. 創建套接字
    SOCKADDR_IN addrSrv;  
    addrSrv.sin_family = AF_INET;  
    addrSrv.sin_port = htons(6000);  //端口號6000
    addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //訪問的服務器IP  
    SOCKET sockClient = socket(AF_INET, SOCK_STREAM, 0);  //創建客戶端套接字
  
    //3. 向服務器發出連接請求  
   if(connect(sockClient, (struct  sockaddr*)&addrSrv, sizeof(addrSrv)) == INVALID_SOCKET)
   {  
        std::cout<<"Connect failed:"<<WSAGetLastError()<<std::endl;  
        return;  
    }

    //4.接收和發送數據  
    char recvbuff[1024];  
    memset(recvbuff, 0, sizeof(recvbuff));
    recv(sockClient, recvbuff, sizeof(recvbuff), 0);  
    std::string sendbuf = "hello, this is a Client…";  
    send(sockClient, sendbuf.c_str(), sendbuf.size(), 0);  
  
    //5. 關閉套接字  
    closesocket(sockClient);  
    WSACleanup();  
    return 0;
}  


免責聲明!

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



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