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。
问题和解决方法都很简单。由于某些原因,这一行出现了线程问题:
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毫秒,但也许更少也可以。(还没试过)