[英]C# Async TCP Server overkill?
This is really an implementation question so I feel it's best to start with my specific case. 这实际上是一个实现问题,所以我觉得最好从我的具体案例开始。
I've got a C# server that listens for TCP connections asynchronously from mobile clients. 我有一个C#服务器,它从移动客户端异步侦听TCP连接。 When a mobile client connects a new thread is started, the client sends a little (<100 bytes usually) text message and receives one of similar size back.
当移动客户端连接新线程启动时,客户端发送一些(通常<100字节)文本消息并接收相似大小的文本消息。 After the server responds, it closes the connection and ends the thread.
服务器响应后,它会关闭连接并结束线程。
Current basic usage is a user logs in, checks on stuff for sometimes up to 5 minutes, sending little messages and thus creating new threads on the server in rapid succession , and they disconnect only to reconnect a few hours later. 当前的基本用法是用户登录,有时最多5分钟检查一些内容, 发送少量消息,从而快速连续地在服务器上创建新线程 ,并且几小时后它们仅断开连接。 Also, every user has their own server they run on their PC, and as such most servers will only ever have one client connected at any given time, in RARE cases two.
此外,每个用户都有自己的PC上运行的服务器,因此大多数服务器在任何给定时间只能连接一个客户端,在RARE情况下只有两个。
Right now I'm running into the following error, An existing connection was forcibly closed by the remote host , and it has got me thinking, am I doing this wrong? 现在我遇到了以下错误, 现有连接被远程主机强行关闭 ,它让我思考, 我做错了吗?
So my question(s): 所以我的问题:
In response to some of the questions here: 回答这里的一些问题:
public void StartListening()
{
//Data buffer for incoming data.
byte[] bytes = new Byte[1024];
//Establish the local endpoint for the socket.
IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, Port);
//Create a TCP/IP socket.
Socket listener = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
listener.SetSocketOption(SocketOptionLevel.Socket,SocketOptionName.DontLinger,1);
listener.SetSocketOption(SocketOptionLevel.Socket,SocketOptionName.ReuseAddress,1);
//Bind the socket to the local endpoint and listen for
//incoming connections.
try
{
listener.Bind(localEndPoint);
listener.Listen(100);
while (listening)
{
//Set the event to nonsignaled state.
allDone.Reset();
//Start an asychronous socket to listen for connections.
Print("Waiting for a connection...");
listener.BeginAccept(
new AsyncCallback(AcceptCallback),
listener);
//Wait until a connection is made before continuing.
allDone.WaitOne();
}
}
catch (Exception e)
{
Print(e.ToString());
}
listener.Close();
}
public void AcceptCallback(IAsyncResult arg)
{
//Signal the main thread to continue.
allDone.Set();
try
{
//Get the socket that handles the client request.
Socket listener = (Socket) arg.AsyncState;
Socket handler = listener.EndAccept(arg);
//Create the state object.
StateObject state = new StateObject();
state.workSocket = handler;
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
catch (ObjectDisposedException ex)
{
Print("Server terminated from another thread.");
}
}
public void ReadCallback(IAsyncResult arg)
{
String content = String.Empty;
//Retrieve the state object and the handler socket
//from the asynchronous state object.
StateObject state = (StateObject) arg.AsyncState;
Socket handler = state.workSocket;
//Read data from the client socket.
int bytesRead = 0;
try
{
bytesRead = handler.EndReceive(arg);
}
catch (ObjectDisposedException ex)
{
Print("Process was terminated from another thread.");
}
if (bytesRead > 0)
{
//There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(
state.buffer,0,bytesRead));
//Check for end-of-file tag. If it is not there, read
//more data.
content = state.sb.ToString();
if (content.IndexOf("<EOF>") > -1)
{
content = content.Remove(content.Length-6);
//All the data has been read from the
//client. Display it on the console.
Print("Read " + content.Length + " bytes from socket. \n Data : " + content);
Respond(handler, content);
}
else
{
//Not all data received. Get more.
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
}
}
private void Send(Socket handler, String data)
{
//Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.ASCII.GetBytes(data);
//Begin sending the data to the remote device.
handler.BeginSend(byteData,0,byteData.Length,0,
new AsyncCallback(SendCallback),handler);
}
private void SendCallback(IAsyncResult arg)
{
try
{
//Retrieve the socket from the state object.
Socket handler = (Socket) arg.AsyncState;
//Complete sending the data to the remote device.
int bytesSent = handler.EndSend(arg);
Print("Sent " + bytesSent + " bytes to client.");
handler.Shutdown(SocketShutdown.Both);
//need to make this not linger around
handler.LingerState = new LingerOption(true,1);
handler.Close();
}
catch (Exception e)
{
Print(e.ToString());
}
}
Ideally, you'd be using the .NET threadpool, which would be much more efficient than creating a new thread for every connection. 理想情况下,您将使用.NET线程池,这比为每个连接创建新线程更有效。 Can you please share your exact "async" code - if you're using the existing async pattern on TCPListener then you're probably already using the threadpool.
你可以分享你确切的“异步”代码 - 如果你在TCPListener上使用现有的异步模式,那么你可能已经在使用线程池了。
With respect to the exception, that's what you'd expect to see when your clients disconnect from the server. 关于异常,当您的客户端与服务器断开连接时,您会看到这种情况。 Is it occurring before you manage to receive all the data?
是否在您设法接收所有数据之前发生了? Are you flushing your socket on the client side?
你在客户端刷新插座吗?
In terms of completely crashing the server, just keep testing, and log any globally unhandled exceptions. 在完全崩溃服务器方面,只需继续测试,并记录任何全局未处理的异常。 That way you'll learn about everything that can be expected.
这样你就可以了解可以预期的一切。
You might want to have a look at this article , which has a good list of several things to check. 您可能希望看一下这篇文章 ,其中列出了一些需要检查的好东西。 For example, what is your backlog set to when you .Listen() on your socket?
例如,当您在套接字上使用.Listen()时,您的待办事项是什么?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.