在C# WINFORM 项目中使用HP-SOCKET网络引擎(客户端与服务器端“状态更新”的模型与实现之一)
Unity2D 客户端使用HP-SOCKET连接服务器(客户端与服务器端“状态更新”的模型与实现之二)
服务器端的对象更新(客户端与服务器端“状态更新”的模型与实现之三)
HP-SOCKET的服务器已经启动,我们在WINFORM用HP-SOCKET client组件去连接:
1、使用了HP-SOCKET的client组件。
2、使用的PACK包模式,就是只接收整包的数据,不用再担心粘包掉包之类的。
3、发送的是:一个对象封包为json字符串再转为的一串byte。
bwobj one = new bwobj { bwtype = 1, longjson = "123456789123456789" };
var bytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(one));
HPclient.Send(bytes, bytes.Length);
4、服务器接收到的一串byte,解包为json字符串,再解为一个对象。
这些收到的对象被放在一个线程安全的ConcurrentBag对象集合里面,后面会安排其它线程来去走这些报文对象。
//data->string->object 解包
bwobj one = JsonConvert.DeserializeObject<bwobj>(Encoding.UTF8.GetString(data));
bws.Add(one);
client端的winform全部实现代码:
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using HPSocket;
using HPSocket.Tcp;
using HPSocket.Thread;
using Newtonsoft.Json;
namespace client2020
{
public partial class Form1 : Form
{
private readonly ITcpPackClient HPclient = new TcpPackClient();
public static ConcurrentBag<bwobj> bws = new ConcurrentBag<bwobj>(); //报文集合
public Form1()
{
InitializeComponent();
Control.CheckForIllegalCrossThreadCalls = false;
}
private void Form1_Load(object sender, EventArgs e)
{
// 演示设置client属性
// 缓冲区大小
HPclient.SocketBufferSize = 4096; // 4K
// 异步连接
HPclient.Async = true;
// pack模型专有设置
HPclient.MaxPackSize = 4096; // 最大封包
HPclient.PackHeaderFlag = 0x01; // 包头标识, 要与客户端对应, 否则无法通信
// 要连接的服务器地址和端口(也可以调用Connect()方法时传入服务器ip和端口)
// 例如: _client.Connect("127.0.0.1", 555)
HPclient.Address = "127.0.0.1";
HPclient.Port = 15555;
// 事件绑定
HPclient.OnPrepareConnect += OnPrepareConnect;
HPclient.OnConnect += OnConnect;
HPclient.OnReceive += OnReceive;
HPclient.OnClose += OnClose;
}
private void toolStripButton1_Click(object sender, EventArgs e)
{
client_go();
}
private void toolStripButton2_Click(object sender, EventArgs e)
{
//线程
Thread lsThread = new Thread(new ThreadStart(sendsomething));
lsThread.IsBackground = true;
lsThread.Start();
}
private void sendsomething()
{
bwobj one = new bwobj { bwtype = 1, longjson = "123456789123456789" };
var bytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(one));
HPclient.Send(bytes, bytes.Length);
}
private void client_go()
{
try
{
// 连接到目标服务器
if (!HPclient.Connect())
{
throw new Exception($"error code: {HPclient.ErrorCode}, error message: {HPclient.ErrorMessage}");
}
}
catch (Exception ex)
{
AddLog($"exception: {ex.Message}");
}
}
private HandleResult OnPrepareConnect(IClient sender, IntPtr socket)
{
AddLog($"OnPrepareConnect({sender.Address}:{sender.Port}), socket handle: {socket}, hp-socket version: {sender.Version}");
return HandleResult.Ok;
}
private HandleResult OnConnect(IClient sender)
{
AddLog("OnConnect()");
return HandleResult.Ok;
}
private HandleResult OnReceive(IClient sender, byte[] data)
{
AddLog($"OnReceive(), data length: {data.Length}");
return HandleResult.Ok;
}
private HandleResult OnClose(IClient sender, SocketOperation socketOperation, int errorCode)
{
AddLog($"OnClose(), socket operation: {socketOperation}, error code: {errorCode}");
return HandleResult.Ok;
}
private void AddLog(string log)
{
listBox1.Items.Add($"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {log}\r\n");
}
public class bwobj
{
public int bwtype { get; set; }
public string longjson { get; set; }
}
}
}
--------------unity客户端连接HP-SOCKET服务器-----------------------
后面测试“状态更新”的例子,可能会用到2D图像的展示,比如客户端控制一个小坦克对象,所以unity也要连上服务器。
unity2019中建一个项目,加入canvas桌布,加入一个button,canvas桌布和主摄像机关联,在game中设定屏幕分辨率。
button中新建一个脚本NewBehaviourScript.cs。
全部代码如下:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.Collections.Concurrent;
using System.Text;
using HPSocket;
using HPSocket.Tcp;
using HPSocket.Thread;
using Newtonsoft.Json;
public class NewBehaviourScript : MonoBehaviour
{
private readonly ITcpPackClient HPclient = new TcpPackClient();
// Start is called before the first frame update
void Start()
{
// 缓冲区大小
HPclient.SocketBufferSize = 4096; // 4K
// 异步连接
HPclient.Async = true;
// pack模型专有设置
HPclient.MaxPackSize = 4096; // 最大封包
HPclient.PackHeaderFlag = 0x01; // 包头标识, 要与客户端对应, 否则无法通信
// 要连接的服务器地址和端口(也可以调用Connect()方法时传入服务器ip和端口)
// 例如: _client.Connect("127.0.0.1", 555)
HPclient.Address = "127.0.0.1";
HPclient.Port = 15555;
// 事件绑定
// HPclient.OnPrepareConnect += OnPrepareConnect;
HPclient.OnConnect += OnConnect;
HPclient.OnReceive += OnReceive;
HPclient.OnClose += OnClose;
HPclient.Connect();
}
// Update is called once per frame
void Update()
{ //按下ESC键则退出游戏
if (Input.GetKeyDown(KeyCode.Escape))
{
HPclient.Dispose();
Application.Quit();
}
}
public void sendsomething()
{
bwobj one = new bwobj { bwtype = 1, longjson = "longlonglonglong" };
var bytes = System.Text.Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(one));
HPclient.Send(bytes, bytes.Length);
}
private HandleResult OnPrepareConnect(IClient sender, IntPtr socket)
{
AddLog($"OnPrepareConnect({sender.Address}:{sender.Port}), socket handle: {socket}, hp-socket version: {sender.Version}");
return HandleResult.Ok;
}
private HandleResult OnConnect(IClient sender)
{
AddLog("OnConnect()");
return HandleResult.Ok;
}
private HandleResult OnReceive(IClient sender, byte[] data)
{
AddLog($"OnReceive(), data length: {data.Length}");
return HandleResult.Ok;
}
private HandleResult OnClose(IClient sender, SocketOperation socketOperation, int errorCode)
{
AddLog($"OnClose(), socket operation: {socketOperation}, error code: {errorCode}");
return HandleResult.Ok;
}
private void AddLog(string log)
{
Debug.Log($"{log}\r\n");
}
public class bwobj
{
public int bwtype { get; set; }
public string longjson { get; set; }
}
}
注意点:
1、在assets目录中放入hpsocket和json的类库:
2、程序编译发布选择对应环境为x86_64模式:
unity客户端使用HP-SOCKET连接了服务器:
unity hpsocket json dll文件打包共享: