基于Socket的游戏服务器通信框架的设计与实现
来源:互联网 发布:js手机号正则表达式 编辑:程序博客网 时间:2024/04/30 23:07
博客地址:blog.liujunliang.com.cn
开发工具:VS2017、Unity2017
本文介绍使用Socket/TCP来开发客户端与服务器端通信框架
博主使用过PhotonServer,由于其简单使用,所以本文模仿PhotonServer服务器框架来编写的
其中可以参考博主之前写的文章Unity3d与PhotonServer通信、Unity3d Socket网络编程
接下来介绍自己编写的一个基于Socket的游戏服务器通信框架的设计与实现
服务器端
客户端的连接请求与每个客户端的数据接收是通过线程来处理
using LJLNet.Application;using System;using System.Collections.Generic;using System.Linq;using System.Net;using System.Net.Sockets;using System.Text;using System.Threading.Tasks;using LJLNet.Thread;namespace LJLNet{ class Program { private static LJLNet.Application.Application application; static void Main(string[] args) { //主类 主入口类(可配置) application = new GameContext(); application.Setup(); //绑定监听消息IP和端口号(可配置ip地址和端口号) IPAddress ip = IPAddress.Parse("127.0.0.1"); EndPoint endPoint = new IPEndPoint(ip, 6000); //创建一个socket对象 //寻址方式 套接字类型 协议方式 Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); serverSocket.Bind(endPoint);//向操作系统申请一个ip和端口号 Console.WriteLine("服务器端启动完成"); //开始监听客户端的连接请求 serverSocket.Listen(100);//最多可以接收100个客户端请求 //开启线程 来接收客户端请求 BaseThread thread = new AcceptThread(application, serverSocket); thread.Start(); //断开连接 } }}
主入口类(框架的启动类),框架中的唯一个入口类
using System;using System.Collections.Generic;using System.Linq;using System.Net.Sockets;using System.Text;using System.Threading.Tasks;using LJLNet.SocketTCPPeer;namespace LJLNet.Application{ /// <summary> /// 该类是主类 框架主入口(如果需要修改主入口函数 需要在Program类中修改) /// </summary> public class GameContext : Application { /// <summary> /// 客户端接入函数 /// </summary> /// <param name="socket"></param> /// <returns></returns> public BasePeer CreatePeer(Socket socket) { BasePeer peer = new ClientPeer(socket); ServerMgr.GetInstance.peerList.Add(peer); return peer; } /// <summary> /// 框架启动函数 /// </summary> public void Setup() { Console.WriteLine("启动框架"); } /// <summary> /// 框架取消函数 /// </summary> public void TearDown() { Console.WriteLine("停止框架"); } }}
当我们的一个客户端连接进服务器时候,便创建一个ClientPeer对象(客户端)
客户端支持数据的接收与响应、事件
其基类如下
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Net;using System.Net.Sockets;using System.Threading.Tasks;using LJLNet.Thread;using LJLCommon.Helper;using LJLCommon.Package;namespace LJLNet.SocketTCPPeer{ public abstract class BasePeer { public Socket mSocket { get; set; } public BasePeer(Socket socket) { mSocket = socket; //启动socket InitSocket(); } public abstract void OnDisconnect(); public abstract void OnOperationRequest(Dictionary<byte, object> parameters); protected void InitSocket() { //开启线程 BaseThread thread = new ReceiveThread(this); thread.Start(); } public void OnOperationResponse(Dictionary<byte, object> parameters) { //向客户端发送消息 //序列化 string message = DictToPackageHelper.Serializa(parameters); Package package = new Package() { type = PackageType.Response, parameters = message }; string packageMessage = XMLHelper.Serialze<Package>(package); //字节转化 var date = ASCIIEncoding.UTF8.GetBytes(packageMessage); mSocket.Send(date); } public void OnOperationEvent(Dictionary<byte, object> dict) { //向客户端发送消息 //序列化 string message = DictToPackageHelper.Serializa(dict); Package package = new Package() { type = PackageType.Event, parameters = message }; message = XMLHelper.Serialze<Package>(package); //字节转化 var date = ASCIIEncoding.UTF8.GetBytes(message); mSocket.Send(date); } }}
ClientPeer.cs
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Net.Sockets;using System.Threading.Tasks;namespace LJLNet.SocketTCPPeer{ public class ClientPeer : BasePeer { public ClientPeer(Socket socket) : base(socket) { } public override void OnDisconnect() { } public override void OnOperationRequest(Dictionary<byte, object> parameters) { Console.WriteLine(parameters.FirstOrDefault(q=>q.Key==1).Value.ToString()); OnOperationResponse(new Dictionary<byte, object>() { { 0, "你好" } }); } }}
在客户端中开辟一个线程,用于接收数据请求
线程基类
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading;namespace LJLNet.Thread{ public abstract class BaseThread { protected System.Threading.Thread mThread; public BaseThread() { mThread = new System.Threading.Thread(Run); } public virtual void Start() { mThread.Start(); } protected abstract void Run(); public void Stop() { mThread.Abort(); } }}
接收数据线程
using LJLNet.SocketTCPPeer;using System;using System.Collections.Generic;using System.Linq;using System.Text;using LJLCommon.Helper;using System.Threading.Tasks;namespace LJLNet.Thread{ public class ReceiveThread : BaseThread { private BasePeer mPeer; public ReceiveThread(BasePeer peer) { mPeer = peer; } //运行的内容 protected override void Run() { if (mPeer.mSocket != null) { while (true) { try { //从客户端接收消息 byte[] buffer = new byte[1024];//设置一个消息接收缓冲区 mPeer.mSocket.Receive(buffer);//该状态处于一个暂停状态,知道接收到消息,并返回字节数 Dictionary<byte, object> parameters = DictToPackageHelper.DeSerializa(ASCIIEncoding.UTF8.GetString(buffer)); mPeer.OnOperationRequest(parameters); } catch { Console.WriteLine("一个客户端断开连接"); //断开线程 this.Stop(); //将该客户端移除 mPeer.OnDisconnect(); ServerMgr.GetInstance.peerList.Remove(mPeer); mPeer = null; } } } } }}
客户端
使用Unity3d作为游戏客户端
当连接服务器时创建一个SocketTCPPeer对象(客户端),客户端中主要处理数据的发送和响应与事件的接收
using System.Collections;using System.Collections.Generic;using UnityEngine;using System.Text;using System.Threading;using System.Net;using System.Net.Sockets;using LJLCommon.Helper;namespace LJLNet{ public class SocketTCPPeer { private BaseThread mBaseThread; public ISocketListener mMono; public Socket mTcpSocket; public SocketTCPPeer(ISocketListener mono) { //创建socket mTcpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); mMono = mono; } //连接服务器 public void Connect(string ip, int point) { if (mTcpSocket != null) { mTcpSocket.Connect(IPAddress.Parse(ip), point); if (mTcpSocket.Connected) { mBaseThread = new ReceiveThread(this); mBaseThread.Start(); } } } //发送数据 public void OnOperationRequest(byte operationType, Dictionary<byte, object> dataDict) { if (mTcpSocket != null && mTcpSocket.Connected) { string parameters = null; try { parameters = DictToPackageHelper.Serializa(dataDict); } catch { Debug.LogError("字典容器序列化失败"); } if (!string.IsNullOrEmpty(parameters)) { mTcpSocket.Send(ASCIIEncoding.UTF8.GetBytes(parameters)); } } else { Debug.Log("与服务器断开连接"); } } //断开连接 public void Disconnect() { if (mBaseThread != null) { mBaseThread.Stop(); } if (mTcpSocket != null) { mTcpSocket.Close(); } } }}
其中在客户端中开辟一个线程用于对数据的接收(与服务器端类似)
线程基类
using System.Collections;using System.Collections.Generic;using UnityEngine;using System.Threading;using System.Net.Sockets;public abstract class BaseThread{ protected Thread mThread; public BaseThread() { mThread = new Thread(Run); } public virtual void Start() { mThread.Start(); } public abstract void Run(); public void Stop() { mThread.Abort(); }}
接收响应线程类
using System;using System.Collections;using System.Collections.Generic;using UnityEngine;using System.Net;using System.Net.Sockets;using LJLNet;using LJLCommon.Package;using LJLCommon.Helper;using System.Text;public class ReceiveThread : BaseThread{ private SocketTCPPeer mSocketTCPPeer; public ReceiveThread(SocketTCPPeer socketTCPPeer) { mSocketTCPPeer = socketTCPPeer; } public override void Run() { if ( mThread != null && mSocketTCPPeer.mTcpSocket != null&& mSocketTCPPeer.mTcpSocket.Connected ) { try { //socket接收消息 byte[] bt = new byte[1024]; mSocketTCPPeer.mTcpSocket.Receive(bt); Debug.Log("接收响应"); //对数据进行处理 Package package = XMLHelper.Deserialze<Package>(ASCIIEncoding.UTF8.GetString(bt)); Dictionary<byte, object> parameters = DictToPackageHelper.DeSerializa(package.parameters); //回调给主类处理 switch (package.type) { case PackageType.Response: mSocketTCPPeer.mMono.OnOperationResponse(parameters); break; case PackageType.Event: mSocketTCPPeer.mMono.OnOperationEvent(parameters); break; default: break; } } catch { //断开线程 Stop(); //断开连接 Debug.Log("与服务器断开连接"); mSocketTCPPeer.Disconnect(); } } }}
启动客户端-------创建一个Monobehavior脚本挂载到Camera上
在客户端启动时候创建SocketTCPPeer与服务器端进行连接并发送数据
using System.Collections;using System.Collections.Generic;using UnityEngine;using LJLNet;using System.Text;using System;public class GameContext : MonoBehaviour, ISocketListener{ public SocketTCPPeer peer { get; set; } private void Start() { peer = new SocketTCPPeer(this); peer.Connect("127.0.0.1", 6000); peer.OnOperationRequest(1, new Dictionary<byte, object>() { { 1, "你好" } }); } private void OnDestroy() { if (peer != null) { peer.Disconnect(); } } public void OnOperationResponse(Dictionary<byte, object> paratemers) { Debug.Log(paratemers[0].ToString()); } public void OnOperationEvent(Dictionary<byte, object> paratemers) { }}
其中主类需要实现ISocketListener接口
将该接口传入到客户端SocketTCPPeer类中,当处理数据的响应与事件时候调用
方便框架的管理与简单使用
using System.Collections;using System.Collections.Generic;using UnityEngine;namespace LJLNet{ public interface ISocketListener { SocketTCPPeer peer { get; set; } void OnOperationResponse(Dictionary<byte,object> paratemers); void OnOperationEvent(Dictionary<byte, object> paratemers); }}
检测
运行服务器端与客户端
日志显示如下
该框架支持多人在线游戏的开发
可以参考博主编写的PhotonServer服务器MMO多人在线游戏开发一文
博客地址:blog.liujunliang.com.cn
阅读全文
0 0
- 基于Socket的游戏服务器通信框架的设计与实现
- 基于MPC860T的嵌入式通信服务器的设计与实现
- Socket+NIO实现客户端与服务器的通信的Demo
- 基于Mina框架的 socket网络通信
- 实现QT与Flex、Flash的通信(基于Socket)
- 基于Boost.Asio的异步通信服务器设计与开发
- C# 使用Socket实现服务器与客户端的通信
- Socket实现Android客户端与服务器的通信
- Android socket与服务器通信及心跳连接的实现
- 一个非常完善的基于Socket的多服务器通信框架
- 游戏服务器端通信框架(C++与Socket)
- 基于cocos2d-x的游戏框架设计
- 基于cocos2d-x的游戏框架设计
- 基于cocos2d-x的游戏框架设计
- 基于HID 规范的六轴体感游戏手柄设计与实现
- 基于LDAP 的Web 邮件服务器的设计与实现
- 基于ARM的嵌入式web服务器的设计与实现
- android端基于socket的局域网内服务器与客户端加密通信
- 对于java 容器的理解
- 概念理解:外包、众包
- 图
- java基础7:io流对象之转换流
- Druid(准)实时分析统计数据库——列存储+高效压缩
- 基于Socket的游戏服务器通信框架的设计与实现
- php版本历史
- Climbing Stairs:步长1或2到达终点
- ArcGIS Silverlight API访问天地图服
- javascriptDom
- OpenCV三维图像的创建和数据遍历
- 【第1108期】小白谈数据脱敏
- 利用XE7的OmniXML完成XML文件的处理,支持跨平台
- Linux学习笔记-Vim常用命令