Zjh游戏(一)服务器连接、接收消息

使用VS2017创建一个类库。

服务器端

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace MyServer
{
   public class SocketPeer
    {
        private Socket serverSocket;
        /// <summary>
        /// 信号技术量,线程的拥塞控制
        /// </summary>
        private Semaphore semaphore;
        private ClientPeerPool clientPeerPool;//客户端连接对象池
        public void StartServer(string ip,int port,int maxClient)
        {
            try
            {
                semaphore = new Semaphore(maxClient, maxClient);
                clientPeerPool = new ClientPeerPool(maxClient);
                for (int i = 0; i < maxClient; i++)
                {
                    ClientPeer client = new ClientPeer();
                    client.ReceiveArgs.Completed += ReceiveArgs_Completed;
                    clientPeerPool.Enqueue(client);
                }
                serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                serverSocket.Bind(new IPEndPoint(IPAddress.Parse(ip), port));
                serverSocket.Listen(maxClient);
                Console.WriteLine("服务器启动成功");
                //开始接收客户端的连接
                StartAccept(null);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }


        /// <summary>
        /// 开始等待客户端的连接
        /// </summary>
        private void StartAccept(SocketAsyncEventArgs e)
        {
            if (e==null)
            {
                e = new SocketAsyncEventArgs();
                e.Completed += E_Completed;
            }
            //result 为true 代表正在接收连接,连接成功以后会调用E_Completed事件
            //为false 代表接收成功
            bool result = serverSocket.AcceptAsync(e);
            if (result==false)
            {
                ProcessAccept(e);
            }
        }
        /// <summary>
        /// 异步接收连接完成后的回调
        /// </summary>
        private void E_Completed(object sender, SocketAsyncEventArgs e)
        {
            ProcessAccept(e);
        }
        /// <summary>
        /// 出来客户端的连接
        /// </summary>
        private void ProcessAccept(SocketAsyncEventArgs e)
        {
            semaphore.WaitOne();//阻止当前线程,直到有客户端释放
            ClientPeer client = clientPeerPool.Dequeue();
            client.clientSocket = e.AcceptSocket;//得到Client  e.AcceptSocket 可以获取当前连接的客户端Sockt 
            Console.WriteLine(client.clientSocket.RemoteEndPoint+"客户端连接成功");
            //接收消息
            StartReceive(client);
            e.AcceptSocket = null;
            StartAccept(e);//伪递归
        }

        /// <summary>
        /// 开始接收消息
        /// </summary>
        private void StartReceive(ClientPeer client)
        {
            try
            {
                //这里需要一个异步套接字 在ClientPeer中定义一个,在构造方法中赋值
                bool result = client.clientSocket.ReceiveAsync(client.ReceiveArgs);
                if (result == false)
                {
                    ProcessReceive(client.ReceiveArgs);
                }
            }
            catch (Exception e)
            {

                Console.WriteLine(e.Message);
            }
          

        }
        /// <summary>
        ///异步接收消息完毕后的回调
        /// </summary>
        private void ReceiveArgs_Completed(object sender, SocketAsyncEventArgs e)
        {
            ProcessReceive(e);
        }
        /// <summary>
        /// 处理数据的接收
        /// </summary>
        private void ProcessReceive(SocketAsyncEventArgs e)
        {
            //UserToken 可以得到与这个套接字绑定的程序对象,在ClientPeer进行了绑定设置,这里得到ClientPeer
            ClientPeer client = e.UserToken as ClientPeer;
            //判断数据是否接收成功 SocketError.Success是否连接 BytesTransferred 获得收到数据的长度
            if (client.ReceiveArgs.SocketError == SocketError.Success && client.ReceiveArgs.BytesTransferred > 0)
            {
                byte[] packet = new byte[client.ReceiveArgs.BytesTransferred];//数据的存储
                //client.ReceiveArgs.Buffer 数据缓冲区,接收到的数据存放到这里
                Buffer.BlockCopy(client.ReceiveArgs.Buffer, 0, packet, 0, client.ReceiveArgs.BytesTransferred);
                //让Client自身处理接收到的消息
                client.ProcessReceive(packet);
                StartReceive(client);//伪递归
            }
            else//数据接收不成功
            {
                //接收的数据为0 代表断开连接了
                if (client.ReceiveArgs.BytesTransferred==0)
                {
                    //客户端主动断开连接
                    if (client.ReceiveArgs.SocketError==SocketError.Success)
                    {
                        DisConnected(client,"客户端主动断开连接");
                    }
                    else//因为网络原因,被动断开连接
                    {
                        DisConnected(client, e.SocketError.ToString());
                    }
                }
            }
        }
        //断开连接
        private void DisConnected(ClientPeer client,string reason)
        {
            try
            {
                if (client==null)
                {
                    throw new Exception("客户端为空,无法断开连接!");
                }
                Console.WriteLine(client.clientSocket.RemoteEndPoint + "客户端断开连接,原因:" + reason);
                //客户端自己处理断开连接
                client.DisConnected();

                clientPeerPool.Enqueue(client);//断开连接的Client放到客户端连接池中
                semaphore.Release();//释放一个Client
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
    }
}

客户端

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;

namespace MyServer
{
    public class ClientPeer
    {
        public Socket clientSocket { get; set; }
        public SocketAsyncEventArgs ReceiveArgs { get; set; }
        public ClientPeer()
        {
            ReceiveArgs = new SocketAsyncEventArgs();
            ReceiveArgs.UserToken = this;//绑定UserToken 通过UserToken获得当前的ClientPeer
            ReceiveArgs.SetBuffer(new byte[2048],0,2048);//设置数据缓冲区,数据缓冲区在使用时,需要进行设置
        }

        /// <summary>
        /// 处理数据的接收
        /// </summary>
        /// <param name="packet"></param>
        public void ProcessReceive(byte[] packet)
        {

        }
        /// <summary>
        /// 断开连接
        /// </summary>
        public void DisConnected()
        {

        }
    }
}

客户端连接池

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyServer
{
  public  class ClientPeerPool
    {
        public Queue<ClientPeer> clientPeers;

        public ClientPeerPool(int maxCount)
        {
            clientPeers = new Queue<ClientPeer>(maxCount);
        }
        public void Enqueue(ClientPeer client)
        {
            clientPeers.Enqueue(client);
        }
        public ClientPeer Dequeue()
        {
            return clientPeers.Dequeue();
        }
    }
}

构造类

using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyServer
{
    /// <summary>
    /// 构造类,避免粘包问题
    /// </summary>
    public class EncodeTools
    {
        /// <summary>
        /// 构造包 包头加包尾
        /// </summary>
        public static byte[] EncodePacket(byte[] packet)
        {
            //using 不需要手动释放 会自动释放
            using (MemoryStream ms = new MemoryStream())
            {
                using (BinaryWriter bw = new BinaryWriter(ms))
                {
                    //写入包头(数据长度,前四个字节)
                    bw.Write(packet.Length);
                    //写入包尾(数据) 解析时:先解析出包头 得到数据长度 数据长度在和剩下数据长度进行比较
                    bw.Write(packet);
                    byte[] data = new byte[ms.Length];//获取所有写入流中的总长度
                    //ms.GetBuffer(); 返回流中所有创建的无符号字节的数组
                    Buffer.BlockCopy(ms.GetBuffer(), 0, data, 0, (int)ms.Length);
                    return data;
                }
            }
        }
        /// <summary>
        /// 解析包 这里修改了缓冲区的地址,需要加引用参数ref 
        /// </summary>
        public static byte[] DecodePacket(ref List<byte> cache)
        {
            if (cache.Count < 4)//前4位为数据长度,不够4位返回null
            {
                return null;
            }

            using (MemoryStream ms = new MemoryStream(cache.ToArray()))
            {
                using (BinaryReader br = new BinaryReader(ms))
                {
                    int length = br.ReadInt32();//ReadInt32 读取前四个字节 得到数据长度
                    //Position获取当前在流中的所在位置  ReadInt32读取以后会让游标向后移4位 当前的总长度-减去游标 
                    //得到剩下的长度
                    int remainLength = (int)(ms.Length - ms.Position);
                    if (length > remainLength)
                    {
                        return null;
                    }
                    byte[] data = br.ReadBytes(length);//读取一个数组
                    cache.Clear();//更新数据缓存
                    //得到读取后的位置
                    int remainLengthAngin = (int)(ms.Length - ms.Position);
                    //把后面的数据填充到缓冲区 解析后的清除掉
                    cache.AddRange(br.ReadBytes(remainLengthAngin));
                    return data;
                }
            }
        }
    }
}

在这里插入图片描述
有需要学习视频欢迎关注微信公众号

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值