當採用最基本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;
}