您的位置:首页 > 理论基础 > 计算机网络

[C#]手把手教你打造Socket的TCP通讯连接(三)

2015-03-14 13:00 393 查看
一篇中,我们编写了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

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)
{
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

/// <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验证等等。

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

原文地址:/article/6996672.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: