一个简单的自定义通信协议(socket)

Socket 接收数据线程, 处理粘包问题, 解释自定义报文数据 在网上很多新手在开发java TCP Socket 程序时, 都不知道怎么处理, 特别是在数据包粘在一起的时候, 不知道怎么取到完整的数据包, 也不知道从什么地方开始取数据 在Socket开发中最基本工作是: 确定双方交互的报文规范 本方的规范是: 报文开头: EB90EB90 报文格式: 报文头(4byte) + 数据长度(2byte) +类型(2byte) +数据(Nbyte)... 阅读详情

转自:http://vtrtbb.javaeye.com/blog/849336

这是转自javaeye的一篇文章,作者是vtrtbb。

按照网络通信的传统,我们都会自定义协议,这有很多好处,大家可以自己体会(嘿嘿)。

一直不知道socket通信时候自定义数据包是什么样子的,偶然做了个小例子。

先来说说数据包的定义,我这里是包头+内容 组成的:其中包头内容分为包类型+包长度, 那就是 消息对象=包类型+包长度+消息体

包类型 byte 型

包长度 int 型

消息体 byte[]

包总长度为 1 + 4 +  消息体.getBytes().length

发包方法如下:

 

  1. private void sendTextMsg(DataOutputStream out,String msg ) throws IOException {    
  2.         byte[] bytes= msg.getBytes();    
  3.         int totalLen = 1 + 4 + bytes.length;    
  4.                                 out.writeByte(1);    
  5.         out.writeInt(totalLen);    
  6.         out.write(bytes);    
  7.         out.flush();    
  8.     }    
 

 

客户端发送消息类为:

 

  1. import java.io.DataOutputStream;    
  2. import java.io.IOException;    
  3. import java.io.InputStream;    
  4. import java.io.OutputStream;    
  5. import java.net.Socket;    
  6. import java.net.UnknownHostException;    
  7. import java.util.Scanner;    
  8.     
  9. public class MsgClient {    
  10.     
  11.     private DataOutputStream outs;    
  12.         
  13.     public static void main(String[] args) {    
  14.         try {    
  15.             MsgClient client = new MsgClient();    
  16.             client.connServer("127.0.0.1", 9292);               
  17.         } catch (UnknownHostException e) {    
  18.             e.printStackTrace();    
  19.         } catch (IOException e) {    
  20.             e.printStackTrace();    
  21.         }    
  22.     }    
  23.         
  24.         
  25.     private void sendTextMsg(DataOutputStream out,String msg ) throws IOException {    
  26.         byte[] bytes= msg.getBytes();    
  27.         int totalLen = 1 + 4 + bytes.length;    
  28.         out.writeByte(1);    
  29.         out.writeInt(totalLen);    
  30.         out.write(bytes);    
  31.         out.flush();    
  32.     }       
  33.         
  34.     public void connServer(String ip,int port) throws UnknownHostException, IOException {    
  35.         Socket client = new Socket(ip,port);    
  36.         InputStream in = client.getInputStream();    
  37.         OutputStream out = client.getOutputStream();    
  38.         outs = new DataOutputStream(out);    
  39.         while(true) {    
  40.             Scanner scaner = new Scanner(System.in);    
  41.             sendTextMsg(outs, "测试消");    
  42.         }           
  43.     }    
 

 

服务端接收类为:

 

  1. import java.io.DataInputStream;    
  2. import java.io.FileOutputStream;    
  3. import java.io.IOException;    
  4. import java.io.InputStream;    
  5. import java.net.ServerSocket;    
  6. import java.net.Socket;    
  7.     
  8. public class MsgServer {    
  9.     public static void main(String[] args) {            
  10.         try {    
  11.             MsgServer server = new MsgServer();    
  12.             server.setUpServer(9090);    
  13.         } catch (IOException e) {    
  14.             e.printStackTrace();    
  15.         }    
  16.     }    
  17.         
  18.     public void setUpServer(int port) throws IOException {    
  19.         ServerSocket server = new ServerSocket(port);    
  20.         while(true) {    
  21.             Socket client = server.accept();    
  22.             System.out.println("客户端IP:"+client.getRemoteSocketAddress());    
  23.             processMesage(client);    
  24.         }    
  25.     }    
  26.         
  27.     private void processMesage(Socket client) throws IOException {    
  28.         InputStream ins = client.getInputStream();          
  29.         DataInputStream dins = new DataInputStream(ins);    
  30.         //服务端解包过程    
  31.         while(true) {    
  32.             int totalLen = dins.readInt();    
  33.             byte flag = dins.readByte();    
  34.             System.out.println("接收消息类型"+flag);    
  35.                 
  36.             byte[] data = new byte[totalLen - 4 - 1];    
  37.             dins.readFully(data);    
  38.             String msg = new String(data);    
  39.             System.out.println("发来的内容是:"+msg);      
  40.         }    
  41.     }    
  42. }    
 

 

这样就基本完成了,但实际还有好多问题,比如说服务端用如何用多线程服务来完成客户端的请求已提高效率,如果是NIO方式怎么来实现?多个消息类型时候怎么抽象?这些都没有考虑

另外有两个开源的框架不错,一个是apache  mina 还有个是netty ,有机会试试。

另一篇文章中叙述:

 

------------------

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协议的时候要特别小心了。具体可以使用以下代码去测试:

代码
         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。

【嵌入式项目应用】__UART自定义通信协议代码实现方法 通信协议不难理解,就是两个(或多个)设备之间进行通信,必须要遵循的一种协议。百度百科的解释:通信协议是指双方实体完成通信或服务所必须遵循的规则和约定。通过通信信道和设备互连起来的多个不同地理位置的数据通信系统,要使其能协同工作实现信息交换和资源共享,它们之间必须具有共同的语言。交流什么、怎样交流及何时交流,都必须遵循某种互相都能接受的规则。这个规则就是通信协议。相应该有很多读者都买过一些基于串口通信的模块,市面上很多基于串口通信的模块都是自定义通信协议,有的比较简单,有的相对复杂一点。 阅读详情

相关推荐

基于 TCP 协议的自定义 Socket 数据帧来进行短数据的传输

我们总是喜欢瞻仰大厂的大神们,但实际上大神也不过凡人,与菜鸟程序员相比,也就多花了几分心思,如果你再不努力,差距也只会越来越大。实际上,作为程序员,丰富自己的知识储备,提升自己的知识深度和广度是很有必要的。我们总是喜欢瞻仰大厂的大神们,但实际上大神也不过凡人,与菜鸟程序员相比,也就多花了几分心思,如果你再不努力,差距也只会越来越大。实际上,作为程序员,丰富自己的知识储备,提升自己的知识深度和广度是很有必要的。

579

基于 Socket 接口实现自定义协议通信

掌握 C 语言形式的 Socket 编程接口用法,能够正确发送和接收网络数据包 开发一个客户端,实现人机交互界面和与服务器的通信 开发一个服务端,实现并发处理多个客户端的请求程序界面不做要求,使用命令行或最简单的窗体即可。本实验涉及到网络数据包发送部分不能使用任何的 Socket 封装类,只能使用最底层的 C 语言形式的 Socket API。开发一个服务端,实现并发处理多个客户端的请求程序界面不做要求,使用命令行或最简单的窗体即可。将某客户端发送过来的内容转发给指定编号的其他客户端。

wm1634208243的博客 1103

一个简单的自定义通信协议 socket

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.youkuaiyun.com/jiangjunshow也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!                转自:http://v

yttyffggh的博客 6750

一个简单的自定义通信协议(socket) .

一个简单的自定义通信协议(socket) 转自:http://vtrtbb.javaeye.com/blog/849336 这是转自javaeye的一篇文章,作者是vtrtbb。 按照网络通信的传统,我们都会自定义协议,这有很多好处,大家可以自己体会(嘿嘿)。     一直不知道socket通信时候自定义数据包是什么样子的,偶然做了个小例子。   先来说说数据包的定义,我这里是...

超哥Blog 599

窥秘Windows 10:SAM密码破解

Windows 10作为广受欢迎的操作系统,拥有一套强大的安全措施来保护用户账号和数据安全。然而,有时候我们需要了解更多关于密码破解的知识,以加强我们的防护措施。本文将深入探讨如何突破Windows 10的SAM密码,以便更好地了解密码安全性并加强系统的保护。

Sgirll的博客 6844

RDP Wrapper失效了?别慌!教你如何手动为Windows 10/11最新版本添加rdpwrap.ini配置

本文详细介绍了当RDP Wrapper在Windows 10/11最新版本中失效时,如何手动添加rdpwrap.ini配置以恢复多用户远程桌面功能。通过获取最新配置文件、安全替换配置的完整流程以及高级手动配置技巧,帮助用户解决[Not support]问题,确保远程工作流程不受系统更新影响。

weixin_30765577的博客 624

系统间通信:通过自定义协议通信加深HTTP理解

系统间通信:通过自定义协议通信加深HTTP理解 一、HTTP协议栈         HTTP是Hypertext TransferProtocol(超文本传输协议)的缩写。它是万维网协会(WorldWild Web Consortium)和IETF(InternetEngineering Task Force)合作的成果,并逐步发展成为整个互联网信息交换的标准,当今普遍采用的版本是HTTP1.

追梦客 2433

深度学习暑期科研项目(两个月发EI论文)

对本科生而言,越早接触系统的科研训练开始上手科研项目,就越能在未来的升学求职中占据很大的优势。暑假是提升个人简历、丰富科研经历的最佳时期!哈尔滨工业大学博士师兄也会在8月1日开始带大家上手科研项目,并撰写发表EI会议论文。

Magnolia_He的博客 1444

TCP服务:Socket实现文件上传下载

客户端 package ServerClient; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Obje

qq_41939099的博客 429

游戏开发实战:如何在Unity中实现六边形网格地图(附完整代码)

本文详细介绍了在Unity中实现六边形网格地图的完整开发流程,从数学原理到实战代码。涵盖三种主流坐标系转换方法、距离计算算法对比、网格生成与渲染技巧,以及高级功能如网格高亮、寻路算法和性能优化策略。适合策略游戏、战棋类游戏开发者提升地图系统开发效率。

656

【优化调度】基于混合整数线性规划的电力系统机组组合优化(Matlab实现)

本文详细介绍了基于混合整数线性规划(MILP)的电力系统机组组合优化方法,通过Matlab实现高效调度决策。内容涵盖目标函数构建、约束条件设置及求解器调优,帮助降低发电成本并提升系统稳定性。适用于电力系统调度员和能源优化研究人员,实现从理论到实践的完整解决方案。

weixin_29050271的博客 585
下一篇: 一种自定义网络通信协议
Open9i
博客等级 码龄16年 14粉丝 43原创
评论 5
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值