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复制过来。
然后我们再写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。
所有工作,全部完成。
这个Socket还有很多功能可以增加、改造。
比如你自己写一个Handler内置加密解密,或者压缩与解压缩。
还可以再改写一下Stream,可以弄成NegotiateStream验证等等。
下一篇我们总结一下所有工作。
原文地址:http://www.cnblogs.com/Kation/archive/2013/03/07/2947278.html