这几天在博客园上看到好几个写Java和C#的socket通信的帖子。但是都为指出其中关键点。
C# socket通信组件有很多,在vs 使用nuget搜索socket组件有很多类似的。本人使用的是自己开发的一套组件。
Java socket通信的组件也有很多,常用的大多数都是用的mina或者netty。游戏行业使用也是居多。
关于socket的底层写法,实在太多,我就不在BB。
这里我想说,C#和C++或者叫VC++把是使用小端序作为字节序。而java使用的是大端序作为字节序。
也就是说比如一个int占用四个字节,java的字节序和c#的字节序是相反的,java的int四个字节第一个字节在数组的最后一个。C#是第一个。
也就是说如果java端正常发送一个int的字节序给C#,需要翻转一次端绪。反之也是一样的。一句话来概括的话就是高位在前还是低位在前的问题。
C#输出数字 int 4 的字节序。为了保证c#下面绝对是是int所以加入了强制int转化。默认的话可能是byte
java的默认输出,这里使用的是netty的默认框架。进行的int4的字节序输出
高位和低位表示法完全不同。
java下面如果传输字符串,那么必须要先把字符串转化成byte数组,然后获取数组长度,在字节序里面压入int表示的数组长度,然后在然如byte数组。不管你的字符串多长。
而C#也是相同做法。但是唯一不同的是数组的长度表示法不同。微软经过了字节压缩的。用字节的前7位表示长度。第8位表示下一个字节是否也是表示长度的字节,值需要与128位于。
从而减少字节的消耗。
现在一般如果我们在java和C#中无论是哪一个语言作为服务器。架设socket通信基准。其中另外一方都要妥协字节序反转问题。
大多数情况下我们也许通信的要求不高,或许把一些类或者参数通过json格式化以后传输给对方。但是在这一条消息的传输中,一般会有两个int需要字节序。最少也要一个字节序。
一个字节序int表示消息长度。另外一个字节序表示消息协议。
如果消息协议都放到json里面没有问题。但是消息长度是必不可少的。因为你需要知道在网络环境中,消息压栈,然后等待系统发出是有可能两条消息一同发送的。也或者消息发送后由于网络阻塞,前后相差好几秒的消息同一时间达到。
这就是所谓的粘包。
我这里就不表演了。
还有另外一种通信方式,就是通过protobuf进行字节序的序列化,和反序列,官方支持java,第三方支持C#。这个组件可以减少字节流。达到省流量,减少网络资源消耗的问题。
例如一个long的类型值是1常规发送需要8个字节,64位。发送。如果改用protobuf的话只需要1字节8位就能发送。
同样的问题,无论你使用哪一种序列化方式,都需要消息长度和消息协议号。
C#下面对int的反转读取。
/// <summary> /// 读取大端序的int /// </summary> /// <param name="value"></param> public int ReadInt(byte[] intbytes) { Array.Reverse(intbytes); ); } /// <summary> /// 写入大端序的int /// </summary> /// <param name="value"></param> public byte[] WriterInt(int value) { byte[] bs = BitConverter.GetBytes(value); Array.Reverse(bs); return bs; }
粘包问题解决。
C#代码
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; /** * * @author 失足程序员 * @Blog http://www.cnblogs.com/ty408/ * @mail 492794628@qq.com * @phone 13882122019 * */ namespace Sz.Network.SocketPool { public class MarshalEndian : IMarshalEndian { public enum JavaOrNet { Java, Net, } public MarshalEndian() { } public static JavaOrNet JN = JavaOrNet.Net; /// <summary> /// 读取大端序的int /// </summary> /// <param name="value"></param> public int ReadInt(byte[] intbytes) { Array.Reverse(intbytes); ); } /// <summary> /// 写入大端序的int /// </summary> /// <param name="value"></param> public byte[] WriterInt(int value) { byte[] bs = BitConverter.GetBytes(value); Array.Reverse(bs); return bs; } //用于存储剩余未解析的字节数 ); //字节数常量一个消息id4个字节 const long ConstLenght = 4L; public void Dispose() { this.Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool flag1) { if (flag1) { IDisposable disposable = this._LBuff as IDisposable; if (disposable != null) { disposable.Dispose(); } } } public byte[] Encoder(SocketMessage msg) { MemoryStream ms = new MemoryStream(); BinaryWriter bw = new BinaryWriter(ms, UTF8Encoding.Default); byte[] msgBuffer = msg.MsgBuffer; if (msgBuffer != null) { switch (JN) { case JavaOrNet.Java: bw.Write(WriterInt(msgBuffer.Length + )); bw.Write(WriterInt(msg.MsgID)); break; case JavaOrNet.Net: bw.Write((Int32)(msgBuffer.Length + )); bw.Write(msg.MsgID); break; } bw.Write(msgBuffer); } else { switch (JN) { case JavaOrNet.Java: bw.Write(WriterInt()); break; case JavaOrNet.Net: bw.Write((Int32)); break; } } bw.Close(); ms.Close(); bw.Dispose(); ms.Dispose(); return ms.ToArray(); } public List<SocketMessage> Decoder(byte[] buff, int len) { //拷贝本次的有效字节 byte[] _b = new byte[len]; Array.Copy(buff, , _b, , _b.Length); buff = _b; ) { //拷贝之前遗留的字节 this._LBuff.AddRange(_b); buff = this._LBuff.ToArray(); this._LBuff.Clear(); ); } List<SocketMessage> list = new List<SocketMessage>(); MemoryStream ms = new MemoryStream(buff); BinaryReader buffers = new BinaryReader(ms, UTF8Encoding.Default); try { byte[] _buff; Label_0073: //判断本次解析的字节是否满足常量字节数 if ((buffers.BaseStream.Length - buffers.BaseStream.Position) < ConstLenght) { _buff = buffers.ReadBytes((int)(buffers.BaseStream.Length - buffers.BaseStream.Position)); this._LBuff.AddRange(_buff); } else { ; switch (JN) { case JavaOrNet.Java: offset = ReadInt(buffers.ReadBytes()); break; case JavaOrNet.Net: offset = buffers.ReadInt32(); break; } //剩余字节数大于本次需要读取的字节数 if (offset <= (buffers.BaseStream.Length - buffers.BaseStream.Position)) { ; switch (JN) { case JavaOrNet.Java: msgID = ReadInt(buffers.ReadBytes()); break; case JavaOrNet.Net: msgID = buffers.ReadInt32(); break; } _buff = buffers.ReadBytes(()); list.Add(new SocketMessage(msgID, _buff)); goto Label_0073; } else { //剩余字节数刚好小于本次读取的字节数 存起来,等待接受剩余字节数一起解析 buffers.BaseStream.Seek(ConstLenght, SeekOrigin.Current); _buff = buffers.ReadBytes((int)(buffers.BaseStream.Length - buffers.BaseStream.Position)); this._LBuff.AddRange(_buff); } } } catch { } finally { buffers.Close(); if (buffers != null) { buffers.Dispose(); } ms.Close(); if (ms != null) { ms.Dispose(); } } return list; } } }
java netty
/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package sz.network.socketpool.nettypool; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ByteToMessageDecoder; import java.nio.ByteOrder; import java.util.ArrayList; import java.util.List; import org.apache.log4j.Logger; /** * 解码器 */ class NettyDecoder extends ByteToMessageDecoder { private static final Logger logger = Logger.getLogger(NettyDecoder.class); private byte ZreoByteCount = 0; private ByteBuf bytes; private final ByteOrder endianOrder = ByteOrder.LITTLE_ENDIAN; private long secondTime = 0; private int reveCount = 0; public NettyDecoder() { } ByteBuf bytesAction(ByteBuf inputBuf) { ByteBuf bufferLen = Unpooled.buffer(); if (bytes != null) { bufferLen.writeBytes(bytes); bytes = null; } bufferLen.writeBytes(inputBuf); return bufferLen; } /** * 留存无法读取的byte等待下一次接受的数据包 * * @param bs 数据包 * @param startI 起始位置 * @param lenI 结束位置 */ void bytesAction(ByteBuf intputBuf, int startI, int lenI) { if (lenI - startI > 0) { bytes = Unpooled.buffer(); bytes.writeBytes(intputBuf, startI, lenI); } } @Override protected void decode(ChannelHandlerContext chc, ByteBuf inputBuf, List<Object> outputMessage) { if (System.currentTimeMillis() - secondTime < 1000L) { reveCount++; } else { secondTime = System.currentTimeMillis(); reveCount = 0; } if (reveCount > 50) { logger.error("发送消息过于频繁"); chc.disconnect(); return; } if (inputBuf.readableBytes() > 0) { ZreoByteCount = 0; //重新组装字节数组 ByteBuf buffercontent = bytesAction(inputBuf); List<NettyMessageBean> megsList = new ArrayList<>(0); for (;;) { //读取 消息长度(short)和消息ID(int) 需要 8 个字节 if (buffercontent.readableBytes() >= 8) { ///读取消息长度 int len = buffercontent.readInt(); if (buffercontent.readableBytes() >= len) { int messageid = buffercontent.readInt();///读取消息ID ByteBuf buf = buffercontent.readBytes(len - 4);//读取可用字节数; megsList.add(new NettyMessageBean(chc, messageid, buf.array())); //第二次重组 if (buffercontent.readableBytes() > 0) { bytesAction(buffercontent, buffercontent.readerIndex(), buffercontent.readableBytes()); buffercontent = Unpooled.buffer(); buffercontent.writeBytes(bytes); continue; } else { break; } } ///重新设置读取进度 buffercontent.setIndex(buffercontent.readableBytes() - 2, inputBuf.readableBytes()); } ///缓存预留的字节 bytesAction(buffercontent, buffercontent.readerIndex(), buffercontent.readableBytes()); break; } outputMessage.addAll(megsList); } else { ZreoByteCount++; if (ZreoByteCount >= 3) { //todo 空包处理 考虑连续三次空包,断开链接 logger.error("decode 空包处理 连续三次空包"); chc.close(); } } } }
这是我封装的部分代码,因为现目前公司的开发组织架构为,java是服务器端。U3D 使用C#是客户端开发。所以考虑性能问题,是C#妥协进行字节序反转操作~!
就不在添加调试和测试代码和结果因为觉得没多少意义~!
到此结束~!
我看不下去鸟。。。。Java和C#的socket通信真的简单吗?的更多相关文章
-
Java网络编程和NIO详解1:JAVA 中原生的 socket 通信机制
Java网络编程和NIO详解1:JAVA 中原生的 socket 通信机制 JAVA 中原生的 socket 通信机制 摘要:本文属于原创,欢迎转载,转载请保留出处:https://github.co ...
-
java和C#之间SOCKET通信的问题
转自:http://www.cdtarena.com/javapx/201307/9170.html java和C#之间SOCKET通信的问题 一.服务器端(使用java编写) /** * 监听客户端 ...
-
Java和C#的socket通信相关(转)
这几天在博客园上看到好几个写Java和C#的socket通信的帖子.但是都为指出其中关键点. C# socket通信组件有很多,在vs 使用nuget搜索socket组件有很多类似的.本人使用的是自己 ...
-
Java进阶(四十七)Socket通信
Java进阶(四十七)Socket通信 今天讲解一个 Hello Word 级别的 Java Socket 通信的例子.具体通讯过程如下: 先启动Server端,进入一个死循环以便一直监听某端口是 ...
-
Java和C++通过Socket通信中文乱码的解决
理想的开发状态是我开始就是C开发,一直是C的开发,现在还是C的开发,若干年后,幸运的话,我可以成为C语言的高手或者专家…… 更实际的情况是我开始是C开发,后来变成了JAVA开发,然后又做起了VC++的 ...
-
java 网络编程之TCP通信和简单的文件上传功能
*/ .hljs { display: block; overflow-x: auto; padding: 0.5em; color: #333; background: #f8f8f8; } .hl ...
-
java 网络编程之UDP通信和简单的群聊程序
*/ .hljs { display: block; overflow-x: auto; padding: 0.5em; color: #333; background: #f8f8f8; } .hl ...
-
170411、java Socket通信的简单例子(UDP)
服务端代码: package com.bobohe.socket; import java.io.*; import java.net.*; class UDPServer { public stat ...
-
170410、java Socket通信的简单例子(TCP)
服务端代码: package com.bobohe.socket; import java.io.*; import java.net.*; import java.applet.Applet; pu ...
随机推荐
-
php+ajax 登录注册页面
主要是登录注册功能,前端后台验证没有什么,这个大家可以自己加上去,比如过滤啊,正则啊等 还是先放图吧 这是登录及注册界面 点击注册切换到注册界面,点击登录切换到登录界面 <!DOCTYPE h ...
-
[转载]来,让我们谈一谈Normalize.css
来源 : http://segmentfault.com/a/1190000002239676 ---------------------------------------------------- ...
-
Busybox下tftp命令使用详解
http://blog.chinaunix.net/uid-375398-id-1991686.html Busybox下的tftp命令可以用来进行单文件传输.使用的时候,是把电脑作为服务器Serve ...
-
SAS学习笔记<;一>;
三个周末的SAS课程宣告结束, 总结下来 第一周的统计原理简介 第二周/第三周讲解SAS的基本操作. 总体下来,对自己的知识结构有了一个新的梳理,对比大学时期,某个老师一上来就教我们SAS编程,而未考 ...
-
通俗易懂------this指向
因为JavaScript 中this 是在运行期进行绑定的,因此JavaScript 中this 关键字具备多重含义. 具体在实际应用中,this的指向大致可以分为下面4种. 作为对象的方法调用 ...
-
重载operator new实现检测内存泄漏是否可行
行与不行,就凭我这水平,说出来未免显示太过自大.不还,我还想根据自己的代码来讨论这个问题. 重载operator new来检测内存只的办法,那就是在new的时候记录指针地址及文件名.行号,在delet ...
-
【Tomcat】项目自动部署的链接重置错误
在服务器中装好的tomcat7 ,(服务器是的window server 2008) 在tomcat bin目录运行的 service.bat install 安装服务.然后,设置服务项(服务项名称: ...
-
Spring in Action --- 第二章 装配Bean
Spirng配置的可选方案 在XML中进行显示配置 在Java中进行显示配置 隐式的bean发现机制和自动装配 bean装配 1. 在希望被扫描到的类上加注解 @Component 2. 基于不同的配 ...
-
6. VIM 系列 - 全局搜索(ctrlsf.vim)
目录 全局搜索利器 ag.vim 更强大的全局搜索利器 ctrlsf.vim 全局搜索利器 ag.vim 终端上安装ag: sudo apt install silversearcher-ag vim ...
-
DOM中获取宽高、位置总结
原生JS 一.文档.窗口的宽高和位置 // 获取屏幕的宽高 window.screen.height | window.screen.width // 屏幕可用工作区宽高 window.screen. ...