• [C#]手把手教你打造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(value + "不是有效端口。");
                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
    public class SocketBase : ISocket, IDisposable
    {
        protected Socket Socket { get; private set; }
        protected Stream Stream { get; set; }
    
        /// <summary>
        /// 实例化TCP客户端。
        /// </summary>
        public SocketBase(Socket socket, ISocketHandler socketHandler)
        {
            if (socket == null)
                throw new ArgumentNullException("socket");
            if (socketHandler == null)
                throw new ArgumentNullException("socketHandler");
            Socket = socket;
            Handler = socketHandler;
        }
        
        /// <summary>
        /// Socket处理程序
        /// </summary>
        public ISocketHandler Handler { get; set; }
    
        /// <summary>
        /// 获取是否已连接。
        /// </summary>
        public bool IsConnected { get { return Socket.Connected; } }
    
        #region 断开连接
    
        /// <summary>
        /// 断开与服务器的连接。
        /// </summary>
        public void Disconnect()
        {
            //判断是否已连接
            if (!IsConnected)
                throw new SocketException(10057);
            lock (this)
            {
                //Socket异步断开并等待完成
                Socket.BeginDisconnect(true, EndDisconnect, true).AsyncWaitHandle.WaitOne();
            }
        }
    
        /// <summary>
        /// 异步断开与服务器的连接。
        /// </summary>
        public void DisconnectAsync()
        {
            //判断是否已连接
            if (!IsConnected)
                throw new SocketException(10057);
            lock (this)
            {
                //Socket异步断开
                Socket.BeginDisconnect(true, EndDisconnect, false);
            }
        }
    
        private void EndDisconnect(IAsyncResult result)
        {
            try
            {
                Socket.EndDisconnect(result);
            }
            catch
            {
    
            }
            //是否同步
            bool sync = (bool)result.AsyncState;
    
            if (!sync && DisconnectCompleted != null)
            {
                DisconnectCompleted(this, new SocketEventArgs(this, SocketAsyncOperation.Disconnect));
            }
        }
    
        //这是一个给收发异常准备的断开引发事件方法
        private void Disconnected(bool raiseEvent)
        {
            if (raiseEvent && DisconnectCompleted != null)
                DisconnectCompleted(this, new SocketEventArgs(this, SocketAsyncOperation.Disconnect));
        }
    
        #endregion
    
        #region 发送数据
    
        /// <summary>
        /// 发送数据。
        /// </summary>
        /// <param name="data">要发送的数据。</param>
        public void Send(byte[] data)
        {
            //是否已连接
            if (!IsConnected)
                throw new SocketException(10057);
            //发送的数据不能为null
            if (data == null)
                throw new ArgumentNullException("data");
            //发送的数据长度不能为0
            if (data.Length == 0)
                throw new ArgumentException("data的长度不能为0");
    
            //设置异步状态
            SocketAsyncState state = new SocketAsyncState();
            state.IsAsync = false;
            state.Data = data;
            try
            {
                //开始发送数据
                Handler.BeginSend(data, 0, data.Length, Stream, EndSend, state).AsyncWaitHandle.WaitOne();
            }
            catch
            {
                //出现异常则断开Socket连接
                Disconnected(true);
            }
        }
    
        /// <summary>
        /// 异步发送数据。
        /// </summary>
        /// <param name="data">要发送的数据。</param>
        public void SendAsync(byte[] data)
        {
            //是否已连接
            if (!IsConnected)
                throw new SocketException(10057);
            //发送的数据不能为null
            if (data == null)
                throw new ArgumentNullException("data");
            //发送的数据长度不能为0
            if (data.Length == 0)
                throw new ArgumentException("data的长度不能为0");
    
            //设置异步状态
            SocketAsyncState state = new SocketAsyncState();
            state.IsAsync = true;
            state.Data = data;
            try
            {
                //开始发送数据并等待完成
                Handler.BeginSend(data, 0, data.Length, Stream, EndSend, state);
            }
            catch
            {
                //出现异常则断开Socket连接
                Disconnected(true);
            }
        }
    
        private void EndSend(IAsyncResult result)
        {
            SocketAsyncState state = (SocketAsyncState)result.AsyncState;
    
            //是否完成
            state.Completed = Handler.EndSend(result);
            //没有完成则断开Socket连接
            if (!state.Completed)
                Disconnected(true);
            //引发发送结束事件
            if (state.IsAsync && SendCompleted != null)
            {
                SendCompleted(this, new SocketEventArgs(this, SocketAsyncOperation.Send) { Data = state.Data });
            }
        }
    
        #endregion
    
        #region 接收数据
    
        protected void EndReceive(IAsyncResult result)
        {
            SocketAsyncState state = (SocketAsyncState)result.AsyncState;
            //接收到的数据
            byte[] data = Handler.EndReceive(result);
            //如果数据长度为0,则断开Socket连接
            if (data.Length == 0)
            {
                Disconnected(true);
                return;
            }
    
            //再次开始接收数据
            Handler.BeginReceive(Stream, EndReceive, state);
    
            //引发接收完成事件
            if (ReceiveCompleted != null)
                ReceiveCompleted(this, new SocketEventArgs(this, SocketAsyncOperation.Receive) { Data = data });
        }
    
        #endregion
    
        #region 事件
    
        ///// <summary>
        ///// 断开完成时引发事件。
        ///// </summary>
        public event EventHandler<SocketEventArgs> DisconnectCompleted;
        ///// <summary>
        ///// 接收完成时引发事件。
        ///// </summary>
        public event EventHandler<SocketEventArgs> ReceiveCompleted;
        ///// <summary>
        ///// 发送完成时引发事件。
        ///// </summary>
        public event EventHandler<SocketEventArgs> SendCompleted;
    
        #endregion
    
        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            lock (this)
            {
                if (IsConnected)
                    Socket.Disconnect(false);
                Socket.Close();
            }
        }
    }

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

    public class TCPListenerClient : SocketBase
    {
        internal TCPListenerClient(TCPListener listener, Socket socket)
            :base(socket,listener.Handler)
        {
            data = new Dictionary<string, object>();
    this["RemoteEndPoint"] = socket.RemoteEndPoint; //创建Socket网络流 Stream = new NetworkStream(socket); //设置服务器 Listener = listener; //开始异步接收数据 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
    /// <summary>
    /// TCP客户端
    /// </summary>
    public class TCPClient : SocketBase
    {
        /// <summary>
        /// 实例化TCP客户端。
        /// </summary>
        public TCPClient()
            : base(new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp), new SocketHandler())
        {
        }
    
        public bool IsUseAuthenticate { get; set; }
    
        /// <summary>
        /// 连接至服务器。
        /// </summary>
        /// <param name="endpoint">服务器终结点。</param>
        public void Connect(IPEndPoint endpoint)
        {
            //判断是否已连接
            if (IsConnected)
                throw new InvalidOperationException("已连接至服务器。");
            if (endpoint == null)
                throw new ArgumentNullException("endpoint");
            //锁定自己,避免多线程同时操作
            lock (this)
            {
                SocketAsyncState state = new SocketAsyncState();
                //Socket异步连接
                Socket.BeginConnect(endpoint, EndConnect, state).AsyncWaitHandle.WaitOne();
                //等待异步全部处理完成
                while (!state.Completed) { }
            }
        }
    
        /// <summary>
        /// 异步连接至服务器。
        /// </summary>
        /// <param name="endpoint"></param>
        public void ConnectAsync(IPEndPoint endpoint)
        {
            //判断是否已连接
            if (IsConnected)
                throw new InvalidOperationException("已连接至服务器。");
            if (endpoint == null)
                throw new ArgumentNullException("endpoint");
            //锁定自己,避免多线程同时操作
            lock (this)
            {
                SocketAsyncState state = new SocketAsyncState();
                //设置状态为异步
                state.IsAsync = true;
                //Socket异步连接
                Socket.BeginConnect(endpoint, EndConnect, state);
            }
        }
    
        private void EndConnect(IAsyncResult result)
        {
            SocketAsyncState state = (SocketAsyncState)result.AsyncState;
    
            try
            {
                Socket.EndConnect(result);
            }
            catch
            {
                //出现异常,连接失败。
                state.Completed = true;
                //判断是否为异步,异步则引发事件
                if (state.IsAsync && ConnectCompleted != null)
                    ConnectCompleted(this, new SocketEventArgs(this, SocketAsyncOperation.Connect));
                return;
            }
    
            //连接成功。
            //创建Socket网络流
            Stream = new NetworkStream(Socket);
            if (IsUseAuthenticate)
            {
                NegotiateStream negotiate = new NegotiateStream(Stream);
                negotiate.AuthenticateAsClient();
                while (!negotiate.IsMutuallyAuthenticated)
                {
                    Thread.Sleep(10);
                }
            }
            //连接完成
            state.Completed = true;
            if (state.IsAsync && ConnectCompleted != null)
            {
                ConnectCompleted(this, new SocketEventArgs(this, SocketAsyncOperation.Connect));
            }
    
            //开始接收数据
            Handler.BeginReceive(Stream, EndReceive, state);
        }
    
        /// <summary>
        /// 连接完成时引发事件。
        /// </summary>
        public event EventHandler<SocketEventArgs> ConnectCompleted;
    }

    所有工作,全部完成。

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

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

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

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

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

  • 相关阅读:
    python的select和epoll
    ibatis annotations 注解方式返回刚插入的自增长主键ID的值
    java web 项目中获取当前路径的几种方法
    Servlet的监听器
    mybatis-配置文件mybatis-config.xml
    数据库死锁
    JDBC控制事务
    server.xml 解析
    linux下Tomcat 安装后执行startup.sh,出现– Cannot find …bin/catalina.sh
    jni 类初始化失败(nested exception is java.lang.NoClassDefFoundError)
  • 原文地址:https://www.cnblogs.com/Kation/p/2947278.html
Copyright © 2020-2023  润新知