tcp粘包问题(封包)
tcp粘包分析 http://blog.csdn.net/zhangxinrun/article/details/6721495
解决TCP网络传输“粘包”问题(经典) http://blog.csdn.net/zhangxinrun/article/details/6721508
粘包出现原因:在流传输中出现,UDP不会出现粘包,因为它有消息边界(参考Windows 网络编程)
1 发送端需要等缓冲区满才发送出去,造成粘包
2 接收方不及时接收缓冲区的包,造成多个包接收
解决办法:
为了避免粘包现象,可采取以下几种措施。一是对于发送方引起的粘包现象,用户可通过编程设置来避免,TCP提供了强制数据立即传送的操作指令push,TCP软件收到该操作指令后,就立即将本段数据发送出去,而不必等待发送缓冲区满;二是对于接收方引起的粘包,则可通过优化程序设计、精简接收进程工作量、提高接收进程优先级等措施,使其及时接收数据,从而尽量避免出现粘包现象;三是由接收方控制,将一包数据按结构字段,人为控制分多次接收,然后合并,通过这种手段来避免粘包。
以上提到的三种措施,都有其不足之处。第一种编程设置方法虽然可以避免发送方引起的粘包,但它关闭了优化算法,降低了网络发送效率,影响应用程序的性能,一般不建议使用。第二种方法只能减少出现粘包的可能性,但并不能完全避免粘包,当发送频率较高时,或由于网络突发可能使某个时间段数据包到达接收方较快,接收方还是有可能来不及接收,从而导致粘包。第三种方法虽然避免了粘包,但应用程序的效率较低,对实时应用的场合不适合。
载自:http://blog.csdn.net/binghuazh/archive/2009/05/28/4222516.aspx
封包:
封包就是给一段数据加上包头,这样一来数据包就分为包头和包体两部分内容了(以后讲过滤非法包时封包会加入"包尾"内容).包头其实上是个大小固定的结构体,其中有个结构体成员变量表示包体的长度,这是个很重要的变量,其他的结构体成员可根据需要自己定义.根据包头长度固定以及包头中含有包体长度的变量就能正确的拆分出一个完整的数据包.
服务端程序:
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<sys/wait.h> //*进程用的头文件*/
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#define MAXLINE 1024 //通信内容的最大长度 struct message
{
int len;
char buffer[];
} ; ssize_t readn(int fd, void *buf, size_t count)
{
ssize_t nleft=count;
ssize_t nread;
char *charbuf=(char*) buf; while(nleft>)
{
nread=read(fd,charbuf,nleft);
if(nread<)
{
if(errno==EINTR)
continue;
return -;
}
else if(nread==)
return count-nleft; charbuf +=nread;
nleft=count-nread;
} return count;
} ssize_t writen(int fd, const void *buf, size_t count)
{
ssize_t nleft=count;
ssize_t nwrite;
char *charbuf=(char*) buf; while(nleft>)
{
nwrite=write(fd,charbuf,nleft);
if(nwrite<)
{
if(errno==EINTR)
continue;
return -;
}
else if(nwrite==)
return count-nleft; charbuf +=nwrite;
nleft=count-nwrite;
} return count;
} int main()
{
int sock_fd,new_fd;//sock_fd用于监听,new_fd用于连接
struct sockaddr_in srv_addr;//服务器的地址信息
struct sockaddr_in client_addr;//客户机的地址信息
int size; //地址结构数据的长度
pid_t pid; //子进程id
ssize_t n,ret;
char buf[MAXLINE]; //用于存放通信的内容 struct message sendbuf,recvbuf;
memset(&sendbuf,,sizeof(sendbuf));
memset(&recvbuf,,sizeof(recvbuf)); /*创建套接字*/
sock_fd=socket(AF_INET,SOCK_STREAM,);//采用IPv4协议
if(sock_fd==-)
{
perror("creat socket failed");
exit();
} /*服务器地址参数*/
srv_addr.sin_family=AF_INET;
srv_addr.sin_port=htons();
srv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
bzero(&srv_addr.sin_zero,sizeof(struct sockaddr_in));//bzero位清零函数,将sin_zero清零,sin_zero为填充字段,必须全部为零 int on=; //表示开启reuseaddr
if(setsockopt(sock_fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on))<) //打开地址、端口重用
perror("setsockopt"); /*绑定地址和端口*/
if(bind(sock_fd,(struct sockaddr*)&srv_addr,sizeof(struct sockaddr))==-)
{
perror("bind failed");
exit();
} /*设置监听模式,等待客户机的监听*/
if((listen(sock_fd,))==-)
{
perror("listen failed");
exit();
} /*接受连接,采用非阻塞是的模式调用accep*/
//while(1)
//{
size=sizeof(struct sockaddr_in);
new_fd=accept(sock_fd,(struct sockaddr*)&client_addr,&size);
if(new_fd==-)
{
perror("accept failed");
//continue;//restart accept when EINTR
} printf("server:got connection from IP= %s prot= %d \n",inet_ntoa(client_addr.sin_addr),ntohs(client_addr.sin_port));//连接成功,打印客户机IP地址和端口号
/*char *inet_nota(struct sockaddr_in in);
头文件:
arpa/inet.h
Winsock2.h
参数:
一个网络上的IP地址
返回值:
如果正确,返回一个字符指针,指向一块存储着点分格式IP地址的静态缓冲区(同一线程内共享此内存);错误,返回NULL。
uint31_t ntohs(uint32_t net32bitvalue);
头文件:
#include<netinet/in.h>
把net32bitvalue有网络字节序转换为主机字节序。
*/
if(send(new_fd,"Hello client,I am 192.168.229.125!\n",,)==-) //192.168.229.125为子进程IP,可更改
perror("send failed"); pid=fork(); //父进程建立套接字的连接之后,创建子进程用于通信
if(pid<)
perror("fork error\n");
if(!pid)//创建新的子进程,用于发送数据
{
while(fgets(sendbuf.buffer,sizeof(sendbuf.buffer),stdin)!=NULL)
{
n=strlen(sendbuf.buffer);
sendbuf.len=htonl(n);
writen(new_fd,&sendbuf,+n);
memset(&sendbuf,,sizeof(sendbuf)); //清空,以免和下一次混淆
// exit(EXIT_SUCCESS);
}
exit(EXIT_SUCCESS);
}
else //父进程用于接收数据
{
while()
{
memset(&recvbuf,,sizeof(recvbuf));
ret=readn(new_fd,&recvbuf.len,);
if(ret<)
perror("read from server error");
else if(ret<)
{
printf("peer closed\n");
break;
}
n=ntohl(recvbuf.len);
ret=readn(new_fd,recvbuf.buffer,n);
if(ret<)
perror("read from server error");
else if(ret<n)
{
printf("peer closed\n");
break;
}
fputs(recvbuf.buffer,stdout);
}
exit(EXIT_SUCCESS);
}
//while(waitpid(-1,NULL,WNOHANG)>0);//等待子进程结束,进行新的连接
//}
return ;
}
客户端程序
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<sys/wait.h> //*进程用的头文件*/
#include<netinet/in.h>
#include<arpa/inet.h> #define MAXBYTEMUN 1024 struct message
{
int len;
char buffer[];
} ; ssize_t readn(int fd, void *buf, size_t count)
{
ssize_t nleft=count;
ssize_t nread;
char *charbuf=(char*) buf; while(nleft>)
{
nread=read(fd,charbuf,nleft);
if(nread<)
{
if(errno==EINTR)
continue;
return -;
}
else if(nread==)
return count-nleft; charbuf +=nread;
nleft=count-nread;
} return count;
} ssize_t writen(int fd, const void *buf, size_t count)
{
ssize_t nleft=count;
ssize_t nwrite;
char *charbuf=(char*) buf; while(nleft>)
{
nwrite=write(fd,charbuf,nleft);
if(nwrite<)
{
if(errno==EINTR)
continue;
return -;
}
else if(nwrite==)
return count-nleft; charbuf +=nwrite;
nleft=count-nwrite;
} return count;
} int main(int argc,char *argv[])
{
int sock_fd,numbytes;
char buf[MAXBYTEMUN];
struct hostent *he;
struct sockaddr_in client_addr;//客户机的地址信息
ssize_t n,ret;
struct message recvbuf,sendbuf;
memset(&recvbuf,,sizeof(recvbuf));
memset(&sendbuf,,sizeof(sendbuf)); if(argc!=)
{
fprintf(stderr,"usage: client IPAddress\n"); //执行客户端程序时,输入客户端程序名称和其IP地址
exit();
} /*创建套接字*/
sock_fd=socket(AF_INET,SOCK_STREAM,);//采用IPv4协议
if(sock_fd==-)
{
perror("creat socket failed");
exit();
} /*服务器地址参数*/
client_addr.sin_family=AF_INET;
client_addr.sin_port=htons();
client_addr.sin_addr.s_addr=inet_addr(argv[]);
bzero(&client_addr.sin_zero,sizeof(struct sockaddr_in));//bzero位清零函数,将sin_zero清零,sin_zero为填充字段,必须全部为零 /*连接到服务器*/
if(connect(sock_fd,(struct sockaddr*)&client_addr,sizeof(struct sockaddr))==-)
{
perror("connect failed");
exit();
}
if((numbytes=recv(sock_fd,buf,MAXBYTEMUN,))==-)
{
perror("receive failed");
exit();
}
buf[numbytes]='\0';//在字符串末尾加上\0,否则字符串无法输出
printf("Received: %s\n",buf); pid_t pid;
pid=fork();
if(!pid)//创建新的子进程,用于接收数据
{
while()
{
memset(&recvbuf,,sizeof(recvbuf));
ret=readn(sock_fd,&recvbuf.len,);
if(ret<)
perror("read from server error");
else if(ret<)
{
printf("peer closed\n");
break;
}
n=ntohl(recvbuf.len);
ret=readn(sock_fd,recvbuf.buffer,n);
if(ret<)
perror("read from server error");
else if(ret<n)
{
printf("peer closed\n");
break;
} fputs(recvbuf.buffer,stdout);
}
close(sock_fd);
}
else //f=父进程用于发送数据
{
while(fgets(sendbuf.buffer,sizeof(sendbuf.buffer),stdin)!=NULL)
{
n=strlen(sendbuf.buffer);
sendbuf.len=htonl(n);
writen(sock_fd,&sendbuf,+n); memset(&sendbuf,,sizeof(sendbuf)); //清空,以免和下一次混淆
}
close(sock_fd);
}
return ;
}
tcp粘包问题(封包)的更多相关文章
- TCP粘包和拆包问题
问题产生 一个完整的业务可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这个就是TCP的拆包和封包问题. 下面可以看一张图,是客户端向服务端发送包: 1. 第一种情况 ...
- python套接字解决tcp粘包问题
python套接字解决tcp粘包问题 目录 什么是粘包 演示粘包现象 解决粘包 实际应用 什么是粘包 首先只有tcp有粘包现象,udp没有粘包 socket收发消息的原理 发送端可以是一K一K地发送数 ...
- TCP粘包问题解析与解决
一.粘包分析 作者本人在写一个FTP项目时,在文件的上传下载模块遇到了粘包问题.在网上找了一些解决办法,感觉对我情况都不好用,因此自己想了个比较好的解决办法,提供参考 1.1 粘包现象 在客户端与服务 ...
- TCP粘包问题分析和解决(全)
TCP通信粘包问题分析和解决(全) 在socket网络程序中,TCP和UDP分别是面向连接和非面向连接的.因此TCP的socket编程,收发两端(客户端和服务器端)都要有成对的socket,因此,发送 ...
- TCP粘包, UDP丢包, nagle算法
一.TCP粘包 1. 什么时候考虑粘包 如果利用tcp每次发送数据,就与对方建立连接,然后双方发送完一段数据后,就关闭连接,这样就不会出现粘包问题(因为只有一种包结构,类似于http协议,UDP不会出 ...
- TCP粘包处理
TCP(transport control protocol,传输控制协议)是面向连接的,面向流的,提供高可靠性服务.收发两端(客户端和服务器端)都要有一一成对的socket, 因此,发送端为了将多个 ...
- tcp 粘包 和 TCP_NODELAY 学习
TCP通信粘包问题分析和解决 在socket网络程序中,TCP和UDP分别是面向连接和非面向连接的.因此TCP的socket编程,收发两端(客户端和服务器端)都要有成对的socket,因此,发送端为 ...
- 【转载】TCP粘包问题分析和解决(全)
TCP通信粘包问题分析和解决(全) 在socket网络程序中,TCP和UDP分别是面向连接和非面向连接的.因此TCP的socket编程,收发两端(客户端和服务器端)都要有成对的socket,因此,发送 ...
- 转,关于TCP粘包问题的一些思路
TCP粘包分析 一 .两个简单概念长连接与短连接:1.长连接 Client方与Server方先建立通讯连接,连接建立后不断开, 然后再进行报文发送和接收. 2.短连接 Client方与Server每进 ...
随机推荐
- zoj3882 博弈
我理解错题目意思,稀里糊涂A了.其实就是先手必胜. #include<stdio.h> int main() { int n; while(scanf("%d",&am ...
- 【CodeForces 616D】Longest k-Good Segment
题意 n个数里,找到最长的一个连续序列使里面最多k个不同的数. 分析 尺取法,每次R++,如果第R个数未出现过,那么不同的数+1,然后这个数的出现次数+1,如果不同的数大于k了,那就要去掉第L个数,直 ...
- 求第N数大问题
问题: InputThe first line of input contains a single integer P, (1 ≤ P ≤ 1000), which is the number of ...
- .net 代码风格规范
声明:内容非原创,转自张子阳博客. 对于为什么是转载,唯一原因就是这东西居然比我整理的好,直接用得了. 1. C# 代码风格要求 1.1注释 类型.属性.事件.方法.方法参数,根据需要添加注释. 如果 ...
- 数组添加:如何往数组的"null"位置插入数据呢?
数组添加,当已经存在的一个数组时,如何往数组的"null"位置插入数据呢? 分析: 1.循环遍历数组元素,找出null的位置(下标) 2.设置一个变量,接收null位置下标值 3. ...
- 从注册流程 分析如何安全退出多个Activity 多种方式(附DEMO)
退出Activity注册Android遍历 目录(?)[+] 前言 知识结构 具体方案 方案1 方法采用FLAG_ACTIVITY_CLEAR_TOP退出整个程序多activity 方案2 方 ...
- POJ1679The Unique MST(次小生成树)
The Unique MST Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 25203 Accepted: 8995 D ...
- 更改动软代码生成器模板 验证Model数据合法性
1.第一个模板 判断字段是否为空 类 IsNullableType.cmt static public partial class CommonType { public static bool Is ...
- C#里List.Sort的用法
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace List ...
- 不引用office动态库导出excel
public class OutExcelReport { /// <summary> /// 把 DataSet 的数据导成 Excel /// </summary> /// ...