C# client 与java netty 服务端的简单通信,客户端采用Unity。
来源:互联网 发布:手机查工资的软件 编辑:程序博客网 时间:2024/05/17 23:32
http://blog.csdn.net/wilsonke/article/details/24721057
C# client 与java netty 服务端的简单通信,客户端采用Unity。
近日根据官方提供的通信例子自己写了一个关于Unity(C#)和后台通信的类,拿出来和大家分享一下。
具体请参考:
1.Java服务端用的apach.mina框架搭建。java服务端请参考:http://blog.9tech.cn/?c=site&m=article&id=548
2.C#环境:.NET framework 2.0
3.C#帮组文档,及Socket注解:http://msdn.microsoft.com/zh-cn/library/system.net.sockets.socket(v=vs.85).aspx
4.官方例子:http://msdn.microsoft.com/zh-cn/library/bew39x2a(v=VS.85).aspx#CommunityContent
个人觉得,最难的地方在与以下几个地方:
1.封装数据,和后台的编解码格式保持一致
封装数据,其实就是一个前后台约定好一个通信格式。比如:获得所以数据后并写入字节数组后,在吧这个字节数组的长度读出来(4个字节的整形数据),再封装进一个字节数组中。
所以最终的数据字节数组内容是:4个字节的数据长度+实际数据的字节数组。
当然数据的加密加压可以在吧实际数据存入字节数组后就进行,那么发送的长度就是加密加压后的数据长度了。
实现方法:
String message =
"shit "
;
//5个字节
byte
[] bytes = Encoding.UTF8.GetBytes(message);
//未加密加压的实际数据
byte
[] prefixBytes =BitConverter.GetBytes(bytes.Length);
//数据长度
Array.Reverse(prefixBytes);
byte
[] sendBytes =
new
byte
[bytes.Length + 4];
//吧字节数组合并到sendBytes
System.Buffer.BlockCopy(prefixBytes ,0,sendBytes ,0,4);
System.Buffer.BlockCopy(bytes,0,sendBytes ,4,bytes.Length);
//合并完毕,就可以使用socket吧sendBytes发送出去了,后台也是有同样的格式解码就可以了。
2.异步通信的线程管理
异步通信的线程安全一直是一个难点,它不像ActionScript那样,建立通信连接后注册事件用来侦听即可。但是在C#中就必须让线程等待当前的异步操作完成后才可以继续向下执行。C#异步通信中可以使用ManualResetEvent类来处理这类问题。当需要暂停执行后续代码以完成异步操作时。使用ManualResetEvent的WaitOne();方法来阻塞这个线程(类似与java的ReentrantLock类),但是必须在异步操作完成后使线程恢复,否则就会出现线程被锁死的情况。使用ManualResetEvent的Set()方法即可。
实现方法:
private
static
ManualResetEvent connectDone =
new
ManualResetEvent(
false
);
public
void
startConnect(){
Connect();
connectDone.WaitOne();
//阻塞线程,
receive();
}
public
void
Connect(){
// to do connection
//socket.BeginConnect....
}
public
void
connectCallback(IAsyncResult ar){
Socket socket = (Socket) ar.AsyncState;
socket .EndConnect(ar);
connectDone.Set();
//恢复线程,继续向下执行
}
public
void
receive(){
// to do received message
}
ManualResetEvent类的帮组文档及例子:http://msdn.microsoft.com/zh-cn/library/system.threading.manualresetevent_members(v=vs.85).aspx
3.关于数据的压缩,和解压.
对于数据的压缩和解压可以采用ICSharpCode.SharpZipLib这个动态链接库。下载地址:http://www.icsharpcode.net/opensource/sharpziplib/
下载后再MonoDevelop里倒入引用就可以了,位置:Project->Edit References..
using ICSharpCode.SharpZipLib.Zip;
然后在代码里就可以倒入类了
参考:http://blog.sina.com.cn/s/blog_62fda93c0101d51j.html
4.接收和读取数据的操作
在接受服务端发送的数据时,也根据同样的格式进行解读;先读取4个字节的数据长度,再跟进这个长度得到实际的数据,最后在解密和解压就可以得到最终的数据了。
但是在这个操作过程中,会出现一些意想不到的麻烦。
大致流程是:
采取分段读取的方式,第一次只读取4个字节的长度信息。
取得长度后,根据设置的每次分段读取数据长度来读取,知道所得的数据和总长度相同;
每次分段读取的数据的长度不一定都是设置的长度,所以将每次读取的数据写入内存流MemoryStream类中。特别重要的每次操作MemoryStream类时注意设置它的Position位置,不然会出现你本来已经成功的存入了数据,但是由于Position的原因没有找准需要取出或者存入数据的准确位置而读取数据失败。
详细代码如下:
using
UnityEngine;
using
System.Collections;
using
System.Collections.Generic;
using
System;
using
System.IO;
using
System.Threading;
using
System.Text;
using
System.Net;
using
System.Net.Sockets;
using
ICSharpCode.SharpZipLib.Zip;
using
ICSharpCode.SharpZipLib.GZip;
using
LitJson;
/**
* 连接对象
* @author duwei
* @version 1.0.0
* build time :2013.11.7
* */
public
class
BufferConnection{
public
Socket socket =
null
;
public
const
int
prefixSize = 4;
public
String ip =
"192.168.1.105"
;
public
int
port = 8005;
private
static
ManualResetEvent connectDone =
new
ManualResetEvent(
false
);
private
static
ManualResetEvent sendDone =
new
ManualResetEvent(
false
);
private
static
ManualResetEvent receiveDone =
new
ManualResetEvent(
false
);
public
BufferConnection(){
}
// State object for receiving data from remote device.
public
class
StateObject {
// Client socket.
public
Socket workSocket =
null
;
// Size of receive buffer.
public
const
int
BufferSize = 1024;
// Receive buffer.
public
byte
[] buffer =
new
byte
[BufferSize];
}
/**开始建立socket连接*/
public
void
startConnect(){
try
{
Debug.Log(
"starting connection..."
);
IPAddress ipd = IPAddress.Parse(ip);
EndPoint endPoint =
new
IPEndPoint(ipd,port);
socket =
new
Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
socket.BeginConnect(endPoint,
new
AsyncCallback(connectCallback),socket);
connectDone.WaitOne();
receive(socket);
//receiveDone.WaitOne();
}
catch
(Exception e){
Console.WriteLine(e.ToString());
}
}
public
void
connectCallback(IAsyncResult ar){
try
{
Socket backSocket = (Socket)ar.AsyncState;
backSocket.EndConnect(ar);
connectDone.Set();
Debug.Log(
"on connected"
);
}
catch
(Exception e){
Console.WriteLine(e.ToString());
}
}
/**发送数据,目前只支持 String 类型数据*/
public
void
send(Socket client,String msg){
//封装数据
byte
[] byteData = Encoding.UTF8.GetBytes(msg);
byte
[] sendData =
new
byte
[byteData.Length + prefixSize];
byte
[] sizeData = BitConverter.GetBytes(byteData.Length);
//反转
Array.Reverse(sizeData);
//合并
System.Buffer.BlockCopy(sizeData,0,sendData,0,prefixSize);
System.Buffer.BlockCopy(byteData,0,sendData,prefixSize,byteData.Length);
try
{
//socket.Send(sendData);
client.BeginSend(sendData,0,sendData.Length,0,
new
AsyncCallback(sendCallback),client);
Debug.Log(
"data send finished, data size:"
+sendData.Length);
}
catch
(Exception e){
Console.WriteLine(e.ToString());
}
}
public
void
send(String msg){
if
(socket !=
null
){
send(socket,msg);
sendDone.WaitOne();
}
}
public
void
sendCallback(IAsyncResult ar){
try
{
Socket socket = (Socket)ar.AsyncState;
if
(ar.IsCompleted){
int
endPoint = socket.EndSend(ar);
Debug.Log(
"send data finished endpoint: "
+endPoint);
sendDone.Set();
}
}
catch
(Exception e){
Console.WriteLine(e.ToString());
}
}
public
void
receive(Socket socket){
try
{
StateObject so =
new
StateObject();
so.workSocket = socket;
//第一次读取数据的总长度
socket.BeginReceive(so.buffer,0,prefixSize,0,
new
AsyncCallback(receivedCallback),so);
//测试用:数据在1024以内的数据,一次性读取出来
//socket.BeginReceive(so.buffer,0,StateObject.BufferSize,0,new AsyncCallback(simpleCallback),so);
}
catch
(Exception e){
Console.WriteLine(e.ToString());
}
}
public
void
simpleCallback(IAsyncResult ar){
StateObject so = (StateObject)ar.AsyncState;
Socket socket = so.workSocket;
byte
[] presixBytes =
new
byte
[prefixSize];
int
presix = 0;
Buffer.BlockCopy(so.buffer,0,presixBytes,0,prefixSize);
Array.Reverse(presixBytes);
presix = BitConverter.ToInt32(presixBytes,0);
if
(presix <= 0){
return
;
}
byte
[] datas =
new
byte
[presix];
Buffer.BlockCopy(so.buffer,prefixSize,datas,0,datas.Length);
String str = Encoding.UTF8.GetString(datas);
Debug.Log(
"received message :"
+str);
}
public
MemoryStream receiveData =
new
MemoryStream();
private
bool
isPresix =
true
;
public
int
curPrefix = 0;
//需要读取的数据总长度
public
void
receivedCallback(IAsyncResult ar){
try
{
StateObject so = (StateObject)ar.AsyncState;
Socket client = so.workSocket;
int
readSize = client.EndReceive (ar);
//结束读取,返回已读取的缓冲区里的字节数组长度
//将每次读取的数据,写入内存流里
receiveData.Write(so.buffer,0,readSize);
receiveData.Position = 0;
//读取前置长度,只读取一次
if
((
int
)receiveData.Length >= prefixSize && isPresix){
byte
[] presixBytes =
new
byte
[prefixSize];
receiveData.Read(presixBytes,0,prefixSize);
Array.Reverse(presixBytes);
curPrefix = BitConverter.ToInt32(presixBytes,0);
isPresix =
false
;
}
if
(receiveData.Length - (
long
)prefixSize < (
long
)curPrefix){
//如果数据没有读取完毕,调整Position到最后,接着读取。
receiveData.Position = receiveData.Length;
}
else
{
//如果内存流中的实际数字总长度符合要求,则说明数据已经全部读取完毕。
//将position位置调整到第4个节点,开始准备读取数据。
receiveData.Position = prefixSize;
//读取数据
byte
[] datas =
new
byte
[curPrefix];
receiveData.Read(datas,0,datas.Length);
//有压缩的话需要先解压,然后在操作。
byte
[] finallyBytes = decompress(datas);
String str = Encoding.UTF8.GetString(finallyBytes);
Debug.Log(
"the finally message is : "
+str);
}
//重复读取,每次读取1024个字节数据
client.BeginReceive(so.buffer,0,StateObject.BufferSize,0,
new
AsyncCallback(receivedCallback), so);
}
catch
(Exception e){
Console.WriteLine(e.ToString());
}
}
private
byte
[] temp =
new
byte
[1024];
//解压
public
byte
[] decompress(
byte
[] bytes){
MemoryStream memory =
new
MemoryStream ();
ICSharpCode.SharpZipLib.Zip.Compression.Inflater inf =
new
ICSharpCode.SharpZipLib.Zip.Compression.Inflater ();
inf.SetInput (bytes);
while
(!inf.IsFinished) {
int
extracted = inf.Inflate (temp);
if
(extracted > 0) {
memory.Write (temp, 0, extracted);
}
else
{
break
;
}
}
return
memory.ToArray ();
}
}
- C# client 与java netty 服务端的简单通信,客户端采用Unity。
- C# client 与java netty 服务端的简单通信,客户端采用Unity。
- Java Socket客户端与C#服务端的通信
- 简单的服务端与客户端通信代码
- Netty实现客户端和服务端的通信
- JAVA与C#的TCP通信——JAVA客户端,C#线程池服务端
- 简单的客户端,服务端通信
- C# SOCKET编写的简单聊天通信程序(客户端+服务端)
- Java Socket简单实现客户端与服务端通信
- 简单的unity 客户端与服务端互相发送消息
- Android作为客户端,采用Netty与服务器通信
- Netty简单示例----客户端与服务器通信
- 使用简单的ServiceSockt实现服务端与客户端的通信
- Java中利用socket实现简单的服务端与客户端的通信(入门级)
- Java中利用socket实现简单的服务端与客户端的通信(基础级)
- C#客户端和java服务端通信的例子
- netty 简单服务端和客户端
- Java简单实现UDP服务端和客户端的通信
- 【二叉树】字典树
- httpclient 4.3中、https请求错误的解决办法
- 针对于打包签名错误的解决办法
- 超可爱的 Loading View
- 4727: [POI2017]Turysta
- C# client 与java netty 服务端的简单通信,客户端采用Unity。
- Java中IO流,输入输出流概述与总结
- 交叉熵代价函数(损失函数)及其求导推导
- jquery 显示隐藏字段及字段可读写控制
- ASP.NET Core(一)【介绍】
- SQLServer将数据导出为SQL脚本的方法
- Kotlin的简单使用练习
- 欢迎使用CSDN-markdown编辑器
- MFC中根据注册表获取串口