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

  • 相关阅读:
    POJ3678 KATU PUZZLE
    poj3321(codevs1228)苹果树
    codevs 1955 光纤通信 USACO
    codevs 1027 姓名与ID
    codevs 1051 接龙游戏
    洛谷 P1717 钓鱼
    codevs 1062 路由选择
    洛谷 P1083 借教室
    codevs 2596 售货员的难题
    Vijos 1053 easy sssp
  • 原文地址:https://www.cnblogs.com/TDou/p/6478903.html
Copyright © 2011-2022 走看看