"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();
    }
}

如果没有完整的代码示例(特别是要测试的真实客户端),很难诊断出这样的虚假错误。但是,您的代码至少有两个明显的问题,其中任何一个 都可能 如果它们碰到错误的东西,就会导致这样的错误:

  1. 您正在通过对 Start() 方法的引用传递 serverSocketclient。没有必要这样做,通过引用传递 client 引入了在错误时间修改 client 变量的可能性,导致尝试发送到 Any,或者导致非法引用(如果在 x64 架构上 运行)。这是最基本的问题,即在没有某种同步的情况下在线程之间共享变量 - 至少使用 volatile - 会带来自己的一系列问题(并且您在客户端代码中有相同的线程安全问题,因为您使用 message 字段作为线程间信号的方式。
  2. 您从未使用取消标记来停止线程。将令牌传递给 Task.Run() 方法仅允许 Task class 正确处理可能发生的任何取消异常。但是在适当的时候 抛出 该异常取决于您自己的代码。你永远不会这样做,这意味着在来自客户端的多条消息之后,你最终会遇到多个线程试图发送给客户端。

我打赌上面的第二个。您的客户端似乎将端口选择留给主机,因此每次发送 "Start" 方法时,客户端端口可能会更改。一旦先前使用的套接字关闭,该端口将无效,如果远程套接字试图发送到该端口,则会导致远程套接字出错。当然你的服务器(远程套接字)尝试发送到那个端口,导致套接字上的错误,可以Receive() 方法(而不是您可能期望的 Send() 方法)。


顺便说一下,不管它值多少钱,您应该避免将线程池用于 long-运行 任务。至少,如果您要使用 Task.Run() 启动您的线程,请通过传递适当的 TaskCreationOptions.LongRunning 值向 .NET 表明您的任务是 long-运行: