英文:
How can I keep connection to server using socket?
问题
我有连接到服务器。连接成功后,我可以通过CMD的netstat命令netstat -a -n
看到连接。但大约21秒后,连接从ESTABLISHED
更改为CLOSE_WAIT
。我想要连接始终保持在ESTABLISHED
状态。我该怎么做?
这是netstat命令的结果,第一次和第二次的结果。
TCP 192.168.10.12:50148 10.10.10.120:3000 ESTABLISHED
TCP 192.168.10.12:50148 10.10.10.120:3000 CLOSE_WAIT
这是我的C#代码片段:
private void _connect()
{
var IpEndPoint = new IPEndPoint(IPAddress.Parse("10.10.10.120"), 3000);
var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
client.SendTimeout = 60;
client.ReceiveTimeout = 60;
try
{
client.Connect(IpEndPoint);
int byteCount = 0;
byte[] bytes = new byte[256];
while (true)
{
byteCount = client.Receive(bytes);
if (byteCount == 0) //没有从服务器接收任何数据,客户端将向服务器发送虚拟数据
{
byte[] data_send = Encoding.ASCII.GetBytes("Hi, dummy data");
client.Send(data_send);
}
else //从服务器接收数据,将其写入日志文件。
{
byte[] byte_rec = new byte[byteCount];
Buffer.BlockCopy(bytes, 0, byte_rec, 0, byteCount);
txt_log.Text = Encoding.ASCII.GetString(byte_rec);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
我尝试了多次调试,但它只能运行几秒钟,然后连接就会从ESTABLISHED
更改为CLOSE_WAIT
。上面的代码从不显示异常消息。
与此同时,我尝试了使用TcpClient
的其他选项:
private void _connect()
{
TcpClient client1 = new TcpClient();
try
{
client1.Connect("10.10.10.120", 3000);
NetworkStream stream1 = client1.GetStream();
var bytes = new byte[256];
while (true)
{
int numbyte = stream1.Read(bytes, 0, bytes.Length);
if (numbyte == 0)
{
byte[] data_send = Encoding.ASCII.GetBytes("Hi, dummy data");
stream1.Write(data_send, 0, data_send.Length);
}
else
{
byte[] byte_rec = new byte[numbyte];
Buffer.BlockCopy(bytes, 0, byte_rec, 0, numbyte);
txt_log.Text = Encoding.ASCII.GetString(byte_rec);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
使用TcpClient的这段代码,连接仍然保持大约20秒,之后连接就会出现,不会显示ESTABLISHED
或CLOSE_WAIT
,当我使用netstat命令检查时。但是它会出现异常消息:无法从传输连接中读取数据:主机计算机中的软件中断了已建立的连接
。
到目前为止,我已经尝试了一些选项并改进了我的代码。然而,当我调试时,我可以看到流的属性DataAvailable
在第一次循环时为true。之后,第二次循环,它始终为false。如果不设置断点并运行此代码,它可以保持连接超过10分钟。但是当我在这一行设置断点numberOfBytesRead = stream.Read(myReadBuffer, 0, myReadBuffer.Length);
,它只在第一次循环时运行,没有下一次循环,因为流变为false。从第一次循环到第二次循环的时间延迟约为52秒。之后,连接就会中断。我收到这个异常:'无法从传输连接中读取数据:主机计算机中的软件中断了已建立的连接。'
。您能告诉我为什么流在第一次循环后从true变为false吗?这种情况让我的代码出现问题。
英文:
I have connection to the server. After connected successfully, I can see the connection via CMD netstat command netstat -a -n
. But after about 21 seconds, the connection changing from ESTABLISHED
to CLOSE_WAIT
. I want to the connection always is ESTABLISHED
. How can I do it?
This is from netstat command, the result in the first time and second time.
TCP 192.168.10.12:50148 10.10.10.120:3000 ESTABLISHED
TCP 192.168.10.12:50148 10.10.10.120:3000 CLOSE_WAIT
This is my snipped code in C#
private void _connect()
{
var IpEndPoint = new IPEndPoint(IPAddress.Parse("10.10.10.120"), 3000);
var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
client.SendTimeout = 60;
client.ReceiveTimeout = 60;
try
{
client.Connect(IpEndPoint);
int byteCount = 0;
byte[] bytes = new byte[256];
while (true)
{
byteCount = client.Receive(bytes);
if (byteCount == 0) //not receive anything from server, client will send dummy data to server
{
byte[] data_send = Encoding.ASCII.GetBytes("Hi, dummy data");
client.Send(data_send);
}
else //receive data from server, write it to log files.
{
byte[] byte_rec = new byte[byteCount];
Buffer.BlockCopy(bytes, 0, byte_rec, 0, byteCount);
txt_log.Text = Encoding.ASCII.GetString(byte_rec);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
I try debug more time, but it only can be run few seconds, after that the connection will be changed from ESTABLISHED
to CLOSE_WAIT
. The code above never show exception message.
Meantime, i have other option using TcpClient
private void _connect()
{
TcpClient client1 = new TcpClient();
try
{
client1.Connect("10.10.10.120", 3000);
NetworkStream stream1 = client1.GetStream();
var bytes = new byte[256];
while (true)
{
int numbyte = stream1.Read(bytes, 0, bytes.Length);
if (numbyte == 0)
{
byte[] data_send = Encoding.ASCII.GetBytes("Hi, dummy data");
stream1.Write(data_send, 0, data_send.Length);
}
else
{
byte[] byte_rec = new byte[numbyte];
Buffer.BlockCopy(bytes, 0, byte_rec, 0, numbyte);
txt_log.Text = Encoding.ASCII.GetString(byte_rec);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
With this code using Tcpclient, the connection still about 20 seconds, after that the connection will be appear, not show ESTABLISHED
or CLOSE_WAIT
when i use netstat command to check. But it have exception message is Unable to read data from the transport connection: An established connection was aborted by the software in your host machine
.
So far, I found some option and enhancement my code. However, when I debug, I can see stream with properties DataAvailable
is true at the first time loop. After that, the second time loop, it's always is false. If don't put break point and run with this code, it's can keep connection about more than 10min. But when I put break point at this line numberOfBytesRead = stream.Read(myReadBuffer, 0, myReadBuffer.Length);
, it only run at the first time loop, without next time because stream is false. The time delay when run first time to second time loop about 52s. After that, the connection is break. I get this exception : 'Unable to read data from the transport connection: An established connection was aborted by the software in your host machine.'
. Can you tell me why stream changed from true to false after first time loop ?. This way stuck and let my code is wrong.
private async void _connect()
{
var serverEndpoint = new IPEndPoint(IPAddress.Parse(_ip), _port);
client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
client.Connect(serverEndpoint);
client.NoDelay = true;
client.SendTimeout = 60000;
client.ReceiveTimeout = 60000;
byte[] myReadBuffer = new byte[1024];
int numberOfBytesRead = 0;
bool _flag = false;
while (true)
{
stream = new NetworkStream(client); //create stream based on socket
if (stream.CanRead)
{
do
{
if (_flag)
{
numberOfBytesRead = 0;
goto receiveskip;
}
if (numberOfBytesRead == 0) //send dummy data to server first
{
byte[] data_dummy = Encoding.ASCII.GetBytes("dummy_abc");
stream.Write(data_dummy, 0, data_dummy.Length);
stream.Flush();
}
numberOfBytesRead = stream.Read(myReadBuffer, 0, myReadBuffer.Length);
byte[] bytes_rec = new byte[numberOfBytesRead];
Buffer.BlockCopy(myReadBuffer, 0, bytes_rec, 0, numberOfBytesRead);
//client receive data from server at the first time, after sent dummy_abc to server
if(bytes_rec.Length == 14)
{
txt_log.Text += Encoding.ASCII.GetString(bytes_rec); //write to textbox
}
//receive data from server at second time and so on.
//After server get data from client and reply first time, server will send data
//server will send data with bytes lengh more 14
if (bytes_rec.Length > 14)
{
byte[] data_rep = Encoding.ASCII.GetBytes("Ok, thank server"); //client reply to server
stream.Write(data_rep, 0, data_rep.Length);
stream.Flush();
_flag = true;
continue;
}
}
while (stream.DataAvailable);
}
else
{
MessageBox.Show("Cannot read from this NetworkStream.");
}
}
}
catch (Exception ex)
{
stream.Close();
client.Close();
MessageBox.Show(ex.Message);
}
}
答案1
得分: 1
以下是翻译好的部分:
- 使用
TcpClient
而不是原始套接字,它更容易。 - 您需要处理您的套接字。
- 您的注释
//not receive anything from server, client will send dummy data to server
没有意义。读取没有超时,因此它将永远阻塞,直到由于没有数据而关闭套接字,在那时您将收到0
。也许您需要一个超时? - TCP KeepAlive消息的最小超时为2小时,因此可能不值得去处理它们。服务器会更早地关闭连接。
- 请注意,单个
Send
不一定会导致单个Read
。它可能以任何数量的方式合并或拆分。 - 使用
async
await
,因为这将使您的用户界面响应。
private async Task _connect()
{
try
{
var IpEndPoint = new IPEndPoint(IPAddress.Parse("10.10.10.120"), 3000);
using var (client = new TcpClient())
{
client.SendTimeout = 60;
client.ReceiveTimeout = 60;
await client.ConnectAsync(IpEndPoint);
using (var stream = client.GetStream())
{
await Read(stream);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private async Task Read(NetworkStream stream)
{
int byteCount = 0;
byte[] bytes = new byte[256];
while (true)
{
try
{
using (var cts = new CancellationTokenSource(someTimeoutHere))
{
byteCount = await stream.Read(bytes, 0, bytes.Length, cts.Token);
if (byteCount == 0) // 套接字已关闭
return;
txt_log.Text = Encoding.ASCII.GetString(bytes, 0, byteCount);
}
}
catch (OperationCanceledException)
// 未从服务器接收任何内容,客户端将向服务器发送虚拟数据
{
var bytesToWrite = Encoding.ASCII.GetBytes("Hi, dummy data");
await stream.WriteAsync(bytesToWrite, 0, bytesToWrite.Length);
}
}
}
英文:
You have a multitude of issues here:
- Use
TcpClient
instead of raw sockets, it's much easier. - You need to dispose your socket.
- Your comment
//not receive anything from server, client will send dummy data to server
doesn't make sense. The Read does not have a timeout, so it will just block forever, until the socket gets closed due to no data, at which point you will receive0
. Perhaps you need a timeout? - TCP KeepAlive messages have a minimum timeout of 2 hours, so it's probably not worth messing with them. The server is closing the connection far earlier.
- Be aware that a single
Send
does not necessarily result in a singleRead
. It may be merged or split in any number of ways. - Use
async
await
as this will keep your UI responsive.
private async Task _connect()
{
try
{
var IpEndPoint = new IPEndPoint(IPAddress.Parse("10.10.10.120"), 3000);
using var (client = new TcpClient())
{
client.SendTimeout = 60;
client.ReceiveTimeout = 60;
await client.ConnectAsync(IpEndPoint);
using (var stream = client.GetStream())
{
await Read(stream);
}
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private async Task Read(NetworkStream stream)
{
int byteCount = 0;
byte[] bytes = new byte[256];
while (true)
{
try
{
using (var cts = new CancellationTokenSource(someTimeoutHere))
{
byteCount = await stream.Read(bytes, 0, bytes.Length, cts.Token);
if (byteCount == 0) // socket was closed
return;
txt_log.Text = Encoding.ASCII.GetString(bytes, 0, byteCount);
}
}
catch (OperationCanceledException)
//not receive anything from server, client will send dummy data to server
{
var bytesToWrite = Encoding.ASCII.GetBytes("Hi, dummy data");
await stream.WriteAsync(bytesToWrite, 0, bytesToWrite.Length);
}
}
}
答案2
得分: 1
可以确认您的问题是服务器关闭了连接,导致客户端套接字的状态从ESTABLISHED
变为CLOSE_WAIT
。通常情况下,如果状态变为CLOSE_WAIT
,它会立即关闭自己(也就是说,您在netstat中看不到此连接),并导致Socket.Receive
和Socket.Send
抛出异常。但是,您告诉我您的第一段代码没有任何异常,我只能推测该方法在某个地方被阻塞,以至于套接字无法关闭。当然,这需要您进行调试,以了解是哪一行出现了问题。
**因此,关键问题是:为什么服务器关闭了您的连接?**由于您没有提供服务器代码,您需要自行找到答案。
最后,关键点是比较您更新的第三段代码与前两段的不同之处。最大的区别在于,在新代码中在读取之前发送了一些数据,因此我可以大胆猜测,您的服务器需要客户端在连接后发送一些数据才能正常工作(换句话说,服务器也在首先调用Receive
)。
无论如何,请检查您的服务器代码。
英文:
It can be confirmed that your problem is that the server closed the connection, causing the status of the client socket to change from ESTABLISHED
to CLOSE_WAIT
. Usually, if the status becomes CLOSE_WAIT
, it will immediately close itself (that is, you cannot see this connection in netstat) and cause Socket.Receive
and Socket.Send
to throw an exception. However, you told me that your first piece of code did not have any exceptions, and I can only speculate that this method has been blocked somewhere so that the socket cannot be closed. Of course, this requires you to debug to know which line it is.
So the key problem is: Why does the server close your connection? Since you didn't provide the server code, you will need to find the answer yourself.
Finally, the key point is to compare your updated third piece of code with the previous first and second pieces. The biggest difference is that you sent some data before reading in the new code, so I can boldly guess that your server needs the client to send some data after connecting to it in order to function properly (in other word the server is also calling Receive
first).
Anyway please check your server code.
答案3
得分: 0
好的,以下是翻译好的部分:
好的,在更多时间的调试和改进之后,我自己解决了问题。我不确定你们是否遇到了相同的问题,但是目前对我来说一切都运行正常。关键在于,只需查看最终的代码并做一些更改。像下面这样,感谢所有的建议。
private async void _connect()
{
var serverEndpoint = new IPEndPoint(IPAddress.Parse(_ip), _port);
var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
client.SendTimeout = 180000;
client.ReceiveTimeout = 180000;
await client.ConnectAsync(serverEndpoint);
byte[] myReadBuffer = new byte[1024];
int numberOfBytesRead = 0;
bool _flag = false;
var stream = new NetworkStream(client);
while (true)
{
if (_flag)
{
numberOfBytesRead = 0;
goto receiveskip;
}
if (numberOfBytesRead == 0) //send dummy data to server first
{
byte[] data_dummy = Encoding.ASCII.GetBytes("dummy_abc");
await stream.WriteAsync(data_dummy, 0, data_dummy.Length);
await stream.FlushAsync();
_flag = true;
}
receiveskip:
numberOfBytesRead = await stream.ReadAsync(myReadBuffer, 0, myReadBuffer.Length);
byte[] bytes_rec = new byte[numberOfBytesRead];
Buffer.BlockCopy(myReadBuffer, 0, bytes_rec, 0, numberOfBytesRead);
//client receive data from server at the first time, after sent dummy_abc to server
if (bytes_rec.Length == 14)
{
txt_log.Text += Encoding.ASCII.GetString(bytes_rec); //write to textbox
_flag = true;
goto receiveskip;
}
//receive data from server at second time and so on.
//After server get data from client and reply first time, server will send data
//server will send data with bytes length more than 14
if (bytes_rec.Length > 14)
{
byte[] data_rep = Encoding.ASCII.GetBytes("Ok, thank server"); //client reply to server
await stream.WriteAsync(data_rep, 0, data_rep.Length);
await stream.FlushAsync();
_flag = true;
goto receiveskip;
}
if (bytes_rec.Length == 0)
{
break;
}
}
}
catch (Exception ex)
{
client.Close();
MessageBox.Show(ex.Message);
}
}
英文:
Ok, after more time to debug and enhancement, I get my resolve by myself, I'm not sure if you all get same issue, but it working well until now for me. The key in here , just look at the finally code and changed something. Like this below, thank for all advices.
private async void _connect()
{
var serverEndpoint = new IPEndPoint(IPAddress.Parse(_ip), _port);
var client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
try
{
client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
client.SendTimeout = 180000;
client.ReceiveTimeout = 180000;
await client.ConnectAsync(serverEndpoint);
byte[] myReadBuffer = new byte[1024];
int numberOfBytesRead = 0;
bool _flag = false;
var stream = new NetworkStream(client);
while (true)
{
if (_flag)
{
numberOfBytesRead = 0;
goto receiveskip;
}
if (numberOfBytesRead == 0) //send dummy data to server first
{
byte[] data_dummy = Encoding.ASCII.GetBytes("dummy_abc");
await stream.WriteAsync(data_dummy, 0, data_dummy.Length);
await stream.FlushAsync();
_flag = true;
}
receiveskip:
numberOfBytesRead = await stream.ReadAsync(myReadBuffer, 0, myReadBuffer.Length);
byte[] bytes_rec = new byte[numberOfBytesRead];
Buffer.BlockCopy(myReadBuffer, 0, bytes_rec, 0, numberOfBytesRead);
//client receive data from server at the first time, after sent dummy_abc to server
if(bytes_rec.Length == 14)
{
txt_log.Text += Encoding.ASCII.GetString(bytes_rec); //write to textbox
_flag = true;
goto receiveskip;
}
//receive data from server at second time and so on.
//After server get data from client and reply first time, server will send data
//server will send data with bytes lengh more 14
if (bytes_rec.Length > 14)
{
byte[] data_rep = Encoding.ASCII.GetBytes("Ok, thank server"); //client reply to server
await stream.WriteAsync(data_rep, 0, data_rep.Length);
await stream.FlushAsync();
_flag = true;
goto receiveskip;
}
if (bytes_rec.Length == 0)
{
break;
}
}
}
catch (Exception ex)
{
client.Close();
MessageBox.Show(ex.Message);
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论