socket?同步
同步就是有阻塞,比如接收数据时如果没有数据就等待,等到有数据了再执行下一步代码。
socket?异步 异步就不用等待,比如指定接收数据后就会继续执行一下步代码,等到有数据了再跳出当前执行的代码转去执行异步调用。 同步的话通常要配合多线程(否则在阻塞的时候当前线程会停止,如果当前线程是UI线程就表现出好像界面停止的样子)。
左边是客户端?右边是服务器
?
服务器储存客户集合?客户类(服务器的)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace 飞机服务器
{
class Client
{
public Socket socket;
public byte[] data = new byte[1024];
public string ip;
public string nick;
}
}
服务器
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace 飞机服务器
{
class NetManager:Singleton<NetManager>
{
Socket socket;
public List<Client> clients = new List<Client>();
public void Init()
{
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
IPEndPoint ip = new IPEndPoint(IPAddress.Any, 8585);
socket.Bind(ip);
socket.Listen(100);
Debug.Log("服务器开启");
socket.BeginAccept(OnAccept, null);//异步连接
}
private void OnAccept(IAsyncResult ar)
{
Socket client = socket.EndAccept(ar);//异步接受传入的连接尝试
IPEndPoint iP = client.RemoteEndPoint as IPEndPoint;
Debug.Log(iP.Address+":"+iP.Port+"连接");//获取连接者的IP地址
Client cli = new Client();
cli.socket = client;
cli.ip = iP.Port.ToString();
clients.Add(cli);
cli.socket.BeginReceive(cli.data, 0, cli.data.Length, SocketFlags.None, OnRecive, cli);
socket.BeginAccept(OnAccept, null);
}
private void OnRecive(IAsyncResult ar)
{
Client cli = ar.AsyncState as Client;
int len = cli.socket.EndReceive(ar);//获取接收消息长度
if(len>0)
{
//创建一个正好长度的数组接收
byte[] datar = new byte[len];
Buffer.BlockCopy(cli.data, 0, datar,0, len);
//把完整消息拆分成消息头和消息体
//消息头 = id
int id = BitConverter.ToInt32(datar, 0);
byte[] boby = new byte[datar.Length - 4];//创建一个接收消息体的byte数组
//消息体等于消息内容
Buffer.BlockCopy(datar, 4, boby, 0, boby.Length);
//消息分发
//通过消息id将消息内容给对应的回调函数;
MessageCenter.Ins().BroadCast(id, boby, cli);
//异步接收消息
cli.socket.BeginReceive(cli.data, 0, cli.data.Length, SocketFlags.None, OnRecive, cli);
}
else
{
cli.socket.Shutdown(SocketShutdown.Both);//禁止消息的发送和接收
cli.socket.Close();//关闭连接并释放所有相关资源
clients.Remove(cli);//从客户端集合中移除
Debug.Log(cli.ip+"离线");
}
// throw new NotImplementedException();
}
public void Send(int id,string str,Client cli)
{
//消息头
byte[] head = BitConverter.GetBytes(id);//转换为byte
//消息体
byte[] body = UTF8Encoding.UTF8.GetBytes(str);
//完整消息=消息头+消息体
byte[] data = new byte[head.Length+body.Length];
Buffer.BlockCopy(head,0,data,0,head.Length);
Buffer.BlockCopy(body, 0, data, 4, body.Length);
cli.socket.BeginSend(data, 0, data.Length, SocketFlags.None, OnSend, cli);
}
public void Send(int id, byte[] str)
{
//消息头
byte[] head = BitConverter.GetBytes(id);//转换为byte
//消息体
//完整消息=消息头+消息体
byte[] data = new byte[head.Length + str.Length];
Buffer.BlockCopy(head, 0, data, 0, head.Length);
Buffer.BlockCopy(str, 0, data, 4, str.Length);
foreach (var item in clients)
{
item.socket.BeginSend(data, 0, data.Length, SocketFlags.None, OnSend, item);
}
}
private void OnSend(IAsyncResult ar)
{
Client cli = ar.AsyncState as Client;
int len = cli.socket.EndSend(ar);//结束挂起发送
Debug.Log("发送长度"+len);
// throw new NotImplementedException();
}
}
}
消息中心
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace 飞机服务器
{
/// <summary>
/// 消息中心
/// </summary>
class MessageCenter:Singleton<MessageCenter>
{
Dictionary<int, Action<object>> dic = new Dictionary<int, Action<object>>();
//注册
public void AddListener(int id,Action<object> action)
{
if(dic.ContainsKey(id))
{
dic[id] += action;
}
else
{
dic.Add(id, action);
}
}
//发送
public void BroadCast(int id,params object[] data)
{
if(dic.ContainsKey(id))
{
dic[id](data);
}
}
//移除
public void Remove(int id,Action<object> action)
{
if(dic.ContainsKey(id))
{
dic[id] -= action;
if(dic[id]==null)
{
dic.Remove(id);
}
}
}
}
}
注册事件
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace 飞机服务器
{
static class MsgID
{
// public const int S2C_SETCOLOR = 1001;//服务器向客户端发颜色
public const int S2C_CHAT = 1001;//服务器向客户端发聊天信息
public const int C2S_CHAT = 1002;//客户端向服务器发聊天信息
public const int C2S_NICK = 1003;
public const int S2C_NICK = 1004;
public const int C2S_ANGLEZ = 1005;
public const int S2C_ANGLEZ = 1006;
public const int C2S_ANGLEX = 1007;
public const int S2C_ANGLEX = 1008;
}
class Chat:Singleton<Chat>
{
public void Init()
{
MessageCenter.Ins().AddListener(MsgID.C2S_CHAT, OnChat);
MessageCenter.Ins().AddListener(MsgID.C2S_NICK, OnStart);
MessageCenter.Ins().AddListener(MsgID.C2S_ANGLEZ, OnAngleZ);
MessageCenter.Ins().AddListener(MsgID.C2S_ANGLEX, OnAngleX);
}
private void OnAngleX(object obj)
{
}
private void OnAngleZ(object obj)
{
}
private void OnStart(object obj)
{
}
private void OnChat(object obj)
{
}
}
}
下面有一个案例当然尽管案例可能不太完美
案例
YZXXDXN/异步平面: 异步连接 (github.com)
|