"An existing connection was forcibly closed by the remote host" 在 UDP 服务器中接收
"An existing connection was forcibly closed by the remote host" in UDP Server on receiving
于是在与线程的斗争之后,我成功地在客户端和服务器之间建立了一个流。我的客户端是 Java Android Studio 应用程序,服务器是 C# 控制台。
我希望能够在客户端单击按钮时启动和停止流。然后随时重新启动它,停止,重新启动,停止..等等
我很适合开始,但是有时在第一次停止后,有时在第一次重启时,有时在第二次重启时,它会弹出 An existing connection was forcibly closed by the remote host
在 PacketReceived = serverSocket.Receive(ref client);
我不知道为什么,我正在努力调试它,但没有任何生产力。我的逻辑或语法有什么问题?
C# 服务器代码:
class UdpServer
{
static void Main(string[] args)
{
// variables declaration block
byte[] PacketReceived = new byte[1024];
var MyToken = new CancellationTokenSource(); //create token for the thread cancel
UdpClient serverSocket = new UdpClient(15000);
string PacketMessage = "";
int i = 0;
while (true) // this while for keeping the server "listening"
{
Console.WriteLine("Waiting for a UDP client..."); // display stuff
IPEndPoint client = new IPEndPoint(IPAddress.Any, 0); // prepare
PacketReceived = serverSocket.Receive(ref client); // receive packet
PacketMessage = Encoding.ASCII.GetString(PacketReceived, 0, PacketReceived.Length); // get string from packet
Console.WriteLine("Response from " + client.Address); // display stuff
Console.WriteLine("Message " + i++ + ": " + PacketMessage + "\n"); // display received string
if (PacketMessage == "Start")
{
MyToken = new CancellationTokenSource(); // for the restart, need a new token
Task.Run(() => Start(ref serverSocket, ref client), MyToken.Token); //start method on another thread
}
if (PacketMessage == "Stop")
{
MyToken.Cancel();
}
}
}
static public void Start(ref UdpClient serverSocket, ref IPEndPoint client)
{
int i = 0;
byte[] dataToSend;
while (true)
{
try
{
dataToSend = Encoding.ASCII.GetBytes(i.ToString());
serverSocket.Send(dataToSend, dataToSend.Length, client);
i++;
}
catch (Exception e)
{ }
}
}
}
Java 客户代码:
public class MainActivity extends AppCompatActivity {
String message;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button StrtBtn = (Button) findViewById(R.id.StartButton);
Button StpBtn = (Button) findViewById(R.id.StopButton);
// Start Button Click
StrtBtn.setOnClickListener(
new Button.OnClickListener() {
public void onClick(View v) {
message = "Start";
SendUdpMsg();
}
}
);
// Stop Button Click
StpBtn.setOnClickListener(
new Button.OnClickListener() {
public void onClick(View v) {
message = "Stop";
}
}
);
}
public void SendUdpMsg()
{
Thread networkThread = new Thread() {
// No local Host 127.0.0.1 in Android
String host = "192.168.200.3"; // Server's IP
int port = 15000;
DatagramSocket dsocket = null;
public void run() {
try {
// Get the Internet address of the specified host
InetAddress address = InetAddress.getByName(host);
// wrap a packet
DatagramPacket packetToSend = new DatagramPacket(
message.getBytes(),
message.length(),
address, port);
// Create a datagram socket, send the packet through it.
dsocket = new DatagramSocket();
dsocket.send(packetToSend);
// Here, I am receiving the response
byte[] buffer = new byte[65535]; // prepare
DatagramPacket packetReceived = new DatagramPacket(buffer, buffer.length); // prepare
while (true)
{
if(message == "Start")
{
dsocket.receive(packetReceived); // receive packet
byte[] buff = packetReceived.getData(); // convert packet to byte[]
final String Response = new String(buffer, 0, packetReceived.getLength());
runOnUiThread(new Runnable()
{
@Override
public void run()
{
// this is executed on the main (UI) thread
final TextView TextOne = (TextView)findViewById(R.id.StatusText);
TextOne.setText(Response);
}
});
}
else
{
// wrap a packet to send the Stop
packetToSend = new DatagramPacket(
message.getBytes(),
message.length(),
address, port);
// Create a datagram socket, send the packet through it.
dsocket = new DatagramSocket();
dsocket.send(packetToSend);
break; // break the whole thread
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
networkThread.start();
}
}
如果没有完整的代码示例(特别是要测试的真实客户端),很难诊断出这样的虚假错误。但是,您的代码至少有两个明显的问题,其中任何一个 都可能 如果它们碰到错误的东西,就会导致这样的错误:
- 您正在通过对
Start()
方法的引用传递 serverSocket
和 client
。没有必要这样做,通过引用传递 client
引入了在错误时间修改 client
变量的可能性,导致尝试发送到 Any
,或者导致非法引用(如果在 x64 架构上 运行)。这是最基本的问题,即在没有某种同步的情况下在线程之间共享变量 - 至少使用 volatile
- 会带来自己的一系列问题(并且您在客户端代码中有相同的线程安全问题,因为您使用 message
字段作为线程间信号的方式。
- 您从未使用取消标记来停止线程。将令牌传递给
Task.Run()
方法仅允许 Task
class 正确处理可能发生的任何取消异常。但是在适当的时候 抛出 该异常取决于您自己的代码。你永远不会这样做,这意味着在来自客户端的多条消息之后,你最终会遇到多个线程试图发送给客户端。
我打赌上面的第二个。您的客户端似乎将端口选择留给主机,因此每次发送 "Start" 方法时,客户端端口可能会更改。一旦先前使用的套接字关闭,该端口将无效,如果远程套接字试图发送到该端口,则会导致远程套接字出错。当然你的服务器(远程套接字)将尝试发送到那个端口,导致套接字上的错误,可以被Receive()
方法(而不是您可能期望的 Send()
方法)。
顺便说一下,不管它值多少钱,您应该避免将线程池用于 long-运行 任务。至少,如果您要使用 Task.Run()
启动您的线程,请通过传递适当的 TaskCreationOptions.LongRunning 值向 .NET 表明您的任务是 long-运行:
于是在与线程的斗争之后,我成功地在客户端和服务器之间建立了一个流。我的客户端是 Java Android Studio 应用程序,服务器是 C# 控制台。
我希望能够在客户端单击按钮时启动和停止流。然后随时重新启动它,停止,重新启动,停止..等等
我很适合开始,但是有时在第一次停止后,有时在第一次重启时,有时在第二次重启时,它会弹出 An existing connection was forcibly closed by the remote host
在 PacketReceived = serverSocket.Receive(ref client);
我不知道为什么,我正在努力调试它,但没有任何生产力。我的逻辑或语法有什么问题?
C# 服务器代码:
class UdpServer
{
static void Main(string[] args)
{
// variables declaration block
byte[] PacketReceived = new byte[1024];
var MyToken = new CancellationTokenSource(); //create token for the thread cancel
UdpClient serverSocket = new UdpClient(15000);
string PacketMessage = "";
int i = 0;
while (true) // this while for keeping the server "listening"
{
Console.WriteLine("Waiting for a UDP client..."); // display stuff
IPEndPoint client = new IPEndPoint(IPAddress.Any, 0); // prepare
PacketReceived = serverSocket.Receive(ref client); // receive packet
PacketMessage = Encoding.ASCII.GetString(PacketReceived, 0, PacketReceived.Length); // get string from packet
Console.WriteLine("Response from " + client.Address); // display stuff
Console.WriteLine("Message " + i++ + ": " + PacketMessage + "\n"); // display received string
if (PacketMessage == "Start")
{
MyToken = new CancellationTokenSource(); // for the restart, need a new token
Task.Run(() => Start(ref serverSocket, ref client), MyToken.Token); //start method on another thread
}
if (PacketMessage == "Stop")
{
MyToken.Cancel();
}
}
}
static public void Start(ref UdpClient serverSocket, ref IPEndPoint client)
{
int i = 0;
byte[] dataToSend;
while (true)
{
try
{
dataToSend = Encoding.ASCII.GetBytes(i.ToString());
serverSocket.Send(dataToSend, dataToSend.Length, client);
i++;
}
catch (Exception e)
{ }
}
}
}
Java 客户代码:
public class MainActivity extends AppCompatActivity {
String message;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button StrtBtn = (Button) findViewById(R.id.StartButton);
Button StpBtn = (Button) findViewById(R.id.StopButton);
// Start Button Click
StrtBtn.setOnClickListener(
new Button.OnClickListener() {
public void onClick(View v) {
message = "Start";
SendUdpMsg();
}
}
);
// Stop Button Click
StpBtn.setOnClickListener(
new Button.OnClickListener() {
public void onClick(View v) {
message = "Stop";
}
}
);
}
public void SendUdpMsg()
{
Thread networkThread = new Thread() {
// No local Host 127.0.0.1 in Android
String host = "192.168.200.3"; // Server's IP
int port = 15000;
DatagramSocket dsocket = null;
public void run() {
try {
// Get the Internet address of the specified host
InetAddress address = InetAddress.getByName(host);
// wrap a packet
DatagramPacket packetToSend = new DatagramPacket(
message.getBytes(),
message.length(),
address, port);
// Create a datagram socket, send the packet through it.
dsocket = new DatagramSocket();
dsocket.send(packetToSend);
// Here, I am receiving the response
byte[] buffer = new byte[65535]; // prepare
DatagramPacket packetReceived = new DatagramPacket(buffer, buffer.length); // prepare
while (true)
{
if(message == "Start")
{
dsocket.receive(packetReceived); // receive packet
byte[] buff = packetReceived.getData(); // convert packet to byte[]
final String Response = new String(buffer, 0, packetReceived.getLength());
runOnUiThread(new Runnable()
{
@Override
public void run()
{
// this is executed on the main (UI) thread
final TextView TextOne = (TextView)findViewById(R.id.StatusText);
TextOne.setText(Response);
}
});
}
else
{
// wrap a packet to send the Stop
packetToSend = new DatagramPacket(
message.getBytes(),
message.length(),
address, port);
// Create a datagram socket, send the packet through it.
dsocket = new DatagramSocket();
dsocket.send(packetToSend);
break; // break the whole thread
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
networkThread.start();
}
}
如果没有完整的代码示例(特别是要测试的真实客户端),很难诊断出这样的虚假错误。但是,您的代码至少有两个明显的问题,其中任何一个 都可能 如果它们碰到错误的东西,就会导致这样的错误:
- 您正在通过对
Start()
方法的引用传递serverSocket
和client
。没有必要这样做,通过引用传递client
引入了在错误时间修改client
变量的可能性,导致尝试发送到Any
,或者导致非法引用(如果在 x64 架构上 运行)。这是最基本的问题,即在没有某种同步的情况下在线程之间共享变量 - 至少使用volatile
- 会带来自己的一系列问题(并且您在客户端代码中有相同的线程安全问题,因为您使用message
字段作为线程间信号的方式。 - 您从未使用取消标记来停止线程。将令牌传递给
Task.Run()
方法仅允许Task
class 正确处理可能发生的任何取消异常。但是在适当的时候 抛出 该异常取决于您自己的代码。你永远不会这样做,这意味着在来自客户端的多条消息之后,你最终会遇到多个线程试图发送给客户端。
我打赌上面的第二个。您的客户端似乎将端口选择留给主机,因此每次发送 "Start" 方法时,客户端端口可能会更改。一旦先前使用的套接字关闭,该端口将无效,如果远程套接字试图发送到该端口,则会导致远程套接字出错。当然你的服务器(远程套接字)将尝试发送到那个端口,导致套接字上的错误,可以被Receive()
方法(而不是您可能期望的 Send()
方法)。
顺便说一下,不管它值多少钱,您应该避免将线程池用于 long-运行 任务。至少,如果您要使用 Task.Run()
启动您的线程,请通过传递适当的 TaskCreationOptions.LongRunning 值向 .NET 表明您的任务是 long-运行: