TCP和UDP的"保护消息边界" (经典)
在socket网络程序中,TCP和UDP分别是面向连接和非面向连接的。因此TCP的socket编程,收发两端(客户端和服务器端)都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化方法(Nagle算法),将多次间隔较小且数据量小的数据,合并成一个大的数据块,然后进行封包。这样,接收端,就难于分辨出来了,必须提供科学的拆包机制。
对于UDP,不会使用块的合并优化算法,这样,实际上目前认为,是由于UDP支持的是一对多的模式,所以接收端的skbuff(套接字缓冲区)采用了链式结构来记录每一个到达的UDP包,在每个UDP包中就有了消息头(消息来源地址,端口等信息),这样,对于接收端来说,就容易进行区分处理了
保护消息边界和流
那么什么是保护消息边界和流呢?
保护消息边界,就是指传输协议把数据当作一条独立的消息在网上
传输,接收端只能接收独立的消息.也就是说存在保护消息边界,接收
端一次只能接收发送端发出的一个数据包.
而面向流则是指无保护消息保护边界的,如果发送端连续发送数据,
接收端有可能在一次接收动作中,会接收两个或者更多的数据包.
我们举个例子来说,例如,我们连续发送三个数据包,大小分别是2k,
4k , 8k,这三个数据包,都已经到达了接收端的网络堆栈中,如果使
用UDP协议,不管我们使用多大的接收缓冲区去接收数据,我们必须有
三次接收动作,才能够把所有的数据包接收完.而使用TCP协议,我们
只要把接收的缓冲区大小设置在14k以上,我们就能够一次把所有的
数据包接收下来.只需要有一次接收动作.
这就是因为UDP协议的保护消息边界使得每一个消息都是独立的.而
流传输,却把数据当作一串数据流,他不认为数据是一个一个的消息.
所以有很多人在使用tcp协议通讯的时候,并不清楚tcp是基于流的
传输,当连续发送数据的时候,他们时常会认识tcp会丢包.其实不然,
因为当他们使用的缓冲区足够大时,他们有可能会一次接收到两个甚
至更多的数据包,而很多人往往会忽视这一点,只解析检查了第一个
数据包,而已经接收的其他数据包却被忽略了.所以大家如果要作这
类的网络编程的时候,必须要注意这一点.
结论:
根据以上所说,可以这样理解,TCP为了保证可靠传输,尽量减少额外
开销(每次发包都要验证),因此采用了流式传输,面向流的传输,
相对于面向消息的传输,可以减少发送包的数量。从而减少了额外开
销。但是,对于数据传输频繁的程序来讲,使用TCP可能会容易粘包。
当然,对接收端的程序来讲,如果机器负荷很重,也会在接收缓冲里
粘包。这样,就需要接收端额外拆包,增加了工作量。因此,这个特
别适合的是数据要求可靠传输,但是不需要太频繁传输的场合(
两次操作间隔100ms,具体是由TCP等待发送间隔决定的,取决于内核
中的socket的写法)
而UDP,由于面向的是消息传输,它把所有接收到的消息都挂接到缓冲
区的接受队列中,因此,它对于数据的提取分离就更加方便,但是,
它没有粘包机制,因此,当发送数据量较小的时候,就会发生数据包
有效载荷较小的情况,也会增加多次发送的系统发送开销(系统调用,
写硬件等)和接收开销。因此,应该最好设置一个比较合适的数据包
的包长,来进行UDP数据的发送。(UDP最大载荷为1472,因此最好能
每次传输接近这个数的数据量,这特别适合于视频,音频等大块数据
的发送,同时,通过减少握手来保证流媒体的实时性)
来自: http://hi.baidu.com/chongerfeia/blog/item/b1e572f631dd7e28bd310965.html
TCP无保护消息边界的解决
针对这个问题,一般有3种解决方案:
(1)发送固定长度的消息
(2)把消息的尺寸与消息一块发送
(3)使用特殊标记来区分消息间隔
下面我们主要分析下前两种方法:
1、发送固定长度的消息
这种方法的好处是他非常容易,而且只要指定好消息的长度,没有遗漏未未发的数据,我们重写了一个SendMessage方法。代码如下:
private static int SendMessage(Socket s, byte[] msg)
{
int offset = 0;
int size = msg.Length;
int dataleft = size;
while (dataleft > 0)
{
int sent = s.Send(msg, offset, SocketFlags.None);
offset += sent;
dataleft -= sent;
}
return offset;
}
简要分析一下这个函数:形参s是进行通信的套接字,msg即待发送的字节数组。该方法使用while循环检查是否还有数据未发送,尤其当发送一个很庞大的数据包,在不能一次性发完的情况下作用比较明显。特别的,用sent来记录实际发送的数据量,和recv是异曲同工的作用,最后返回发送的实际数据总数。
有sentMessage函数后,还要根据指定的消息长度来设计一个新的Recive方法。代码如下:
private byte[] ReciveMessage(Socket s, int size)
{
int offset = 0;
int recv;
int dataleft = size;
byte[] msg = new byte[size];
while (dataleft > 0)
{
//接收消息
recv = s.Receive(msg, offset, dataleft, 0);
if (recv == 0)
{
break;
}
offset += recv;
dataleft -= recv;
}
return msg;
}
以上这种做法比较适合于消息长度不是很长的情况。
2、消息长度与消息一同发送
我们可以这样做:通过使用消息的整形数值来表示消息的实际大小,所以要把整形数转换为字节类型。下面是发送变长消息的SendMessage方法。具体代码如下:
private static int SendMessage(Socket s, byte[] msg)
{
int offset = 0;
int sent;
int size = msg.Length;
int dataleft = size;
byte[] msgsize = new byte[2];
//将消息尺寸从整形转换成可以发送的字节型
msgsize = BitConverter.GetBytes(size);
//发送消息的长度信息
sent = s.Send(size);
while (dataleft > 0)
{
sent = s.Send(msg, offset, dataleft, SocketFlags.None);
//设置偏移量
offset += sent;
dataleft -= sent;
}
return offset;
}
下面是接收变长消息的ReciveVarMessage方法。代码如下:
private byte[] ReciveVarMessage(Socket s)
{
int offset = 0;
int recv;
byte[] msgsize = new byte[2];
//将字节数组的消息长度信息转换为整形
int size = BitConverter.ToInt16(msgsize);
int dataleft = size;
byte[] msg = new byte[size];
//接收2个字节大小的长度信息
recv = s.Receive(msgsize, 0, 2, 0);
while (dataleft > 0)
{
//接收数据
recv = s.Receive(msg, offset, dataleft, 0);
if (recv == 0)
{
break;
}
offset += recv;
dataleft -= recv;
}
return msg;
}
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/xinshi9608/archive/2010/12/31/6109511.aspx
TCP和UDP的"保护消息边界" (经典)的更多相关文章
- TCP和UDP的保护消息边界机制
在socket网络程序中,TCP和UDP分别是面向连接和非面向连接的.TCP的socket编程,收发两端都要有一一成对的socket,因此,发送端为了将多个发往接收端的包,更有效的发到对方,使用了优化 ...
- TCP和UDP的"保护消息边界”
转自:http://blog.csdn.net/zhangxinrun/article/details/6721427 在socket网络程序中,TCP和UDP分别是面向连接和非面向连接的.因此TCP ...
- 有关TCP和UDP 粘包 消息保护边界
http://www.cnblogs.com/lancidie/archive/2013/10/28/3392428.html 在socket网络程序中,TCP和UDP分别是面向连接和非面向连接的.因 ...
- 【转】关于TCP和UDP协议消息保护边界的介绍
在 socket网络程序中,TCP和UDP分别是面向连接和非面向连接的.因此TCP的socket编程,收发两端(客户端和服务器端)都要有一一成对的 socket,因此,发送端为了将多个发往接收端的包, ...
- UDP TCP 消息边界
先明确一个问题,如果定义了一个数据结构,大小是,比方说 32 个字节,然后 UDP 客户端连续向服务端发了两个包.现在假设这两个包都已经到达了服务器,那么服务端调用 recvfrom 来接收数据,并且 ...
- 介绍开源的.net通信框架NetworkComms框架之四 消息边界
原文网址: http://www.cnblogs.com/csdev Networkcomms 是一款C# 语言编写的TCP/UDP通信框架 作者是英国人 以前是收费的 目前作者已经开源 许可是 ...
- 【转】TCP协议的无消息边界问题
http://www.cnblogs.com/eping/archive/2009/12/12/1622579.html 使用TCP协议编写应用程序时,需要考虑一个问题:TCP协议是无消息边界的, ...
- Mina、Netty、Twisted一起学(二):TCP消息边界问题及按行分割消息
在TCP连接开始到结束连接,之间可能会多次传输数据,也就是服务器和客户端之间可能会在连接过程中互相传输多条消息.理想状况是一方每发送一条消息,另一方就立即接收到一条,也就是一次write对应一次rea ...
- 基于tcp的应用层消息边界如何定义
聊聊基于tcp的应用层消息边界如何定义 背景 2018年笔者有幸接触一个项目要用到长连接实现云端到设备端消息推送,所以借机了解过相关的内容,最终是通过rabbitmq+mqtt实现了相关功能,同时在心 ...
随机推荐
- 使用@class和#import的细节问题
在.h头文件中导入其它头文件可以使用#import语句,从而在该头文件下使用另一个文件中的类和成员,但是我在使用#import语句时却遇到了以下问题: 首先写一个ViewController类: #i ...
- lightoj Again Array Queries
1100 - Again Array Queries PDF (English) Statistics Forum Time Limit: 3 second(s) Memory Limit: 32 ...
- 具体解释coredump
一,什么是coredump 我们常常听到大家说到程序core掉了,须要定位解决,这里说的大部分是指相应程序因为各种异常或者bug导致在执行过程中异常退出或者中止,而且在满足一定条件下(这里为什么说须要 ...
- CF-192-diy-2
题目链接: http://codeforces.com/contest/330 A. Cakeminator 题目意思: 给一个r*c的矩阵方格,有些位置有S,如果某一行和一列都不含标记为S的方格,则 ...
- linux下内存调试工具——valgrind
1.valgrind之memcheck 最常用的工具,用来检测程序中出现的内存问题,所有对内存的读写都会被检测到,一切对malloc()/free()/new/delete的调用都会被捕获.所以,它 ...
- Js 返回页面 or 跳转页面
跳出 iframe 在当前页跳转, window.parent.frames.location.href=www.baidu.com" 跳转页面 onclick="history. ...
- LeetCode 二叉树的最小深度
计算二叉树的最小深度.最小深度定义为从root到叶子节点的最小路径. public class Solution { public int run(TreeNode root) { if(root = ...
- OpenGL ES 正反面设置指令
在OpenGL ES 中,仅有一种表面网格表示方式,那就是三角形. 三角形的三个顶点,可以组几个面?有答 1 的没有?有!那就是还不懂OpenGL ES 的我. 事实上,一张纸是有正反面的,那么一个三 ...
- QT学习 之 事件与事件过滤器(分为五个层次)
事件 在Qt中,事件是作为对象处理的,所有事件对象继承自抽象类QEvent.此类用来表示程序内部发生或者来自于外部但应用程序应该知道的动作.事件能够能过被 QObject 的子类接受或者处理,但是通常 ...
- linux grep详解
Table of Contents 1. grep简介 2. grep正则表达式元字符集(基本集) 3. 用于egrep和 grep -E的元字符扩展集 4. POSIX字符类 5. Grep命令选项 ...