c# UDP数据包中继器:重新启动将无法工作

本文关键字:工作 重新启动 UDP 数据包 中继器 | 更新日期: 2023-09-27 18:09:04

长话短说:我需要一个UDP数据包中继器/重发器。它应该做什么?嗯,它应该在特定的端口上监听传入的数据包,并在指定的端口上发送它们。该程序有一个GUI,其中有一个用于运行和关闭程序的按钮。

程序工作得很好,直到我开始…我们去他妈的。

慢慢地,我设法把大部分的虫子弄出来了。除了一个……或更多:

c# udp套接字通信-每个套接字地址(协议/网络地址/端口)通常只允许使用一次

问题是,如果我想重新启动程序,通过点击按钮,我得到相同的错误在上面的链接。

我试图实现Shutdown(), Dispose(), Disconnect(), UDP_Client = null,也许更多,我已经搜索,但它产生了一个新的错误,或没有效果。

using System;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Threading;
namespace Packet_Repeater
{
    public partial class Form1 : Form
    {
        public bool connection_status = false;
        UdpClient[]     UDP_Client;
        IPEndPoint[]    IP_End_Point;
        IPAddress[][]   IP_Address;
        Thread[]        Task;
        public int[] upd_client_src_port, udp_client_dst_port;
        public bool[] packet_rxd;
        public bool[] task_running;
        public byte[][] BUFFER;
        public int[,] UDP_Client_num;
    public Form1()
    {
        InitializeComponent();
    }
    public void Init_Size(int num)
    {
        UDP_Client              = new UdpClient[num];
        IP_End_Point            = new IPEndPoint[num];
        IP_Address              = new IPAddress[num][];
        Task                    = new Thread[num];
        upd_client_src_port     = new int[num];
        udp_client_dst_port     = new int[num];
        packet_rxd              = new bool[num];
        BUFFER                  = new byte[num][];
        UDP_Client_num          = new int[num,2];
        task_running            = new bool[num];
    }
    public void UDP_Init(int client_num, string target_ip, int src_port, int dst_port)
    {
        upd_client_src_port[client_num] = src_port;
        udp_client_dst_port[client_num] = dst_port;
        BUFFER[client_num] = new byte[1536];
        if(target_ip == "Any")
        {
            IP_End_Point[client_num] = new IPEndPoint(IPAddress.Any, udp_client_dst_port[client_num]);
        }
        else
        {
            IP_Address[client_num] = Dns.GetHostAddresses(target_ip);
            IP_End_Point[client_num] = new IPEndPoint(IP_Address[client_num][0], udp_client_dst_port[client_num]);
        }
        UDP_Client[client_num] = new UdpClient(upd_client_src_port[client_num]);
        UDP_Client[client_num].Client.SendTimeout       =   500;
        UDP_Client[client_num].Client.ReceiveTimeout    =   500;
    }
    public void Run_Client(int listner_client_num, int destination_client_num)
    {
        task_running[listner_client_num] = true;
        while (connection_status)
        {
            try
            {
                BUFFER[listner_client_num] = UDP_Client[listner_client_num].Receive(ref IP_End_Point[listner_client_num]);
                UDP_Client[destination_client_num].Send(BUFFER[listner_client_num], BUFFER[listner_client_num].Length, IP_End_Point[destination_client_num]);
            }
            catch (Exception) { }
        }
        Array.Clear(BUFFER[listner_client_num], 0, BUFFER[listner_client_num].Length);
// Thread.Sleep(10000);
        UDP_Client[listner_client_num].Close();
        task_running[listner_client_num] = false;
    }
    public void button_Click(object sender, EventArgs e)
    {
        int number_of_connections = 2;
        if(!connection_status)
        {
            button.Enabled = false;
            connection_status = true;
            Init_Size(number_of_connections);
            UDP_Client_num[0, 0] = 0;
            UDP_Client_num[0, 1] = 1;
            UDP_Client_num[1, 0] = 1;
            UDP_Client_num[1, 1] = 0;
            UDP_Init(0, "Any", 9700, 9701);
            UDP_Init(1, "Any", 9600, 9601);
// the Problem is in this for() cycle
            for (UInt32 i = 0; i < (number_of_connections-1); i++)
            {
                Task[i] = new Thread(ThreadStart => Run_Client(UDP_Client_num[i, 0], UDP_Client_num[i, 1]));
                Task[i].Start();
            }
/*   This works OK:
myTask[0] = new Thread(ThreadStart => Run_Client(UDP_Client_num[0, 0], UDP_Client_num[0, 1]));
                myTask[0].Start();
                myTask[1] = new Thread(ThreadStart => Run_Client(UDP_Client_num[1, 0], UDP_Client_num[1, 1]));
                myTask[1].Start();
*/

      button.Text = "Close";
            button.Enabled = true;
        }
        else
        {
            connection_status = false;
            button.Enabled = false;
            for (int i = 0; i < (number_of_connections-1);)
            {
                if(!task_running[i])
                {
                    i++;
                }
            }
            button.Text = "Run";
            button.Enabled = true;
        }
        }
    }
}

如果我在正确的轨道上,基本上我的UDP客户端没有正确关闭,对吗?

我注意到的另一个奇怪的错误是在Run_Client函数的while循环之后。Sleep()命令未运行。它似乎跳过了那部分。这是怎么发生的?

谁能告诉我我哪里搞砸了?

更新:我一直在寻找系统中的bug,发现了这个:在浏览时,我在命令提示符上发现了这个有用的命令:

netstat -a -b -n- p udp -o

这帮助我发现,最后一个客户根本没有关闭。因此,我在Run_Client()的开头和结尾添加了一个写入文本框的函数。由于某些原因,一个Task[]无法启动。所以这原来是一个线程问题,而不是NetSocket。

c# UDP数据包中继器:重新启动将无法工作

问题和解决方法都很简单。由于某些原因,这一行出现了线程问题:

for (int i = 0; i < (number_of_connections); i++)
{
  Task[i] = new Thread(ThreadStart => Run_Client(UDP_Client_num[i, 0], UDP_Client_num[i, 1]));
  Task[i].Start();
  while(!task_running[i])// <<-- This is probably a better Solution
  {
    Thread.Sleep(1);
  }
  // Thread.Sleep(100);   // <<-- This Solved the Problem
}

由于我不明白的原因,有时(并非总是)For()循环中的Task[0]会被Task[1]覆盖。所以Task[0]没有启动,而是有两个Task[1]-s在运行。在for()循环之前,UDP侦听器/客户端已经初始化,因此端口被程序打开/占用。但是因为Task[0]根本没有运行,所以当我按下GUI上的关闭按钮时,它的客户端不会关闭。因此,当我再次按下按钮,重新启动应用程序时,弹出错误消息,因为代码中指定的端口仍在使用中。

按照jdweng的建议,我在程序中添加了一些调试语句,这对找到错误的来源有很大帮助。另一个有用的工具是命令提示符中的命令:

netstat -a -n -p udp -o

显示所有使用的udp端口。

解决方案是在for()周期中设置主线程休眠一段时间。我把它设置为100毫秒,但也许更少也可以。(还没试过)