socket分包小實驗

當採用最基本socket來傳輸數據時,如果接受方的緩存去足夠大,一次就能保存全部數據;但是當傳輸的數據比較大(像視頻,大文件等),這個時候數據明顯需要多次傳輸。下面是演示將數據分開多次發送的一個例子,分包的關鍵就是自己設定一套規則,將要發送的數據按照規則組織在一起。有一個要注意的地方:如果發送的數據小於接受方的緩衝區大小,分包就沒有意義了;所以接受方緩衝區的大小應該和發送包的最大size相等。
服務端:

#include <winsock2.h>
#pragma comment(lib,"ws2_32")
#include <stdio.h>
int main(int argc, char* argv[])
{
    sockaddr_in remoteAddr; 
    sockaddr_in sin;
    SOCKET sClient;
    WSADATA wsaData;
    WORD sockVersion = MAKEWORD(2, 2);

    int nAddrLen = sizeof(remoteAddr),ret=0,size=0;
    char revData[18]={0},*buff="\r\n編程,我來了\r\n",Data[0x1008]={0x04,0x01};

    //加載winsock庫
    if(WSAStartup(sockVersion, &wsaData) != 0)
        return 0;
    // 創建套節字
    SOCKET sListen = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(sListen == INVALID_SOCKET)
    {
        printf("socket error\n");
        return 0;
    }   
    // 在sockaddr_in結構中裝入地址信息
    sin.sin_family = AF_INET;
    sin.sin_port = htons(4500); // htons函數 將主機的無符號短整形數轉換成網絡
    //字節順序
    sin.sin_addr.S_un.S_addr = INADDR_ANY;
    // 使套接字和本地地址綁定
    if(bind(sListen, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)
    {
        printf(" bind error \n");
        closesocket(sListen);
        return 0;
    }
    // 設置套接字進入監聽模式
    if(listen(sListen, 5) == SOCKET_ERROR)
    {
        printf("listen error\n");
        closesocket(sListen);
        return 0;
    }
    sClient = accept(sListen, (SOCKADDR*)&remoteAddr, &nAddrLen);
    if(sClient == INVALID_SOCKET)
    {
        printf("accept() error");
        return 0;
    }
    printf(" 接受到一個連接:%s \r\n", inet_ntoa(remoteAddr.sin_addr));
    do
    {
        ret=recv(sClient,revData,18,0);//緩衝區大小爲18
        if(ret>0)
        {   
            memcpy(Data+size,revData+2,16);//接受包中前2個字節是標記,後面是有效數據
            size+=(ret-2);
        }
        if(revData[1]==0x01)//收到最後一個包,數據接受完畢
              break;
        memset(revData,0,18);
    }while(1);
    printf("recv is %s\n",Data);
    send(sClient,buff,strlen(buff),0);
    // 關閉套接字句柄,結束會話
    closesocket(sClient);
    closesocket(sListen);
    WSACleanup();
    return 0;
}

客戶端:

#include<stdio.h>
#include<windows.h>
#pragma comment(lib,"ws2_32")
int main()
{
    WSADATA wsaData;
    WORD sockVersion = MAKEWORD(2, 2);
    int DataSize=0,num=0,ret=0;
    char revData[255],
        buff[0x100]="abcdxxxx xxxefghijklmn2288ostua0vwaxyzxxx xxxx11111111\n",
        sendData[0x1008]={0x04,0x01};

    //加載winsock庫
    if(WSAStartup(sockVersion, &wsaData) != 0)
        return 0;
    SOCKET sClient = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if(sClient == INVALID_SOCKET)
    {
        printf("socket error\n");
        return 0;
    }   
    sockaddr_in servAddr;
    servAddr.sin_family = AF_INET;
    servAddr.sin_port = htons(4500);// htons函數 將主機的無符號短整形數轉換成網絡
                                    //字節順序,4500端口爲要連接服務器端的端口
    servAddr.sin_addr.S_un.S_addr =inet_addr("127.0.0.1");//服務器端ip
    if(connect(sClient,(sockaddr*)&servAddr,sizeof(servAddr))==SOCKET_ERROR)
    {
        printf("connect error\n");
        closesocket(sClient);
        return 0;
    }
    DataSize=strlen(buff);
    do
    {
        if(DataSize>16)//還有數據
        {
            memcpy(sendData+2,buff+16*(num++),16);
            sendData[1]=0x0;//設置第2個字節爲0x0,表示中間包
            send(sClient,sendData,18,0);
        }
        else //最後一個包 
        {
            memcpy(sendData+2,buff+16*(num),DataSize);
            sendData[1]=0x01;//設置第2個字節爲0x1,表示最後一個包
            send(sClient,sendData,DataSize+2,0);
        }
        DataSize-=16; //two bytes per time 
    }while(DataSize>0);
    //直到收到有效數據時纔打印出來
    ret=recv(sClient,revData,255,0);
    if(ret>0)
    {
        //爲了防止打印出錯,把字符串結尾設成0x00
        revData[ret]=0x00;
        printf(revData);
    }
    closesocket(sClient);
    WSACleanup();
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章