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

来源:互联网 发布:淘宝数据插件 编辑:程序博客网 时间:2024/06/05 13:22

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

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

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

 

 

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

 

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

 

包类型 byte 型

包长度 int 型

消息体 byte[]

 

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

 

发包方法如下:

view plaincopy to clipboardprint?
  1. private void sendTextMsg(DataOutputStream out,String msg ) throws IOException {          byte[] bytes= msg.getBytes();          int totalLen = 1 + 4 + bytes.length;                                  out.writeByte(1);          out.writeInt(totalLen);          out.write(bytes);          out.flush();      }    
 

 

客户端发送消息类为:

view plaincopy to clipboardprint?
  1. import java.io.DataOutputStream;  import java.io.IOException;  import java.io.InputStream;  import java.io.OutputStream;  import java.net.Socket;  import java.net.UnknownHostException;  import java.util.Scanner;    public class MsgClient {        private DataOutputStream outs;            public static void main(String[] args) {          try {              MsgClient client = new MsgClient();              client.connServer("127.0.0.1"9292);                     } catch (UnknownHostException e) {              e.printStackTrace();          } catch (IOException e) {              e.printStackTrace();          }      }                  private void sendTextMsg(DataOutputStream out,String msg ) throws IOException {          byte[] bytes= msg.getBytes();          int totalLen = 1 + 4 + bytes.length;          out.writeByte(1);          out.writeInt(totalLen);          out.write(bytes);          out.flush();      }               public void connServer(String ip,int port) throws UnknownHostException, IOException {          Socket client = new Socket(ip,port);          InputStream in = client.getInputStream();          OutputStream out = client.getOutputStream();          outs = new DataOutputStream(out);          while(true) {              Scanner scaner = new Scanner(System.in);              sendTextMsg(outs, "测试消");          }             }    
 

 

服务端接收类为:

view plaincopy to clipboardprint?
  1. import java.io.DataInputStream;  import java.io.FileOutputStream;  import java.io.IOException;  import java.io.InputStream;  import java.net.ServerSocket;  import java.net.Socket;    public class MsgServer {      public static void main(String[] args) {                  try {              MsgServer server = new MsgServer();              server.setUpServer(9090);          } catch (IOException e) {              e.printStackTrace();          }      }            public void setUpServer(int port) throws IOException {          ServerSocket server = new ServerSocket(port);          while(true) {              Socket client = server.accept();              System.out.println("客户端IP:"+client.getRemoteSocketAddress());              processMesage(client);          }      }            private void processMesage(Socket client) throws IOException {          InputStream ins = client.getInputStream();                DataInputStream dins = new DataInputStream(ins);          //服务端解包过程          while(true) {              int totalLen = dins.readInt();              byte flag = dins.readByte();              System.out.println("接收消息类型"+flag);                            byte[] data = new byte[totalLen - 4 - 1];              dins.readFully(data);              String msg = new String(data);              System.out.println("发来的内容是:"+msg);            }      }  }    
 

 

 

这样就基本完成了,但实际还有好多问题,比如说服务端用如何用多线程服务来完成客户端的请求已提高效率,如果是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();