taichi
taichi

Reputation: 683

About Unity Error : "SocketException: Only one usage of each socket address"

I want to distribute the following code to users to receive UDP:

UDPReceive.cs

using UnityEngine;
using System.Collections;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

public class UDPReceive : MonoBehaviour
{
    int LOCA_LPORT = 22222;
    static UdpClient udp;
    Thread thread;

    void Start ()
    {
        udp = new UdpClient(LOCA_LPORT);
        udp.Client.ReceiveTimeout = 1000;
        thread = new Thread(new ThreadStart(ThreadMethod));
        thread.Start(); 
    }

    void Update ()
    {
    }

    void OnApplicationQuit()
    {
        thread.Abort();
    }

    private static void ThreadMethod()
    {
        while(true)
        {
            IPEndPoint remoteEP = null;
            byte[] data = udp.Receive(ref remoteEP);
            string text = Encoding.ASCII.GetString(data);
            Debug.Log(text);
        }
    } 
}

https://qiita.com/nenjiru/items/8fa8dfb27f55c0205651

However, if the user attaches this file to more than one object, an error is issued.

Error message is as follows.

"SocketException: Only one usage of each socket address"

System.Net.Sockets.Socket.Bind (System.Net.EndPoint local_end)
System.Net.Sockets.UdpClient.InitSocket (System.Net.EndPoint localEP)
System.Net.Sockets.UdpClient..ctor (Int32 port)
UDPReceive.Start () (at Assets/UDPReceive.cs:16)

If "I" only use this file, it is no problem if I make sure I don't attach it to multiple objects.

However, when used by "other users", users may wonder why the error occurs.

So, when a user encounters this error, I want to give a message saying "This script must not attach to more than one object."

How can I do such a thing?

Upvotes: 0

Views: 2756

Answers (1)

derHugo
derHugo

Reputation: 90659

The why the error occurs is quite self-explanatory and I think you already got the problem: You can listen on one port only once. A second script would need to use another port.


How to avoid it and give a proper warning/error?

There are a lot of options.

  • One might be e.g. to check how many are there in a scene using FindObjectsOfType like e.g.

    private void Awake()
    {
        var instances = FindObjectsOfType<UDPReceive>();
    
        if(instances.Length > 1)
        {
            Debug.LogError("Found multiple instances of UDPReceive! Only one is allowed!");
        }
    }
    

    this you could actually also already do in Reset which is called as soon as the component is attached to an object. This way you could already show the error before PlayMode is even started!

  • Another one would be a classical Singleton-Pattern

    // as this is static it is "shared" between all instances or
    // better said it is not bound to any instance at all
    private static UDPReceive instance;
    
    private void Awake()
    {
        // check if another instance already exists
        if(instance && instance != this)
        {
            Debug.LogError("There is already another instance of UDPReceive in the scene! Only one is allowed!", this);
            Debug.LogError("That's true, I am the active instance for this scene", instance);
    
            this.enabled = false;
            return;
        }
    
        // otherwise become the instance yourself
        instance = this;
    }
    
  • You could also catch the exception and show your own custom message like e.g.

    void Start ()
    {
        try
        {
            udp = new UdpClient(LOCA_LPORT);
            udp.Client.ReceiveTimeout = 1000;
            thread = new Thread(new ThreadStart(ThreadMethod));
            thread.Start();
        }
        // If any SocketException is thrown while starting the UDP client
        // it is most probably due to the port already being in use
        catch(SocketException e)
        {
            Debug.LogError("UDP cliente has a SocketException." 
               + "/nThis is probably caused by the target port already being used by something else." 
               + "/nMake sure you have only one instance of UDPReceive in your scene and try again", this);
        } 
    }
    

As a side note: It actually would be possible to use this multiple times in the same scene by simply allowing the developer to choose a different port:

//                                | probably a typo btw ;)
//                                v 
[SerializeField] private int LOCA_LPORT = 22222;

This still uses 22222 as default but now someone could also use a custom one. (You should then ofcourse rename it to comply with the c# coding conventions)

Upvotes: 1

Related Questions