介绍
前后端均采用C# 降低学习成本
代码依赖 Newtonsoft.Json, 您可以通过Nuget获取或者去github下载最新版
服务端
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using Newtonsoft.Json;
public class TCPServers
{
// 各阶段处理回调
public delegate bool ReceiveAction(Socket arg0, string arg1);
protected static Action<Socket> _linkHandler;
protected static ReceiveAction _receiveHandler;
protected static Action<Socket> _clientOutHandler;
protected static Action<Socket> _illegalityHandler;
/// <summary>
///
/// </summary>
/// <param name="configFile"> 服务器配置地址 </param>
/// <param name="newLinkHandler"> 新客户端连接处理方法 </param>
/// <param name="receiveHandler"> 客户端消息处理 </param>
/// <param name="clientOutHandler"> 客户端掉线处理 </param>
/// <param name="illegalityHandler"> 程序连接异常崩溃处理 </param>
public static void Listen(string configFile, Action<Socket> newLinkHandler, ReceiveAction receiveHandler, Action<Socket> clientOutHandler, Action<Socket> illegalityHandler)
{
//读取配置
string ip = "0.0.0.0";
int port = 5566;
int backlog = 5000;
Encoding encoding = Encoding.UTF8;
try
{
var jsonstr = File.ReadAllText(configFile);
var conf = JsonConvert.DeserializeObject<JsonData>(jsonstr);
ip = conf.GetStr("ip");
port = conf.GetInt("port");
backlog = conf.GetInt("max-client-count");
encoding = Encoding.GetEncoding(conf.GetInt("Encoding-Code"));
}
catch (Exception e)
{
Console.WriteLine($"配置文件异常: {configFile}\n${e.Message}\n${e.StackTrace}");
}
//回调绑定
_linkHandler = newLinkHandler;
_receiveHandler = receiveHandler;
_clientOutHandler = clientOutHandler;
_illegalityHandler = illegalityHandler;
try
{
//初始化服务器
_netServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
var endPoint = new IPEndPoint(IPAddress.Parse(ip), port);
_netServer.Bind(endPoint);
_netServer.Listen(backlog);
Console.WriteLine("服务器已启动... ...");
}
catch (SocketException e)
{
Console.WriteLine($"套接字绑定异常: \n${e.Message}\n${e.StackTrace}");
}
catch (Exception e2)
{
Console.WriteLine($"程序异常: \n${e2.Message}\n${e2.StackTrace}");
}
var T1 = new Thread(() =>
{
while (true)
{
var newAccept = _netServer.Accept();//接收新的客户端接入
Console.WriteLine($"客户端 {newAccept.RemoteEndPoint.ToString()} 建立连接......");
_linkHandler?.Invoke(newAccept);//新客户端连接
var T2 = new Thread(() =>
{
byte[] buffer = new byte[1024 * 1024];
bool runState = true;
try
{
//循环监听客户端事件
while (runState)
{
int len = newAccept.Receive(buffer, 0, buffer.Length, SocketFlags.None);
if (len == 0)
{
break;
}
runState = _receiveHandler(newAccept, encoding.GetString(buffer, 0, len));
}
//断开连接
Console.WriteLine($"客户端 {newAccept.RemoteEndPoint.ToString()} 断开连接......");
_clientOutHandler?.Invoke(newAccept);
}
catch
{
//程序连接异常崩溃处理
Console.WriteLine($"客户端 {newAccept.RemoteEndPoint.ToString()} 程序连接异常......");
_illegalityHandler?.Invoke(newAccept);
}
});
//运行后台运行
T2.IsBackground = true;
T2.Start();
}
});
//运行后台运行
T1.IsBackground = true;
T1.Start();
}
#region private
/// <summary> 唯一套接字 </summary>
static Socket _netServer = null;
/// <summary> 共享Socket 不允许修改 </summary>
public static Socket shareSocketInstance { get { return _netServer; } }
/// <summary> 不允许用户自己创建 </summary>
private TCPServers() { }
#endregion
}
客户端
using System;
using System.Net.Sockets;
using UnityEngine;
using System.Collections.Generic;
using Newtonsoft.Json;
using System.Text;
public class TCPClient
{
private Socket init()
{
Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// 接收的消息数据包大小限制为 0x4000 byte, 即16KB
m_recvBuff = new byte[0x4000];
m_recvCb = new AsyncCallback(RecvCallBack);
return clientSocket;
}
/// <summary>
/// 连接服务器
/// </summary>
/// <param name="host">ip地址</param>
/// <param name="port">端口号</param>
/// <returns> 是否连接成功 </returns>
public bool Connect(string host, int port)
{
if (m_socket == null)
m_socket = init();
try
{
Debug.Log("connect: " + host + ":" + port);
m_socket.SendTimeout = 3;
m_socket.Connect(host, port);
connected = true;
}
catch (Exception ex)
{
Debug.LogError(ex);
connected = false;
}
return connected;
}
/// <summary>
/// 发送消息
/// </summary>
public void SendData(byte[] bytes)
{
NetworkStream netstream = new NetworkStream(m_socket);
netstream.Write(bytes, 0, bytes.Length);
}
/// <summary>
/// 发送消息
/// </summary>
/// <param name="data"></param>
public void SendData(ProtocolEvent e, JsonData data)
{
data["Event"] = e;
string pkt = JsonConvert.SerializeObject(data);
SendData(Encoding.UTF8.GetBytes(pkt));
}
/// <summary>
/// 尝试接收消息(每帧调用)
/// </summary>
public void BeginReceive()
{
m_socket.BeginReceive(m_recvBuff, 0, m_recvBuff.Length, SocketFlags.None, m_recvCb, this);
}
/// <summary>
/// 当收到服务器的消息时会回调这个函数
/// </summary>
private void RecvCallBack(IAsyncResult ar)
{
var len = m_socket.EndReceive(ar);
byte[] msg = new byte[len];
Array.Copy(m_recvBuff, msg, len);
var msgStr = Encoding.UTF8.GetString(msg);
// 将消息塞入队列中
m_msgQueue.Enqueue(msgStr);
// 将buffer清零
for (int i = 0; i < m_recvBuff.Length; ++i)
{
m_recvBuff[i] = 0;
}
}
/// <summary>
/// 从消息队列中取出消息
/// </summary>
/// <returns></returns>
public string GetMsgFromQueue()
{
if (m_msgQueue.Count > 0)
return m_msgQueue.Dequeue();
return null;
}
/// <summary>
/// 关闭Socket
/// </summary>
public void CloseSocket()
{
Debug.Log("close socket");
try
{
m_socket.Shutdown(SocketShutdown.Both);
m_socket.Close();
}
catch (Exception e)
{
//Debug.LogError(e);
}
finally
{
m_socket = null;
connected = false;
}
}
public bool connected = false;
private byte[] m_recvBuff;
private AsyncCallback m_recvCb;
private Queue<string> m_msgQueue = new Queue<string>();
private Socket m_socket;
}
JsonData
using System;
using System.Collections;
using System.Collections.Generic;
/// <summary>
/// JsonData 对象
///
/// example:
///
/// //JsonData对象转json字符串
/// var t = new JsonData();
/// t["name"] = "geek7";
/// t["age"] = 24;
/// t["isMan"] = true;
/// var jsonstr = JsonConvert.SerializeObject(t);
/// //输出
/// Debug.Log($"jsonString:\n{jsonstr}");
///
///
/// //json字符串转JsonData对象
/// var obj = JsonConvert.DeserializeObject<JsonData>(jsonstr);
/// //输出
/// Debug.Log($"jsonData:\nname: {obj.GetStr("name")}\tage: {obj.GetInt("age")}\tsex: {(obj.GetBoolean("isMan") ? "man" : "woman")}");
///
/// </summary>
[Serializable]
public class JsonData : Dictionary<string, object>
{
/// <summary>
/// 获取一个整形值
/// </summary>
/// <param name="k"></param>
/// <returns> value || default(int) </returns>
public int GetInt(string k)
{
object v;
if (TryGetValue(k, out v))
{
return Convert.ToInt32(v);
}
return default(int);
}
/// <summary>
/// 获取一个字符串值
/// </summary>
/// <param name="k"></param>
/// <returns> value || string.Empty</returns>
public string GetStr(string k)
{
object v;
if (TryGetValue(k, out v))
{
return v.ToString();
}
return string.Empty;
}
/// <summary>
/// 获取一个float值
/// </summary>
/// <param name="k"></param>
/// <returns> value || float.NaN</returns>
public float GetFloat(string k)
{
object v;
if (TryGetValue(k, out v))
{
return Convert.ToSingle(v);
}
return float.NaN;
}
/// <summary>
/// 获取一个bool值
/// </summary>
/// <param name="k"></param>
/// <returns> value || default(bool)</returns>
public bool GetBoolean(string k)
{
object v;
if (TryGetValue(k, out v))
{
return Convert.ToBoolean(v);
}
return default(bool);
}
/// <summary>
/// 获取一个Double值
/// </summary>
/// <param name="k"></param>
/// <returns> value || Double.NaN</returns>
public double GetDouble(string k)
{
object v;
if (TryGetValue(k, out v))
{
return Convert.ToDouble(v);
}
return Double.NaN;
}
}