当采用最基本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;
}