在C# WINFORM 项目中使用HP-SOCKET网络引擎(客户端与服务器端“状态更新”的模型与实现之一)

在C# WINFORM 项目中使用HP-SOCKET网络引擎(客户端与服务器端“状态更新”的模型与实现之一)

Unity2D 客户端使用HP-SOCKET连接服务器(客户端与服务器端“状态更新”的模型与实现之二)

服务器端的对象更新(客户端与服务器端“状态更新”的模型与实现之三)

保持服务器与客户端的数据一致模型,能查到的方式只有2种:帧同步和状态同步。

帧同步把计算都交给客户端毫无安全可言,只剩下全部由服务器处理的“状态同步”,我希望按“状态更新”模型做一个DEMO,

一步一步做出来,看看服务器世界中有成百上千个对象的状态同步到客户端后,“状态更新”模型能处理的对象上限和问题。

本文其实也是在介绍如何使用HP-SOCKET网络引擎。

开发环境:VS2019,HP-SOCKET开源库

上次HP-SOCKET的掌门“伤神小怪兽”出现在重庆西站,可惜当时我没在重庆,没有遇见:)

HP-SOCKET开源库网络引擎已经为C#开发了专门的版本,

在VS2019 WINFORM项目中安装HP-SOCKET,只需在NuGet安装一下即可:

开源中国可以下载到HP-SOCKET .net相关的DEMO程序,比从github快了很多。

1、建一个WINFORM程序,在NuGet中安装HP-SOCKET .net。

2、加入头文件的引用:

using HPSocket;
using HPSocket.Tcp;
using HPSocket.Thread;
//using Models;
using Newtonsoft.Json;
using ThreadPool = HPSocket.Thread.ThreadPool;
using Timer = System.Timers.Timer;

3、定义一个服务器实例的全局变量,其它代码来源于原生HP-SOCKET.NET  DEMO,后面我会精简掉:

DEMO的代码中在处理服务器收到的数据用到了线程池,我后面应该不会用这个。

4、WINFORM的控件使用:

先放一个toolstrip_container,然后上面放工具条、下面放状态条、中间放splitContainer,左边放listbox,右面空,

工具条上加一个按钮,这些控件的DOCK布局都让它们FILL:

5、按DEMO在程序加载函数中,加入服务器实例的参数:

 private void Form1_Load(object sender, EventArgs e)
        {
            // 演示设置serer属性

            // 缓冲区大小应根据实际业务设置, 并发数和包大小都是考虑条件
            // 都是小包的情况下4K合适, 刚好是一个内存分页(在非托管内存, 相关知识查百度)
            // 大包比较多的情况下, 应根据并发数设置比较大但是又不会爆内存的值
            _server.SocketBufferSize = 4096; // 4K

            // pack模型专有设置
            _server.MaxPackSize = 4096;     // 最大封包
            _server.PackHeaderFlag = 0x01;  // 包头标识, 要与客户端对应, 否则无法通信

            // serer绑定地址和端口
            _server.Address = "0.0.0.0";
            _server.Port = 15555;

            // 演示绑定事件
            _server.OnPrepareListen += OnPrepareListen;
            _server.OnAccept += OnAccept;
            _server.OnReceive += OnReceive;
            _server.OnClose += OnClose;
            _server.OnShutdown += OnShutdown;


            // 线程池相关设置
            // 线程池回调函数
            _taskTaskProc = TaskTaskProc;

            // 定时输出线程池任务数
            _timer.Elapsed += (_, args) =>
            {
                if (_server.HasStarted && _threadPool.HasStarted)
                {
                    AddLog($"线程池当前在执行的任务数: {_threadPool.TaskCount}, 任务队列数: {_threadPool.QueueSize}");
                }
            };
            _timer.Start();
        }

6、工具条按钮执行服务器启动:

 private void Service_go()
        {
            try
            {             
                    // 2个线程处理耗时操作, 作为相对耗时的任务, 可根据业务需求多开线程处理
                    if (!_threadPool.Start(2, RejectedPolicy.WaitFor))
                    {
                
                        throw new Exception($"线程池启动失败, 错误码: {_threadPool.ErrorCode}");
                    }

                    // 启动服务
                    if (!_server.Start())
                    {
                        throw new Exception($"error code: {_server.ErrorCode}, error message: {_server.ErrorMessage}");
                    }                  

                    // 等待线程池停止
                  //  await _threadPool.WaitAsync();
                    // 等待服务停止
                  //  await _server.WaitAsync(); 
                    // 停止并等待线程池任务全部完成
                   // await _threadPool.StopAsync();
                    // 等待服务停止            
            }
            catch (Exception ex)
            {
                AddLog($"exception: {ex.Message}");
            }
        }

7、HP-SOCKET的服务器实例工作时,会在相应的响应函数出现调用

我们只需要在相应的响应函数处理我们要做的事情即可,当然服务器函数主要是在OnReceive接收客户端传来的数据:


        //服务器开始监听
        private HandleResult OnPrepareListen(IServer sender, IntPtr listen)
        {
            AddLog($"OnPrepareListen({sender.Address}:{sender.Port}), listen: {listen}, hp-socket version: {sender.Version}");

            return HandleResult.Ok;
        }

        //服务器接收到接入
        private HandleResult OnAccept(IServer sender, IntPtr connId, IntPtr client)
        {
            // 获取客户端地址
            if (!sender.GetRemoteAddress(connId, out var ip, out var port))
            {
                return HandleResult.Error;
            }

            AddLog($"OnAccept({connId}), ip: {ip}, port: {port}");

            return HandleResult.Ok;
        }

        //服务器收到数据
        private HandleResult OnReceive(IServer sender, IntPtr connId, byte[] data)
        {
            AddLog($"OnReceive({connId}), data length: {data.Length}");

            return OnProcessFullPacket(sender, connId, data);
        }

        //服务器关闭连接
        private HandleResult OnClose(IServer sender, IntPtr connId, SocketOperation socketOperation, int errorCode)
        {
            AddLog($"OnClose({connId}), socket operation: {socketOperation}, error code: {errorCode}");
            return HandleResult.Ok;
        }

        //服务器关闭
        private HandleResult OnShutdown(IServer sender)
        {
            AddLog($"OnShutdown({sender.Address}:{sender.Port})");

            return HandleResult.Ok;
        }

8、启动一下服务器试试这个环境,HP-SOCKET是否可用,OK没有问题:

发布了249 篇原创文章 · 获赞 85 · 访问量 49万+

猜你喜欢

转载自blog.csdn.net/ot512csdn/article/details/103951013