TCP Socket协议定义
2014-05-05 20:18
176 查看
TCP Socket协议定义
------------------
本文从这里开始,主要介绍TCP的socket编程。
新手们(例如当初的我),第一次写socket,总是以为在发送方压入一个"Helloworld",接收方收到了这个字符串,就“精通”了Socket编程了。而实际上,这种编程根本不可能用在现实项目,因为:
1. socket在传输过程中,helloworld有可能被拆分了,分段到达客户端),例如 hello + world,一个分段就是一个包(Package),这个就是分包问题。
2. socket在传输过成功,不同时间发送的数据包有可能被合并,同时到达了客户端,这个就是黏包问题。例如发送方发送了hello+world,而接收方可能一次就接受了helloworld.
3. socket会自动在每个包后面补n个 0x0 byte,分割包。具体怎么去补,这个我就没有深入了解。
4. 不同的数据类型转化为byte的长度是不同的,例如int转为byte是4位(int32),这样我们在***socket协议的时候要特别小心了。具体可以使用以下代码去测试:
![](http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
代码
public void test()
{
int myInt = 1;
byte[] bytes = new byte[1024];
BinaryWriter writer = new BinaryWriter(new MemoryStream(bytes));
writer.Write(myInt);
writer.Write("j");
writer.Close();
}
尽管socket环境如此恶劣,但是TCP的链接也至少保证了:
包发送顺序在传输过程中是不会改变的,例如发送方发送 H E L L,那么接收方一定也是顺序收到H E L L,这个是TCP协议承诺的,因此这点成为我们解决分包、黏包问题的关键。
如果发送方发送的是helloworld, 传输过程中分割成为hello+world,那么TCP保证了在hello与world之间没有其他的byte。但是不能保证helloworld和下一个命令之间没有其他的byte。
因此,如果我们要使用socket编程,就一定要编写自己的协议。目前业界主要采取的协议定义方式是:包头+包体长度+包体。具体如下:
1. 一般包头使用一个int定义,例如int = 173173173;作用是区分每一个有效的数据包,因此我们的服务器可以通过这个int去切割、合并包,组装出完整的传输协议。有人使用回车字符去分割包体,例如常见的SMTP/POP协议,这种做法在特定的协议是没有问题的,可是如果我们传输的信息内容自带了回车字符串,那么就糟糕了。所以在设计协议的时候要特别小心。
2. 包体长度使用一个int定义,这个长度表示包体所占的比特流长度,用于服务器正确读取并分割出包。
3. 包体就是自定义的一些协议内容,例如是对像序列化的内容(现有的系统已经很常见了,使用对象序列化、反序列化能够极大简化开发流程,等版本稳定后再转入手工压入byte操作)。
一个实际编写的例子:比如我要传输2个整型 int = 1, int = 2,那么实际传输的数据包如下:
173173173 8 1 2
|------包头------|----包体长度----|--------包体--------|
这个数据包就是4个整型,总长度 = 4*4 = 16。
------------------
本文从这里开始,主要介绍TCP的socket编程。
新手们(例如当初的我),第一次写socket,总是以为在发送方压入一个"Helloworld",接收方收到了这个字符串,就“精通”了Socket编程了。而实际上,这种编程根本不可能用在现实项目,因为:
1. socket在传输过程中,helloworld有可能被拆分了,分段到达客户端),例如 hello + world,一个分段就是一个包(Package),这个就是分包问题。
2. socket在传输过成功,不同时间发送的数据包有可能被合并,同时到达了客户端,这个就是黏包问题。例如发送方发送了hello+world,而接收方可能一次就接受了helloworld.
3. socket会自动在每个包后面补n个 0x0 byte,分割包。具体怎么去补,这个我就没有深入了解。
4. 不同的数据类型转化为byte的长度是不同的,例如int转为byte是4位(int32),这样我们在***socket协议的时候要特别小心了。具体可以使用以下代码去测试:
![](http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
代码
public void test()
{
int myInt = 1;
byte[] bytes = new byte[1024];
BinaryWriter writer = new BinaryWriter(new MemoryStream(bytes));
writer.Write(myInt);
writer.Write("j");
writer.Close();
}
尽管socket环境如此恶劣,但是TCP的链接也至少保证了:
包发送顺序在传输过程中是不会改变的,例如发送方发送 H E L L,那么接收方一定也是顺序收到H E L L,这个是TCP协议承诺的,因此这点成为我们解决分包、黏包问题的关键。
如果发送方发送的是helloworld, 传输过程中分割成为hello+world,那么TCP保证了在hello与world之间没有其他的byte。但是不能保证helloworld和下一个命令之间没有其他的byte。
因此,如果我们要使用socket编程,就一定要编写自己的协议。目前业界主要采取的协议定义方式是:包头+包体长度+包体。具体如下:
1. 一般包头使用一个int定义,例如int = 173173173;作用是区分每一个有效的数据包,因此我们的服务器可以通过这个int去切割、合并包,组装出完整的传输协议。有人使用回车字符去分割包体,例如常见的SMTP/POP协议,这种做法在特定的协议是没有问题的,可是如果我们传输的信息内容自带了回车字符串,那么就糟糕了。所以在设计协议的时候要特别小心。
2. 包体长度使用一个int定义,这个长度表示包体所占的比特流长度,用于服务器正确读取并分割出包。
3. 包体就是自定义的一些协议内容,例如是对像序列化的内容(现有的系统已经很常见了,使用对象序列化、反序列化能够极大简化开发流程,等版本稳定后再转入手工压入byte操作)。
一个实际编写的例子:比如我要传输2个整型 int = 1, int = 2,那么实际传输的数据包如下:
173173173 8 1 2
|------包头------|----包体长度----|--------包体--------|
这个数据包就是4个整型,总长度 = 4*4 = 16。
相关文章推荐
- Socket 之 TCP 协议通信-c#实现
- 简述Socket,IP,TCP三次握手,HTTP协议
- 轨迹系列——Socket总结及实现基于TCP或UDP的809协议方法
- 【Java TCP/IP Socket】构建和解析自定义协议消息(含代码)
- 【Java】【网络协议】Http,TCP/IP,Socket,XMMP
- java.net.SocketException(permission denied)安卓端写的TCP协议软件报错 原因是建立的套接字没有限权对外连接。
- Linux网络编程之[基于socket通信的tcp协议的编程模型]
- uc笔记10---网络通信,套接字(Socket),基于 TCP 协议的客户机/服务器模型
- TCP,http协议区分,以及socket
- tcp/ip socket通信协议
- 基于 TCP (面向连接)和无连接UDP协议的 socket 套接字编程
- JAVA之旅(三十二)——JAVA网络请求,IP地址,TCP/UDP通讯协议概述,Socket,UDP传输,多线程UDP聊天应用
- 网络篇——七层协议、四层协议、TCP、HTTP、SOCKET、长短连接
- SOCKET,TCP/UDP,HTTP,FTP网络协议之间的关系
- 【Java TCP/IP Socket】应用程序协议中消息的成帧与解析(含代码)
- 网络协议的详细讲解,Socket和TCP/IP还有HTTP
- HTTP协议,Socket和WebSocket以及TCP/UDP连接小结
- Socket之TCP和UDP协议通信
- TCP/IP、Http、Socket的区别以及面试中关于网络协议常问的考点
- 计算机网络Socket编程之TCP协议