C# Socket异步通信

由于平时使用Unity开发,所以相对其他的编程语言,对C#比较了解,所以写了一个C/S的Socket异步通信程序。目前只是在C#中测试,未来会接入Unity3D,这个只是我个人用来进行学习用的,所以大家不必在意使用哪个语言做后端服务器开发更好

一些说明:

  • 使用最简单的TCP协议来进行通信,通信数据未做任何包装和加工(后面会接入Protobuf-net)
  • 采用异步通信结构
  • 为了提高服务器的性能,使用了连接池复用的方法

1.服务器连接池

class Conn
{
    //缓存区大小
    public const int BUFFER_SIZE = 1024;
    //socket
    public Socket socket;
    //是否被使用
    public bool isUse = false;
    //Buff
    public byte[] readBuff;
    public int buffCount = 0;

    //构造函数
    public Conn()
    {
        readBuff = new byte[BUFFER_SIZE];
    }

    //初始化
    public void Init(Socket socket)
    {
        this.socket = socket;
        isUse = true;
        buffCount = 0;
    }

    //缓存区剩余的字节
    public int BuffRemain()
    {
        return BUFFER_SIZE - buffCount;
    }

    //获取客户端的地址
    public string GetAddress()
    {
        if (!isUse)
            return "获取地址失败";

        return socket.RemoteEndPoint.ToString();
    }

    //关闭
    public void Close()
    {
        if (isUse)
            return;

        Console.WriteLine(GetAddress() + " 断开连接");
        socket.Close();
        isUse = false;
    }
}
View Code

2.服务器Socket

class UnityServer
{
    //监听套接字
    public Socket listenSocket;
    //客户端连接(异步)
    public Conn[] conns;
    //最大连接数
    public int maxCount = 100;

    //获取连接池索引
    public int NewIndex()
    {
        if (conns == null)
            return -1;

        for(int i=0;i<conns.Length;i++)
        {
            if (conns[i] == null)
            {
                conns[i] = new Conn();
                return i;
            }
            else if(conns[i].isUse == false)
            {
                return i;
            }
        }
        return -1;
    }

    //开启服务器
    public void Start(IPEndPoint iPEndPoint)
    {
        //连接对象池
        conns = new Conn[maxCount];
        for(int i=0;i<maxCount;i++)
        {
            conns[i] = new Conn();
        }
        //socket
        listenSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            
        //bind
 
        listenSocket.Bind(iPEndPoint);

        //listen
        listenSocket.Listen(maxCount);

        //Accept
        listenSocket.BeginAccept(AcceptCb, null);
    }

    //Accept回调函数
    private void AcceptCb(IAsyncResult asyncResult)
    {
        try
        {
            Socket socket = listenSocket.EndAccept(asyncResult);
            int index = NewIndex();

            if(index == -1)
            {
                Console.WriteLine("Conn连接池已满!");
            }
            else
            {
                Conn conn = conns[index];
                conn.Init(socket);
                string addr = conn.GetAddress();
                Console.WriteLine("客户端 [" + addr + "] 连接");
                Console.WriteLine("已连接 " + index + " 个客户端");
                conn.socket.BeginReceive(conn.readBuff, conn.buffCount, conn.BuffRemain(), SocketFlags.None, ReceiveCb, conn);
                listenSocket.BeginAccept(AcceptCb, null);
            }
        }
        catch(Exception ex)
        {
            Console.WriteLine("AcceptCb 失败:" + ex.Message);
        }
    }

    //Receive回调函数
    private void ReceiveCb(IAsyncResult asyncResult)
    {
        //获取接收对象
        Conn conn = (Conn) asyncResult.AsyncState;
        try
        {
            int count = conn.socket.EndReceive(asyncResult);
            //关闭信号
            if(count <= 0)
            {
                Console.WriteLine("收到 [" + conn.GetAddress() + "] 断开连接");
                conn.Close();

                return;
            }

            //数据处理
            string str = conn.socket.RemoteEndPoint.ToString() +":" + Encoding.UTF8.GetString(conn.readBuff, 0, count);
            Console.WriteLine("[" + conn.GetAddress() + "] : " + str);

            byte[] sendMsg = Encoding.UTF8.GetBytes(str);
            //将所有的消息广播
            for(int i=0;i<maxCount;i++)
            {
                if (conns[i] == null || !conns[i].isUse)
                    continue;
                conns[i].socket.Send(sendMsg);
                        
            }

            //继续接收
            conn.socket.BeginReceive(conn.readBuff, conn.buffCount, conn.BuffRemain(), SocketFlags.None, ReceiveCb, conn);

        }
        catch (Exception ex)
        {
            Console.WriteLine("[" + conn.GetAddress() + "] 断开连接");
            conn.Close();
        }
    }        
}
View Code

3.服务器Main函数

class Program
{
    static void Main(string[] args)
    {
        int port = 1234;
        IPEndPoint iPEndPoint = new IPEndPoint(IPAddress.Any, port);
        Console.WriteLine("服务器开启...");

        UnityServer unityServer = new UnityServer();
        unityServer.Start(iPEndPoint);

        while(true)
        {
            string input = Console.ReadLine();
            if (input == "Q")
                return;
        }
            
    }
}
View Code

4.客户端Socket

class Program
{
    //客户端套接字
    static Socket clientSocket;
    static void Main(string[] args)
    {

        string IP = "127.0.01";
        IPAddress ip = IPAddress.Parse(IP);
        int port = 1234;

        clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        IPEndPoint endPoint = new IPEndPoint(ip, port);

        //连接服务器
        try
        {
            clientSocket.Connect(endPoint);

            //发送字节
            string str = Console.ReadLine();
            byte[] message = Encoding.UTF8.GetBytes(str);
            clientSocket.Send(message);

            //接收服务器信息
            byte[] receive = new byte[1024];
            clientSocket.Receive(receive);
            Console.WriteLine("接收消息为:" + Encoding.UTF8.GetString(receive));

        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
        finally
        {
            //关闭连接
            clientSocket.Close();
        }

        Console.ReadKey();

    }

}
View Code

5.测试结果

  启动服务器,注意端口号和IP地址

  

  启动客户端,服务器显示有客户端连接上来(0个客户端是因为是0开始计数的)

  

  客户端发送消息,服务器将这个消息返回到所有在线的客户端

  

  

总结:

    这只是一个简单的Socket测试Demo,如果要用于真正的通信,就需要考虑封装协议、加密数据、网络异常处理、服务器负载、高并发能力等,后续会继续学习如何完成一个小型的Unity游戏服务器

 

转载于:https://www.cnblogs.com/AbstractLee/p/8623454.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值