Socket异步C#简易网络框架和使用

服务器端
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();
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值