我看不下去鸟。。。。Java和C#的socket通信真的简单吗?
这几天在博客园上看到好几个写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 ...
随机推荐
- 【组织级项目管理】P2 MSP P3O
组织级项目管理--有你,有我,有大家 在过去的2年,无论对于企业来讲,还是对于我们个人都有很多大脑的冲击,有几个词大家应该特别耳熟能详:转型,变革,敏捷,互联网+,组织的项目化管理等.就是这些让我们的 ...
- 【声明】前方不设坑位,不收费!~ 我为NET狂官方学习计划
发个通知,过段时间学习计划相关的东西就出来了,上次写了篇指引文章后有些好奇心颇重的人跟我说:“发现最近群知识库和技能库更新的频率有点大,这是要放大招的节奏啊!” 很多想学习却不知道如何规划的人想要一个 ...
- 关于python的bottle框架跨域请求报错问题的处理
在用python的bottle框架开发时,前端使用ajax跨域访问时,js代码老是进入不了success,而是进入了error,而返回的状态却是200.url直接在浏览器访问也是正常的,浏览器按F12 ...
- ASP.NET Core 中文文档 第四章 MVC(4.4)依赖注入和控制器
原文: Dependency Injection and Controllers 作者: Steve Smith 翻译: 刘浩杨 校对: 孟帅洋(书缘) ASP.NET Core MVC 控制器应通过 ...
- 神技!微信小程序(应用号)抢先入门教程(附最新案例DEMO-豆瓣电影)持续更新
微信小程序 Demo(豆瓣电影) 由于时间的关系,没有办法写一个完整的说明,后续配合一些视频资料,请持续关注 官方文档:https://mp.weixin.qq.com/debug/wxadoc/de ...
- 记录一次bug解决过程:数据迁移
一 总结 不擅长语言表达,勤于沟通,多锻炼 调试MyBatis中SQL语法:foreach 问题:缺少关键字VALUES.很遗憾:它的错误报的让人找不着北. 二 BUG描述:MyBatis中批量插入数 ...
- 通过sails和阿里大于实现短信验证
通过sails与阿里大于来实现注册短信验证码的发送,逻辑图如下 1.用户在客户端发送手机号给服务器,服务器接收到手机号,生成对应时间戳,随机四位数验证码 2.服务器将电话号码和验证码告诉阿里大于服务器 ...
- nuget常用命令
nuget命令的用法: 一.安装 1.安装指定版本类库install-package <程序包名> -version <版本号> 2.安装到指定的项目install-packa ...
- 感悟 GNU C 以及将 Vim 打造成 C/C++ 的半自动化 IDE
C 语言在 Linux 系统中的重要性自然是无与伦比.不可替代,所以我写 Linux 江湖系列不可能不提 C 语言.C 语言是我的启蒙语言,感谢 C 语言带领我进入了程序世界.虽然现在不靠它吃饭,但是 ...
- [翻译]AKKA笔记 - ACTOR生命周期 - 基本 -5
原文地址:http://rerun.me/2014/10/21/akka-notes-actor-lifecycle-basic/ (请注意这了讨论的生命周期并不包括 preRestart 或者pos ...