问题:
你需要写一个服务端去监听从某一个端口进来的一个TCP客户端的请求。然后,这些请求可以在服务端被处理,并且每一个响应都可以被发送到TCP客户端。我将在下一个“菜”中介绍怎么样创建一个TCP客户端和服务端进行交互。
解决方法:
我们创建一个MyTcpServer的类来监听从一个端口进来的基于TCP终端的请求:
class
MyTcpServer
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
Private Membersregion Private Members
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
private TcpListener _listener = null;
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
private IPAddress _address;
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
private int _port;
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
private bool _listening = false;
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
构造函数#region 构造函数
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public MyTcpServer(IPAddress address, int port)
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
_port = port;
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
_address = address;
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
#endregion // 构造函数
}
这个MyTCPServer类有两个属性:
- Address(IPAddress)
- Port(int)
下面这些返回当前服务端正在监听的地址和端口,并且返回监听的状态:
属性
#region 属性
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public IPAddress Address
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return _address; }
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public int Port
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return _port; }
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public bool Listening
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
get
{ return _listening; }
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
#endregion
这个Listen方法告诉MyTcpServer类开始在某个地址和端口对上开始监听。你创建并启动了一个TcpListener,然后调用它的AcceptTcpClient方法去等待一个客户端请求的到来。一旦客户端连接成功,一个请求将被发送到线程池去处理客户端,它将运行ProcessClient方法。
监听器将在处理完客户端的请求后关闭:
public
void
Listen()
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
try
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
lock (_syncRoot)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
_listener = new TcpListener(_address, _port);
// 启动服务器
_listener.Start();
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
// 设置为监听状态
_listening = true;
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
// 监听
do
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
Trace.Write("Looking for someone to talk to…?");
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
// 等待连接
TcpClient newClient = _listener.AcceptTcpClient();
Trace.WriteLine("Connected to new client");
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
// 排队处理客户的请求
ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessClient), newClient);
}while (_listening);
}
catch (SocketException se)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
Trace.WriteLine("SocketException: " + se.ToString());
}
finally
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
// 关闭
StopListening();
}
}
StopListening方法使得TCPServer停止监听请求:
public
void
StopListening()
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
if (_listening)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
lock (_syncRoot)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
{
// 设置为非监听状态
_listening = false;
// 关闭
_listener.Stop();
}
}
}
ProcessClient方法在一个线程池上被执行用来处理一个已经连接的客户端。它通过TcpClient.GetStream方法从客户端得到NetworkStream,然后读取整个请求。在返回一个响应后,它关闭客户端的连接。
私有方法
#region 私有方法
private void ProcessClient(object client)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
TcpClient newClient = (TcpClient)client;
try
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
// 缓存读取的数据
byte[] bytes = new byte[1024];
StringBuilder clientData = new StringBuilder();
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
// 获取NetworkStream
using (NetworkStream ns = newClient.GetStream())
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
// 初始化一个连接的read timeout为1分钟
ns.ReadTimeout = 60000;
int bytesRead = 0;
do
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
// 读取数据
try
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
bytesRead = ns.Read(bytes, 0, bytes.Length);
if (bytesRead > 0)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
// 把bytes数据转换成ASCII类型的字符串,并加到末尾
clientData.Append(
Encoding.ASCII.GetString(bytes, 0, bytesRead));
// 减少read timeout到1秒
ns.ReadTimeout = 1000;
}
}
catch (IOException ioe)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
// 读取超时;所有的数据都已经被读取了
Trace.WriteLine("Read timed out: " + ioe.ToString());
bytesRead = 0;
}
}
while (bytesRead > 0);
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
Trace.WriteLine("Client says: " + clientData.ToString());
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
bytes = Encoding.ASCII.GetBytes("Thanks call again!");
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
// 返回一个响应
ns.Write(bytes, 0, bytes.Length);
}
}
finally
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
// 关闭客户端的连接
if (newClient != null)
newClient.Close();
}
}
#endregion
一个简单的监听客户端的服务端程序是这样的:
class
Program
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
{
static MyTcpServer server = null;
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
static void Main(string[] args)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
// 在另一个线程上运行服务端
ThreadPool.QueueUserWorkItem(RunServer);
Console.WriteLine("Press Esc to stop the server…?");
ConsoleKeyInfo cki;
while (true)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
cki = Console.ReadKey();
if (cki.Key == ConsoleKey.Escape)
break;
}
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
static void RunServer(object stateInfo)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
// 启动
server = new MyTcpServer(IPAddress.Loopback, 55555);
server.Listen();
}
}
MyTcpServer类使用传进来的IP地址和端口号作为参数,并且在Listen方法中在该IP地址和端口上创建一个TcpListener。
一旦创建成功,TcpListener.Start方法被调用来启动服务器。个阻塞的AcceptTcpClient方法被调用来监听来自基于TCP的客户端的请求。一旦建立连接,ProcessClient方法将执行。
在这个方法中,服务器从客户端读取数据,并返回一个可信的响应。服务器通过NetworkStream.Close方法和TcpClient.Close方法来中断和客户端的连接。
当StopListening方法被调用的时候,服务器停止监听。StopListening方法通过调用TcpListener.Stop方法来使服务器掉线。
写一个TCP客户端