zoukankan      html  css  js  c++  java
  • GJM : Socket TCP 通信连接(三)

    上一篇中,我们编写了SocketHandler处理Socket的IO。

    现在我们只剩下服务器端了。

    服务器端包含两个类,一个TCPListener,一个TCPListenerClient。

    TCPListener只管Start与Stop还有Accept。

    TCPListenerClient是连接到服务器的客户端,相当于TCPClient在TCPListener上的体现。

    现在我们开始编写TCPListener。

    复制代码
    /// <summary>
    /// TCP监听端
    /// </summary>
    public class TCPListener : IEnumerable<TCPListenerClient>
    {
        private Socket socket;
        private HashSet<TCPListenerClient> clients;
    
        /// <summary>
        /// 实例化TCP监听者。
        /// </summary>
        public TCPListener()
        {
            clients = new HashSet<TCPListenerClient>();
            IsStarted = false;
            Handler = new SocketHandler();
        }
    
        public ISocketHandler Handler { get; set; }
    
        private int port;
        /// <summary>
        /// 监听端口。
        /// </summary>
        public int Port
        {
            get { return port; }
            set
            {
                if (value < 0 || value > 65535)
                    throw new ArgumentOutOfRangeException(port + "不是有效端口。");
                port = value;
            }
        }
    
        /// <summary>
        /// 服务启动中
        /// </summary>
        public bool IsStarted { get; private set; }
    
        /// <summary>
        /// 开始服务。
        /// </summary>
        public void Start()
        {
    
        }
    
        /// <summary>
        /// 停止服务。
        /// </summary>
        public void Stop()
        {
    
        }
    
        /// <summary>
        /// 接收完成时引发事件。
        /// </summary>
        public event EventHandler<SocketEventArgs> ReceiveCompleted;
        /// <summary>
        /// 接受客户完成时引发事件。
        /// </summary>
        public event EventHandler<SocketEventArgs> AcceptCompleted;
        /// <summary>
        /// 客户断开完成时引发事件。
        /// </summary>
        public event EventHandler<SocketEventArgs> DisconnectCompleted;
        /// <summary>
        /// 发送完成时引发事件。
        /// </summary>
        public event EventHandler<SocketEventArgs> SendCompleted;
    
        /// <summary>
        /// 获取客户端泛型。
        /// </summary>
        /// <returns></returns>
        public IEnumerator<TCPListenerClient> GetEnumerator()
        {
            return clients.GetEnumerator();
        }
    
        /// <summary>
        /// 获取客户端泛型。
        /// </summary>
        /// <returns></returns>
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return clients.GetEnumerator();
        }
    
        /// <summary>
        /// 释放资源。
        /// </summary>
        /// <returns></returns>
        public void Dispose()
        {
    
        }
    }
    复制代码

    TCPListener继承IEnumerable<TCPListenerClient>与IDisposable

    clients保存所有已连接的客户端。

    编写Start方法。

    复制代码
        /// <summary>
        /// 开始服务。
        /// </summary>
        public void Start()
        {
            lock (this)
            {
                if (IsStarted)
                    throw new InvalidOperationException("已经开始服务。");
                socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                //绑定端口
                //可以引发端口被占用异常
                socket.Bind(new IPEndPoint(IPAddress.Any, port));
                //监听队列
                socket.Listen(512);
                //如果端口是0,则是随机端口,把这个端口赋值给port
                port = ((IPEndPoint)socket.LocalEndPoint).Port;
                //服务启动中设置为true
                IsStarted = true;
                //开始异步监听
                socket.BeginAccept(EndAccept, null);
            }
        }
    
        //异步监听结束
        private void EndAccept(IAsyncResult result)
        {
            //获得客户端Socket
            Socket clientSocket = socket.EndAccept(result);
            //实例化客户端类
            TCPListenerClient client = new TCPListenerClient(this, clientSocket);
            //增加事件钩子
            client.SendCompleted += client_SendCompleted;
            client.ReceiveCompleted += client_ReceiveCompleted;
            client.DisconnectCompleted += client_DisconnectCompleted;
            socket.BeginAccept(EndAccept, null);
    
            //增加客户端
            lock (clients)
                clients.Add(client);
    
            //客户端连接事件
            if (AcceptCompleted != null)
                AcceptCompleted(this, new SocketEventArgs(client, SocketAsyncOperation.Accept));
        }
    
        //客户端断开连接
        private void client_DisconnectCompleted(object sender, SocketEventArgs e)
        {
            //移除客户端
            lock (clients)
                clients.Remove((TCPListenerClient)e.Socket);
    
            e.Socket.DisconnectCompleted -= client_DisconnectCompleted;
            e.Socket.ReceiveCompleted -= client_ReceiveCompleted;
            e.Socket.SendCompleted -= client_SendCompleted;
            if (DisconnectCompleted != null)
                DisconnectCompleted(this, e);
        }
    
        //收到客户端发送的数据
        private void client_ReceiveCompleted(object sender, SocketEventArgs e)
        {
            if (ReceiveCompleted != null)
                ReceiveCompleted(this, e);
        }
    
        //向客户端发送数据完成
        private void client_SendCompleted(object sender, SocketEventArgs e)
        {
            if (SendCompleted != null)
                SendCompleted(this, e);
        }
    复制代码

    编写Stop与Dispose方法。

    复制代码
        /// <summary>
        /// 停止服务。
        /// </summary>
        public void Stop()
        {
            lock (this)
            {
                if (!IsStarted)
                    throw new InvalidOperationException("没有开始服务。");
                foreach (TCPListenerClient client in clients)
                {
                    client.Disconnect();
                    client.DisconnectCompleted -= client_DisconnectCompleted;
                    client.ReceiveCompleted -= client_ReceiveCompleted;
                    client.SendCompleted -= client_SendCompleted;
                }
                socket.Close();
                socket = null;
                IsStarted = false;
            }
        }
    
        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            if (socket == null)
                return;
            Stop();
        }
    复制代码

    轮到TCPListenerClient了,TCPListenerClient其实和TCPClient差不多,也是要继承ISocket和IDisposable。

    既然重复代码做么多,要不要合并起来呢?答案是肯定的。

    做一个SocketBase类,继承ISocket和IDisposable。

    大部分代码直接从TCPClient复制过来。

    View Code

    然后我们再写TCPListenerClient,继承SocketBase。

    复制代码
    public class TCPListenerClient : SocketBase
    {
        internal TCPListenerClient(TCPListener listener, Socket socket)
            :base(socket,listener.Handler)
        {
            this["RemoteEndPoint"] = socket.RemoteEndPoint;
            //创建Socket网络流
            Stream = new NetworkStream(socket);
    //设置服务器
            Listener = listener;            
            data = new Dictionary<string, object>();
    
            //开始异步接收数据
            SocketAsyncState state = new SocketAsyncState();
            Handler.BeginReceive(Stream, EndReceive, state);
        }
    
        public TCPListener Listener { get; private set; }
    }
    复制代码

    我们还可以给TCPListenerClient加上点东西,比如类似Session的东西。

    复制代码
        private Dictionary<string, object> data;
    
        public object this[string key]
        {
            get
            {
                key = key.ToLower();
                if (data.ContainsKey(key))
                    return data[key];
                return null;
            }
            set
            {
    
                key = key.ToLower();
                if (value == null)
                {
                    if (data.ContainsKey(key))
                        data.Remove(key);
                    return;
                }
                if (data.ContainsKey(key))
                    data[key] = value;
                else
                    data.Add(key, value);
            }
        }
    复制代码

    为构造函数添加以下代码。

            data = new Dictionary<string, object>();
            //保存IP地址到字典
            this["RemoteEndPoint"] = socket.RemoteEndPoint;

    这样,我们的TCPListenerClient就完成了。

    接下来我们再把TCPClient修改以下,继承SocketBase。

    View Code

    所有工作,全部完成。

    这个Socket还有很多功能可以增加、改造。

    比如你自己写一个Handler内置加密解密,或者压缩与解压缩。

    还可以再改写一下Stream,可以弄成NegotiateStream验证等等。

    下一篇我们总结一下所有工作。

    原文地址:http://www.cnblogs.com/Kation/archive/2013/03/07/2947278.html

  • 相关阅读:
    Roce ofed 环境搭建与测试
    Ubuntu 1804 搭建NFS服务器
    Redhat 8.0.0 安装与网络配置
    Centos 8.1 安装与网络配置
    SUSE 15.1 系统安装
    VSpare ESXi 7.0 基本使用(模板、iso、SRIOV)
    VSpare ESXi 7.0 服务器安装
    open SUSE leap 15.1 安装图解
    KVM虚拟机网卡连接网桥
    GitHub Action一键部署配置,值得拥有
  • 原文地址:https://www.cnblogs.com/TDou/p/6478903.html
Copyright © 2011-2022 走看看