SocketException:远程主机强制关闭了一个已经存在的连接。

3

我决定研究网络消息传递等内容,我的第一个调查对象是UDP。

问题在于当我尝试发送一条消息时遇到了困难。我正在尝试连接到特定端口的IP地址,但应用程序会报错:

"SocketException 远程主机强制关闭了一个现有的连接"。

以下是代码。

    User ME = new User();
    UdpClient MyUDPClient;

    private void Form1_Load(object sender, EventArgs e)
    {
        ME.Username = Environment.UserName;
    }

    private void StartUDP_Click(object sender, EventArgs e)
    {
        CreateUDPClient();
    }

    private void CreateUDPClient()
    {
        IPHostEntry host = Dns.GetHostEntry(Dns.GetHostName());
        foreach (IPAddress ip in host.AddressList)
        {
            if (ip.AddressFamily == AddressFamily.InterNetwork)
            {
                int Port = int.Parse(txt_Port.Text);
                ME.UserIP = new IPEndPoint(ip, Port);
                break;
            }
        }

        MyUDPClient = new UdpClient(ME.UserIP);
        UDPListening();
    }


    public void UDPListening()
    {
        MyUDPClient.BeginReceive(ReceiveMessage, new object());
    }

    private void ReceiveMessage(IAsyncResult IAR)
    {
        byte[] B = MyUDPClient.EndReceive(IAR, ref ME.UserIP);
        ProcessMSG(Encoding.ASCII.GetString(B));
        UDPListening();
    }

    delegate void MessageDelegate(String MSG);

    public void ProcessMSG(String M)
    {
        if (this.lbl_Messages.InvokeRequired)
        {
            MessageDelegate Del = new MessageDelegate(ProcessMSG);
            this.Invoke(Del, M);
        }
        else
        {
            lbl_Messages.Text = M;
        }
    }



   //Send Data to Another version of this program elsewhere.

    private void btn_SendtoTarget_Click(object sender, EventArgs e)
    {
        IPEndPoint TargetIP = new IPEndPoint(IPAddress.Parse(txt_Send2IP.Text),int.Parse(txt_Send2Port.Text));
        byte[] Message = Encoding.ASCII.GetBytes("TEST TEST TEST");

        MyUDPClient.Send(Message, Message.Length, TargetIP);
    }


}

感谢您的帮助。

不确定是否有用,但是...

应用程序正在运行在监听192.168.0.25:5555的机器上

发送方试图发送到192.168.0.50:10001的机器上

T

通过进一步阅读,我认为我的问题在于创建UDPClient对象。它被创建并在ip 192.168.0.25:5555上进行监听。当我尝试发送消息时,我尝试使用相同的UDPClient,但是发送到一个新的IP地址。我感觉这不是正确的程序流程,因此它正在尝试关闭先前的连接吗? 我确定有人能够确认这一点。因此,这会提示我要实现有效的UDP网络(向上和向下),我需要有一个UDP客户端接收和第二个UDP客户端能够发送(动态发送到每个目标地址)。再次强调,这都是猜测,如果我有这个,我希望有人能够提供一些指导。


哪行发生了错误,错误是什么?更新你的问题。 - Gray
这是代码行 MyUDPClient.BeginReceive(ReceiveMessage, new object()); - user2607250
在编程套接字时,了解底层的网络技术和可能出现的问题是很有用的,这样你就不必在遇到第一个错误时放弃。这个错误本身可能有各种原因,但通常表示存在防火墙的阻碍。 - CodeCaster
嗨,codecaster,我已经更新了主要问题,并提供了额外的信息,因为它对于评论来说太长了。 - user2607250
1个回答

3
上述存在几个问题。
  1. Don't use ME in C#, it is this. Me is VB. The code above would not compile, as written, if using C#, since you left out your User() class you define as ME. I am not sure why you think you need that, since all it seems to hold is UserIP and UserName. The IP is part of a Socket, so you should just use that if you ever needed it:
    IPAddress ip = IPAddress.Parse(socket.RemoteEndPoint);
    And UserName could be made a global string variable - doesn't have to be part of a class.
    Maybe I'm being nitpicky, but I didn't see where it was required to have either of those variables in the User() object like that and just mucked things up, for me, trying to decipher the code.
  2. Always define your Socket, connect to it, then do your .Send(). You are missing the first 2 steps in your button click - you just do your .Send(). UdpClient is not a Socket, and I think it's better to do this as a Socket and define your connection type later. I think you could be right that you are trying to do your .Send() using the UdpClient you defined as your listener. Don't use the same object for your send and your listening! Normally you would, but you have 2 different addresses and ports for each event.
  3. Don't use a foreach in your CreateUDPClient() function to get your IPAddress to assign to your IPEndPoint. You already knew your IP. Assign it directly. It's wasting processing time.

    IPAddress ip = IPAddress.Parse("192.168.0.25");
    IPEndPoint endPoint = new IPEndPoint(ip, port);
    
如果你只有主机名,你需要执行以下操作:
    string hostName = "MyComputerName";
    int port = 5555;   // or int.Parse(txt_Port.Text);   ???
    IPHostEntry hostEntry = Dns.GetHostAddresses(hostName);
    IPEndPoint endPoint = new IPEndPoint(hostEntry[0], port);

不要使用 Dns.GetHostEntry() -- 如果该名称没有反向查找 (PTR) 记录,它将失败。请使用 Dns.GetHostAddresses()。除非您提供的主机名有 IPv6 地址,否则我不知道为什么您认为需要循环。如果没有,只需使用 [0] - 它应该是返回的第一个且唯一的 IP,如果不使用 IPv6,则将使用它。但是,由于您已经有了 IP,请直接使用它 - 不需要进行查找。这还可以帮助您消除 Dns.GetHostName(),只需使用 192.168.0.25
供您参考,MSDN 在此处提供了同步套接字发送/接收的过程:https://msdn.microsoft.com/en-us/library/kb5kfec7(v=vs.110).aspx,以及异步套接字发送/接收的过程:http://msdn.microsoft.com/en-us/library/bew39x2a(v=vs.110).aspx
由于我不喜欢仅包含链接的回答,并且您似乎正在混合使用这两种方法,分别使用上述链接中的 Send()EndReceive(),我将努力简洁地描述其内容并帮助修复您的代码:
基本上,它们建议使用一个名为 StateObject 的类,而不是您现有的全局变量 MyUDPClient
public class StateObject
{
    public byte[] buffer = new byte[1024];
    public Socket workSocket; 
    public StringBuilder sb = new StringBuilder();
}

您需要创建一个套接字并将其添加到其中。缓冲区作为一种方式,告诉Receive()EndReceive()您要一次从响应中读取的块的大小,而sb将用作响应的占位符。
看起来您在这里有很多事情要做:一个SendtoTarget_Click用于从表单进行一次性测试,以及您的监听器。您的SendtoTarget按钮执行同步发送,而StartUDP_Click()执行异步接收。
您需要更改SendtoTarget_Click()事件,使其变成这样(之前从未定义过套接字,并且在发送之前必须连接到它):
private void btn_SendtoTarget_Click(object sender, EventArgs e)
{
    IPEndPoint TargetIP = new IPEndPoint(IPAddress.Parse(txt_Send2IP.Text),int.Parse(txt_Send2Port.Text));
    byte[] Message = Encoding.ASCII.GetBytes("TEST TEST TEST");

    // Create a UDP socket.  
    Socket sender = new Socket(AddressFamily.InterNetwork,  
        SocketType.Stream, ProtocolType.Udp);  

    try 
    {            
        // Connect to the remote endpoint.
        sender.Connect(TargetIP);  

        // Send message -- already contains the endpoint so no need to 
        // specify again
        sender.Send(Message, 0, Message.Length, SocketFlags.None);

        sender.Close();
    }
    catch (Exception)
    {
       // do something here...
    }
}

对于您的听众,您可以进行以下操作:

private void CreateUDPClient()
{
    IPEndPoint TargetIP = new IPEndPoint(IPAddress.Parse("192.168.0.25"), 5555);

    // Create a UDP socket.  
    Socket receiver = new Socket(AddressFamily.InterNetwork,  
            SocketType.Stream, ProtocolType.Udp);  

    try {  

        // Create the state object.  
        StateObject state = new StateObject();  
        state.workSocket = receiver;  

        // Begin receiving the data from the remote device.  
        receiver.BeginReceive(state.buffer, 0, 256, 0,  
            new AsyncCallback(ReceiveMessage), state);  

    } catch (Exception e) {  
        Console.WriteLine(e.ToString());  
    }  
}

你的名为ReceiveMessage()的函数,执行以下操作:

private void ReceiveMessage(IAsyncResult IAR)
{
    string response = String.Empty;
    try {  

        // Retrieve the state object and the client socket   
        // from the asynchronous state object.  
        StateObject state = (StateObject) IAR.AsyncState;  
        Socket client = state.workSocket;  

        // Read data from the remote device.  
        int bytesRead = client.EndReceive(IAR);  

        if (bytesRead > 0) {  
            // There might be more data, so store the data received so far.  
            state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));  

            // Get the rest of the data.  
            client.BeginReceive(state.buffer, 0, 256, 0,  
                new AsyncCallback(ReceiveCallback), state);  
        } else {  
            // All the data has arrived; put it in response.  
            if (state.sb.Length > 1) {  
                response = state.sb.ToString();  
            }  
            // Signal that all bytes have been received.  
            client.Close();  
        }  
        ProcessMSG(response);
        CreateUDPClient();  // personally, I would re-create than calling BeginReceive directly

    } catch (Exception e) {  
        Console.WriteLine(e.ToString());  
    }  
}

我尽力把你的代码与MSDN的代码整合起来,以确保它能正常工作。你可能可以把该套接字分配给StateObject并在其上调用BeginReceive(),但不确定是否可行。但是不要像那样重复使用UdpClient对象。像MSDN一样使用StateObject类,并将其仅用作监听器。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接