一个简单的自定义通信协议(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

docker部署zabbix6.2.7+grafana

docker部署zabbix6.2.7+grafana。

qq_48391148的博客 3656

ODrive0.5.5源码分析(1) ADC的处理

odrive 0.5.5 ADC

danger的博客 4307

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

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

追梦客 2433

Lede(OpenWrt)安装和双宽带叠加

LEDE是一个专为路由器和嵌入式设备设计的自由和开源的操作系统。LEDE是OpenWRT项目的一个分支项目。LEDE操作系统包括许多路由器有用的功能,如网络地址转换、防火墙、端口转发、DHCP服务器等。

我的博客 6441

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

用OpenCV+ROS实现焊接机器人智能纠偏:线激光视觉的算法调参全流程

本文详细介绍了基于OpenCV和ROS的焊接机器人智能纠偏系统全流程。通过线激光视觉系统实时感知焊缝,重点解析了抗干扰图像预处理、改进的Steger算法提取激光条纹中心线,以及从2D像素到机器人基坐标系的完整坐标变换。该系统为应对焊接飞溅、弧光等复杂工况,实现高精度实时路径纠偏提供了从算法优化到工程集成的完整解决方案。

vodka的博客 159

从零到一:用STM32 FOC算法驯服无刷电机的艺术

本文详细介绍了如何使用STM32和FOC算法实现无刷电机的高性能控制。从硬件搭建、参数识别到闭环调节,一步步解析FOC算法的核心原理与调优技巧,帮助开发者掌握电机控制的关键技术,实现从零到一的精准驯服。

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

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值