TCP概述
TCP是传输层的协议,它是一个面向连接,安全的,流传输协议。因为数据传输基于流的所以发送端和接收端每次处理的数量,处理数据的频率是不对等的,传递的数据是没有消息边界的。
问题分析
客户端和服务器之间要进行基于TCP的套接字通信
- 通信过程中客户端每次会不定期给服务器发送一个不定长的特定含义的字符串
- 通信的服务器每次都需要接受到客户用户端这个不定长的字符串,并对其进行解析
根据上面的描述,我们可能遇到如下情况
1.一次接收到客户端发送过来的一个完整的数据包
2.一次接收到客户端发送的N个数据包,因为每个包长度不定,无法将每个数据包拆开
3.一次接受到一个或者多个数据包+下一个数据包的一部分,无法将数据包拆开
4.一次收到半个数据包,下一次接收数据的时候收到剩下的一部分+下一个数据包的一部分,当然这种情况更难拆包
5.一些其他因素,导致客户端和服务器的发送和接受速度不一样。
上述问题,就是TCP粘包问题
解决方案
解决方案
1.使用标准应用层协议(比如:http)来封装传输的不定长数据包
2.在每条数据的尾部添加特殊字符,(充当边界),如果遇到特殊字符,代表当条数据接受完毕。
(效率低,需要一个一个字节的接受,接受一个字符判断一次)
3.在发送数据之前,在数据块最前便添加一固定大小的数据头,用来存放此条数据的大小。
具体实现分析
发送端(客户端)
1.根据待发送的数据长度N动态开辟固定达雄安的内存:N+4(4是包头的长度)
2.将待发送数据的总长度写入申请的内存的前四个字节,此处需要将其转化成为网络字节序(大端)
3.将待发送的数据拷贝到包头后边的地址空间中,再将完整的数据包发送出去(字符串没有字节序问题)
4.申请释放空间
int writen(int fd, char*msg,int size)
{
char*buf=msg;
int count =size;
while(count>0)
{
int len=send(fd,buf,count,0);
if(len==-1)
{
return -1;
}
else if(len==0)
{
continue;
}
buf+=len;
count-=len;
}
return size;
}
int sendMsg(int fd,const char*msg,int len)
{
if(fd<0||msg==NULL||len<=0)
{
return -1;
}
char*data=(char*)malloc(sizeof(char)*(len+4));
int biglen=htonl(len);
memcpy(data,&biglen,4);
memcpy(data+4,msg,len);
int ret;
ret=writen(fd,data,len+4);
if(ret==-1)
{
perror("send error");
free(data);
close(fd);
}
free(data);
return ret;
}
客户端
#include"socket.h"
int main()
{
char temp[1001];
int fd=socket(AF_INET,SOCK_STREAM,0);
struct sockaddr_in caddr;
caddr.sin_family=AF_INET;
caddr.sin_port=htons(9526);
inet_pton(AF_INET,"127.0.0.1",&caddr.sin_addr.s_addr);
connect(fd,(struct sockaddr*)&caddr,sizeof(caddr));
printf("客户端连接成功\n");
if(fd==-1)
return -1;
int length=0;
int fd1=open("english.txt",O_RDONLY);
while((length=read(fd1,temp,rand()%1000))>0)
{
sendMsg(fd,temp,length);
memset(temp,0,sizeof(temp));
usleep(300);
}
sleep(10);
}
接收端(服务器)
int readn(int fd,char*buf,int size)
{
char*pt=buf;
int count =size;
while(count>0)
{
int len=recv(fd,pt,count,0);
if(len==-1)
{
return -1;
}
else if(len==0)
{
return size-count;
}
pt+=len;
count-=len;
}
return size-count;
}
int recvMsg(int fd,char**msg)
{
int len=0;
readn(fd,(char*)&len,4);
len=ntohl(len);
printf("接收到的 数据块大小 %d\n",len);
char * data=(char*)malloc(len+1);
int Len=readn(fd,data,len);
if(Len==0)
{
printf("对方断开链接\n");
close(fd);
}
else if(len!=Len)
{
printf("数据接收失败\n");
}
data[len]='\0';
*msg=data;
return Len;
}
服务器
#include"socket.h"
int createsocket()
{
int lfd=socket(AF_INET,SOCK_STREAM,0);
if(lfd==-1)
{
perror("socket error");
return -1;
}
else
{
printf("套接字创建成功\n");
return lfd;
}
}
int setListen(int lfd,unsigned short port)
{
struct sockaddr_in saddr;
saddr.sin_family=AF_INET;
saddr.sin_port=htons(port);
saddr.sin_addr.s_addr=INADDR_ANY;
int ret=0;
ret=bind(lfd,(struct sockaddr*)&saddr,sizeof(saddr));
if(ret==-1)
{
perror("bind error");
return -1;
}
else
{
printf("套接字地址绑定成功\n");
}
listen(lfd,128);
return 1;
}
int acceptConn(int lfd,struct sockaddr_in*addr)
{
int cfd=-1;
if(addr==NULL)
{
cfd=accept(lfd,NULL,NULL);
return cfd;
}
socklen_t len=sizeof(addr);
cfd=accept(lfd,(struct sockaddr*)&addr,&len);
return cfd;
}
int connectToHost(int lfd,char*ip,unsigned short port)
{
struct sockaddr_in caddr;
caddr.sin_family=AF_INET;
caddr.sin_port=htons(port);
inet_pton(AF_INET,ip,&caddr.sin_addr.s_addr);
int fd= connect(lfd,(struct sockaddr*)&caddr,sizeof(caddr));
printf("成功与客户端建立链接\n");
return fd;
}
int writen(int fd, char*msg,int size)
{
char*buf=msg;
int count =size;
while(count>0)
{
int len=send(fd,buf,count,0);
if(len==-1)
{
return -1;
}
else if(len==0)
{
continue;
}
buf+=len;
count-=len;
}
return size;
}
int sendMsg(int fd,const char*msg,int len)
{
if(fd<0||msg==NULL||len<=0)
{
return -1;
}
char*data=(char*)malloc(sizeof(char)*(len+4));
int biglen=htonl(len);
memcpy(data,&biglen,4);
memcpy(data+4,msg,len);
int ret;
ret=writen(fd,data,len+4);
if(ret==-1)
{
perror("send error");
close(fd);
}
return ret;
}
int readn(int fd,char*buf,int size)
{
char*pt=buf;
int count =size;
while(count>0)
{
int len=recv(fd,pt,count,0);
if(len==-1)
{
return -1;
}
else if(len==0)
{
return size-count;
}
pt+=len;
count-=len;
}
return size-count;
}
int recvMsg(int fd,char**msg)
{
int len=0;
readn(fd,(char*)&len,4);
len=ntohl(len);
printf("接收到的 数据块大小 %d\n",len);
char * data=(char*)malloc(len+1);
int Len=readn(fd,data,len);
if(Len==0)
{
printf("对方断开链接\n");
close(fd);
}
else if(len!=Len)
{
printf("数据接收失败\n");
}
data[len]='\0';
*msg=data;
return Len;
}