SocketException An existing connection was forcibly closed by the remote host

一曲冷凌霜 提交于 2019-12-06 06:32:01

问题


I've decided to take a look at network messaging etc and my first port of call was UDP.

The problem i have is when i attempt to send a message. I'm trying to hit an IP on a specifc port, but the application errors with the error

"SocketException An existing connection was forcibly closed by the remote host".

Here is the code.

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


}

thanks for the help.

Not sure if this helps but...

The Application is running on machine which is set to listen is on 192.168.0.25:5555

the Send is trying to send to Machine 192.168.0.50:10001

T

From further reading, I think my specific issue is the creation of the UDPclient object. It is created and is then listening on the ip 192.168.0.25:5555. When i attempt to send a message I'm attempting to use the same UDPClient but sending to a new IP. I'm getting the vibe that this is not the correct procedure and thus its trying to close the previous down??? I'm sure someone can comfirm this. So that would suggest to me that to have effective UDP networking (UP and Down) i'd need to have a UDPclient receiving and a second UDP to be able to send (which is dynamic to each target address i want to hit). Once again this is all guess work, and if i have this i hope someone could provide some pointers.


回答1:


Several problems above.

  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);
    

If you only had the host name, you would do:

    string hostName = "MyComputerName";
    int port = 5555;   // or int.Parse(txt_Port.Text);   ???
    IPHostEntry hostEntry = Dns.GetHostAddresses(hostName);
    IPEndPoint endPoint = new IPEndPoint(hostEntry[0], port);

Don't use Dns.GetHostEntry() -- if there's no reverse-lookup (PTR) record for that name, it will fail. Use Dns.GetHostAddresses(). And I have no idea why you think you needed the loop unless you have IPv6 addresses for the same host name you were providing. If not, just use [0] - it should be the first and only IP that will be returned that you'll be using if not using IPv6. But again, since you already have the IP, just plug that in - no need to do the lookup. It helps you eliminate Dns.GetHostName(), too - just use 192.168.0.25.

For your reference, MSDN has a procedure for your synchronous socket send/receive here: https://msdn.microsoft.com/en-us/library/kb5kfec7(v=vs.110).aspx

and asynchronous socket send/receive here:
http://msdn.microsoft.com/en-us/library/bew39x2a(v=vs.110).aspx

Since I despise link only answers, and you seem to be mixing these 2 methods by using Send() and EndReceive(), respectively to the links above, I will endeavor to describe its contents succinctly and help fix your code:

Basically they say to use a StateObject class, instead of the global variable MyUDPClient you have:

public class StateObject
{
    public byte[] buffer = new byte[1024];
    public Socket workSocket; 
    public StringBuilder sb = new StringBuilder();
}

You would create a socket and add it to that. Buffer serves as a way to tell the Receive() or EndReceive() the size of the chunk you want to read back from the response at one time, and sb will serve as a placeholder for the response.

It looks like you have a lot going on here: a SendtoTarget_Click to do a one-off test from the form, and your listener. Your SendtoTarget button does a synchronous send, your StartUDP_Click() does an asynchronous receive.

You would change your SendtoTarget_Click() event to be this (which never defined your socket, before, and you must connect to it before sending):

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...
    }
}

For your listener, you can do:

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

and your function called ReceiveMessage(), does this:

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

Did my best to integrate your code with MSDN's in a way that should work. You might be able to get away with assigning that socket to the StateObject and calling BeginReceive() on it, again - not sure. But don't re-use the UdpClient object like that. Use the StateObject class like on MSDN and use it ONLY as your listener.



来源:https://stackoverflow.com/questions/17790612/socketexception-an-existing-connection-was-forcibly-closed-by-the-remote-host

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!