服务器端
NetManager类
internal class NetManager : Singleton<NetManager>
{
Socket socket;
public List<Client> allcli=new List<Client>();
public void Init()
{
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Bind(new IPEndPoint(IPAddress.Any,12345));
socket.Listen(1000);
Console.WriteLine("服务器开启");
socket.BeginAccept(AsyAccept, null);
}
private void AsyAccept(IAsyncResult ar)
{
//被连接后的逻辑
try
{
Socket socket_cli = socket.EndAccept(ar);//获取连接的客户端
IPEndPoint ipend = socket_cli.RemoteEndPoint as IPEndPoint;//获取连接的客户端的ip地址和客户端
//创建一个用户类(里面有一个客户端和一个缓存消息的容器)用上面获取到的客户端给类里的客户端赋值
Client client = new Client();
client.socket = socket_cli;
//添加到用户的集合中(服务器可以被多个客户连接 所以需要集合来区分)
allcli.Add(client);
Console.WriteLine("IP为:{0}的用户{1}已经连接",ipend.Address,ipend.Port);
//在存储刚刚连接的客户端数据后 再次开启连接
socket.BeginAccept(AsyAccept, null);
//开始接收消息
socket_cli.BeginReceive(client.data,0,client.data.Length, SocketFlags.None,AsyReceive, client);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
private void AsyReceive(IAsyncResult ar)
{
try
{
//获取一个用户定义的对象(在接收消息的步骤中传过来的参数)
Client client = ar.AsyncState as Client;
//获取到 接收的对象中消息的真是长度
int len = client.socket.EndReceive(ar);
//如果该长度大于0(有内容)
if(len> 0)
{
//开辟一个真是长度的空间(用户类中缓存消息的空间为1024)在一步舍去后面不需要的空间(获取长度后不需要在开辟那么多了)
byte[] rdata = new byte[len];
//从用户类中缓存的数据的第0位 给刚刚开辟的真是长度字节数组 从第0位开始赋值 赋值的长度为真实长度
Buffer.BlockCopy(client.data, 0, rdata, 0, len);
//循环判断 如果消息的真实长度大于4
while (rdata.Length>4)
{
//从真实长度的 第0位开始 获取四个字节的值转为int(在客户端中包头的值是消息号(int)+消息内容长度)(客户端发的 包头+消息号+消息内容 在这里舍掉包头的4个字节)
//从参数1的第参数2位开始获取四个字节的值转为int
int bodylen = BitConverter.ToInt32(rdata, 0);
//创建一个剩余长度(消息号+消息长度)的字节数组
byte[] bodydata = new byte[bodylen];
//从真实长度的字节数组的第4位开始给剩余长度的字节数组从第0位开始赋值 赋值长度为剩余长度
Buffer.BlockCopy(rdata, 4, bodydata, 0, bodylen);
//从剩余长度的第四位开始 同上 获取消息号
int msgid = BitConverter.ToInt32(bodydata, 0);
//长度再减4 就是消息内容
byte[] infodata = new byte[bodydata.Length - 4];
//从消息号+消息内容的字节数组的第四位开始给长度再减4的字节数组从第0位开始赋值 赋值长度为长度再-4
Buffer.BlockCopy(bodydata, 4, infodata, 0, infodata.Length);
//new 一个消息类型 把发消息的对象 和 消息内容存下来
MsgData msgData = new MsgData();
msgData.Cli = client;
msgData.data = infodata;
//发消息传数据
MesManager<MsgData>.Ins.OnBroadCast(msgid, msgData);
//剩余长度(如果多条消息)真实长度-4(包头字节数)减去掉包头的单条消息剩余长度(消息号+消息内容的长度)
int sylen = rdata.Length - 4 - bodylen;
//赋值
byte[] sydata = new byte[sylen];
Buffer.BlockCopy(rdata, 4 + bodylen, sydata, 0, sylen);
//赋值
rdata = sydata;
}
}
//在收消息
client.socket.BeginReceive(client.data, 0, client.data.Length, SocketFlags.None, AsyReceive, client);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
public void OnSendCli(int id, byte[] data, Client cli)
{
int bodylen = 4 + data.Length;
byte[] enddata = new byte[0];
enddata = enddata.Concat(BitConverter.GetBytes(bodylen)).Concat(BitConverter.GetBytes(id)).Concat(data).ToArray();
cli.socket.BeginSend(enddata, 0, enddata.Length, SocketFlags.None, AsySend, cli);
}
private void AsySend(IAsyncResult ar)
{
try
{
Client client = ar.AsyncState as Client;
int len = client.socket.EndSend(ar);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}
Client类
internal class Client
{
public Socket socket;
public byte[] data=new byte[1024];
}
Define类 消息号类(要和客户端消息号类一致)
internal class Define
{
public const int C2S_LOGIN = 1000;
public const int S2C_LOGINCALL = 1001;
public const int C2S_REGISTER = 1002;
public const int S2C_REGISTERCALL = 1003;
}
MsgData类
internal class MsgData
{
public Client Cli = new Client();
public byte[] data;
}
消息中心
internal class MesManager<T>:Singleton<MesManager<T>>
{
Dictionary<int, Action<T>> mesdic = new Dictionary<int, Action<T>>();
public void OnAddListen(int id, Action<T> action)
{
if (mesdic.ContainsKey(id))
{
mesdic[id] += action;
}
else
{
mesdic.Add(id, action);
}
}
public void OnBroadCast(int id, T t)
{
if (mesdic.ContainsKey(id))
{
mesdic[id](t);
}
}
}
单例
internal class Singleton<T> where T : class, new()
{
private static T ins;
public static T Ins
{
get
{
if (ins == null)
{
ins = new T();
}
return ins;
}
}
}
客户端Unity
消息中心
public class MesManager<T> : Singleton<MesManager<T>>
{
Dictionary<int, Action<T>> mesdic = new Dictionary<int, Action<T>>();
public void OnAddListen(int id, Action<T> action)
{
if (mesdic.ContainsKey(id))
{
mesdic[id] += action;
}
else
{
mesdic.Add(id, action);
}
}
public void OnBroadCast(int id, T t)
{
if (mesdic.ContainsKey(id))
{
mesdic[id](t);
}
}
}
单例
public class Singleton<T> where T : class, new()
{
private static T ins;
public static T Ins
{
get
{
if (ins == null)
{
ins = new T();
}
return ins;
}
}
}
NetManager类 基本逻辑和服务器NetManager一样
public class NetManager : Singleton<NetManager>
{
Socket socket;
byte[] data = new byte[1024];
Queue<byte[]> queue = new Queue<byte[]>();
public void Init()
{
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.BeginConnect("127.0.0.1", 12345, www, null);
}
private void www(IAsyncResult ar)
{
try
{
Debug.Log("已连接");
socket.EndConnect(ar);
socket.BeginReceive(data, 0, data.Length, SocketFlags.None, AsyReceive, null);
}
catch (Exception ex)
{
Debug.LogError(ex);
}
}
private void AsyReceive(IAsyncResult ar)
{
try
{
int len = socket.EndReceive(ar);
if (len > 0)
{
byte[] rdata = new byte[len];
Buffer.BlockCopy(data, 0, rdata, 0, len);
while (rdata.Length > 4)
{
int bodylen = BitConverter.ToInt32(rdata, 0);
byte[] bodydata = new byte[bodylen];
Buffer.BlockCopy(rdata, 4, bodydata, 0, bodylen);
queue.Enqueue(bodydata);
int sylen = rdata.Length - 4 - bodylen;
byte[] sydata = new byte[sylen];
Buffer.BlockCopy(rdata, 4 + bodylen, sydata, 0, sylen);
rdata = sydata;
}
}
socket.BeginReceive(data, 0, data.Length, SocketFlags.None, AsyReceive, null);
}
catch (Exception ex)
{
Debug.LogError(ex);
}
}
public void OnSendCli(int id, byte[] data)
{
int bodylen = 4 + data.Length;
byte[] enddata = new byte[0];
enddata = enddata.Concat(BitConverter.GetBytes(bodylen)).Concat(BitConverter.GetBytes(id)).Concat(data).ToArray();
socket.BeginSend(enddata, 0, enddata.Length, SocketFlags.None, AsySend, null);
}
private void AsySend(IAsyncResult ar)
{
try
{
int len = socket.EndSend(ar);
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
public void NetupData()
{
if (queue.Count > 0)
{
byte[] data = queue.Dequeue();
int msgid = BitConverter.ToInt32(data, 0);
byte[] infodata = new byte[data.Length - 4];
Buffer.BlockCopy(data, 4, infodata, 0, infodata.Length);
MesManager<byte[]>.Ins.OnBroadCast(msgid, infodata);
}
}
}
GameManager类
public class GameManager : MonoBehaviour
{
private void Awake()
{
MesManager<byte[]>.Ins.OnAddListen(2, www);
}
private void www(byte[] obj)
{
string str = UTF8Encoding.UTF8.GetString(obj);
Debug.Log(str);
}
// Start is called before the first frame update
void Start()
{
NetManager.Ins.Init();
}
// Update is called once per frame
void Update()
{
NetManager.Ins.NetupData();
}
}
消息号类Define (要和服务器端消息号类一致)
public class Define
{
public const int C2S_LOGIN = 1000;
public const int S2C_LOGINCALL = 1001;
public const int C2S_REGISTER = 1002;
public const int S2C_REGISTERCALL = 1003;
}
简易登录注册
客户端
//登录面板
public class LoginPrent : MonoBehaviour
{
public Button login, register;
public InputField account, password;
public GameObject registerPrent;
private void Awake()
{
MesManager<byte[]>.Ins.OnAddListen(Define.S2C_LOGINCALL, deng);
}
private void deng(byte[] obj)
{
string str=UTF8Encoding.UTF8.GetString(obj);
Debug.Log(str);
if(str=="登陆成功")
{
gameObject.SetActive(false);
}
}
// Start is called before the first frame update
void Start()
{
register.onClick.AddListener(() =>
{
gameObject.SetActive(false);
registerPrent.SetActive(true);
});
login.onClick.AddListener(() =>
{
string str=account.text+"|"+password.text;
NetManager.Ins.OnSendCli(Define.C2S_LOGIN,UTF8Encoding.UTF8.GetBytes(str));
});
}
// Update is called once per frame
void Update()
{
}
}
//注册面板
public class RegisterPrent : MonoBehaviour
{
public Button register, returnbut;
public InputField account, password;
public GameObject loginPrent;
private void Awake()
{
gameObject.SetActive(false);
MesManager<byte[]>.Ins.OnAddListen(Define.S2C_REGISTERCALL, Zhus);
}
private void Zhus(byte[] obj)
{
string str=UTF8Encoding.UTF8.GetString(obj);
Debug.Log(str);
}
// Start is called before the first frame update
void Start()
{
returnbut.onClick.AddListener(() =>
{
gameObject.SetActive(false);
loginPrent.SetActive(true);
});
register.onClick.AddListener(() =>
{
string str=account.text+"|"+password.text;
NetManager.Ins.OnSendCli(Define.C2S_REGISTER,UTF8Encoding.UTF8.GetBytes(str));
});
}
// Update is called once per frame
void Update()
{
}
}
服务器端
简易账号密码 字典存储
internal class Logindic:Singleton<Logindic>
{
public Dictionary<string,string> dic=new Dictionary<string, string> ();
public void Init()
{
dic.Add("111", "111");
dic.Add("222", "222");
MesManager<MsgData>.Ins.OnAddListen(Define.C2S_REGISTER, Zhuce);
MesManager<MsgData>.Ins.OnAddListen(Define.C2S_LOGIN, Deng);
}
private void Deng(MsgData data)
{
string str = UTF8Encoding.UTF8.GetString(data.data);
string[] arr=str.Split('|');
if (dic.ContainsKey(arr[0]))
{
if (dic[arr[0]] == arr[1])
{
NetManager.Ins.OnSendCli(Define.S2C_LOGINCALL, UTF8Encoding.UTF8.GetBytes("登陆成功"),data.Cli);
}
else
{
NetManager.Ins.OnSendCli(Define.S2C_LOGINCALL, UTF8Encoding.UTF8.GetBytes("密码错误"), data.Cli);
}
}
else
{
NetManager.Ins.OnSendCli(Define.S2C_LOGINCALL, UTF8Encoding.UTF8.GetBytes("没有该账号"), data.Cli);
}
}
private void Zhuce(MsgData data)
{
string str = UTF8Encoding.UTF8.GetString(data.data);
string[] arr=str.Split('|');
if (dic.ContainsKey(arr[0]))
{
NetManager.Ins.OnSendCli(Define.S2C_REGISTERCALL, UTF8Encoding.UTF8.GetBytes("已有账号,注册失败"),data.Cli);
}
else
{
dic.Add(arr[0], arr[1]);
NetManager.Ins.OnSendCli(Define.S2C_REGISTERCALL, UTF8Encoding.UTF8.GetBytes("注册成功"), data.Cli);
}
}
}
//服务器端 Program
internal class Program
{
static void Main(string[] args)
{
Logindic.Ins.Init();
NetManager.Ins.Init();
Console.ReadKey();
}
}