12

I am trying to send UDP packets with Android to a server written in C# on my computer. When I run the app on my phone, I get an illegal state exception. I think it may have something to do with performing network operations on the main activity, but I'm not sure how to resolve that problem. Here is my client:

public class MainActivity extends Activity {

WifiManager wifi;
InetAddress dev_ip;
final int serverPort = 31337;
Thread drawThread = new Thread(new drawer());

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    //set up wifi and connection
    wifi = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);
    WifiInfo info = wifi.getConnectionInfo();
    int ip = info.getIpAddress();        
    String ipaddr = (ip & 0xff) + "." + (ip >> 8 & 0xff) + "." + (ip >> 16 & 0xff) + "." + (ip >> 24 & 0xff);

    try {
        dev_ip = InetAddress.getByName(ipaddr);
    } catch (UnknownHostException e) {
        Toast.makeText(this, "host error", Toast.LENGTH_LONG).show();
    }

    if (!wifi.isWifiEnabled())
       wifi.setWifiEnabled(true);

    Toast.makeText(this, "IP: " + ipaddr, Toast.LENGTH_LONG).show();     
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.activity_main, menu);
    return true;
}

public void draw(View view) throws IOException, SocketException
{
    drawThread.start();
}

public class drawer implements Runnable {

    public void run() {

        //transmit data
        try {
            DatagramSocket socket = new DatagramSocket(serverPort, /*myip*/);
            String test_data = "It works!";
            byte btest[] = new byte[50];
            btest = test_data.getBytes();
            DatagramPacket p1 = new DatagramPacket(btest, btest.length, /*myip*/, serverPort);
            socket.send(p1);
            socket.close();
        }   
        catch (IOException e) {
        }
    }
}
}

LogCat:

07-27 00:10:17.155: D/CLIPBOARD(1711): Hide Clipboard dialog at Starting input: 

finished by someone else... !
07-27 00:10:18.020: W/System.err(1711): java.net.BindException: bind failed: EADDRNOTAVAIL (Cannot assign requested address)
07-27 00:10:18.020: W/System.err(1711):     at libcore.io.IoBridge.bind(IoBridge.java:89)
07-27 00:10:18.020: W/System.err(1711):     at java.net.PlainDatagramSocketImpl.bind(PlainDatagramSocketImpl.java:68)
07-27 00:10:18.020: W/System.err(1711):     at java.net.DatagramSocket.createSocket(DatagramSocket.java:133)
07-27 00:10:18.020: W/System.err(1711):     at java.net.DatagramSocket.<init>(DatagramSocket.java:95)
07-27 00:10:18.020: W/System.err(1711):     at com.ls.styloid.MainActivity$drawer.run(MainActivity.java:67)
07-27 00:10:18.025: W/System.err(1711):     at java.lang.Thread.run(Thread.java:856)
07-27 00:10:18.025: W/System.err(1711): Caused by: libcore.io.ErrnoException: bind failed: EADDRNOTAVAIL (Cannot assign requested address)
07-27 00:10:18.025: W/System.err(1711):     at libcore.io.Posix.bind(Native Method)
07-27 00:10:18.025: W/System.err(1711):     at libcore.io.ForwardingOs.bind(ForwardingOs.java:39)
07-27 00:10:18.025: W/System.err(1711):     at libcore.io.IoBridge.bind(IoBridge.java:87)
07-27 00:10:18.025: W/System.err(1711):     ... 5 more
07-27 00:10:42.090: D/CLIPBOARD(1711): Hide Clipboard dialog at Starting input: finished by someone else... !
07-27 00:11:30.150: W/System.err(2535): java.net.BindException: bind failed: EADDRNOTAVAIL (Cannot assign requested address)
07-27 00:11:30.155: W/System.err(2535):     at libcore.io.IoBridge.bind(IoBridge.java:89)
07-27 00:11:30.155: W/System.err(2535):     at java.net.PlainDatagramSocketImpl.bind(PlainDatagramSocketImpl.java:68)
07-27 00:11:30.155: W/System.err(2535):     at java.net.DatagramSocket.createSocket(DatagramSocket.java:133)
07-27 00:11:30.155: W/System.err(2535):     at java.net.DatagramSocket.<init>(DatagramSocket.java:95)
07-27 00:11:30.155: W/System.err(2535):     at com.ls.styloid.MainActivity$drawer.run(MainActivity.java:67)
07-27 00:11:30.155: W/System.err(2535):     at java.lang.Thread.run(Thread.java:856)
07-27 00:11:30.155: W/System.err(2535): Caused by: libcore.io.ErrnoException: bind failed: EADDRNOTAVAIL (Cannot assign requested address)
07-27 00:11:30.155: W/System.err(2535):     at libcore.io.Posix.bind(Native Method)
07-27 00:11:30.155: W/System.err(2535):     at libcore.io.ForwardingOs.bind(ForwardingOs.java:39)
07-27 00:11:30.155: W/System.err(2535):     at libcore.io.IoBridge.bind(IoBridge.java:87)
07-27 00:11:30.155: W/System.err(2535):     ... 5 more
07-27 00:11:36.515: D/CLIPBOARD(2535): Hide Clipboard dialog at Starting input: finished by someone else... !

EDIT: There seem to be numerous problems with the server that I didn't notice before. They started happening when I rewrote the listener according to one of the answers. I sometimes get a "Cannot access a disposed object" error with label3, a socket exception 0x80004005, and still no packets received. However, when checking the socket state it appears to be readable. I probably screwed up the threading, help me fix this please. Server:

public partial class Form1 : Form
    {
        Socket listener;
        Thread udp_listener;

        public Form1()
        {
            InitializeComponent();

            //set up listener thread
            udp_listener = new Thread(listen);
            udp_listener.IsBackground = true;
            udp_listener.Start();
        }

        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            base.OnFormClosing(e);
            listener.Close();
            udp_listener.Join();
        }

        private void listen()
        {
            //set up UDP
            const int serverPort = 31337;
            bool terminate = false;
            IPHostEntry iphost = Dns.GetHostEntry(Dns.GetHostName());
            IPAddress ipaddr = iphost.AddressList[0];
            IPEndPoint endpoint = new IPEndPoint(ipaddr, serverPort);
            listener = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            label3.Text = ipaddr.ToString();

            try
            {
                do
                {
                    byte[] buffer = new byte[100];
                    listener.Receive(buffer);
                    label3.Text = "Connected";
                    label3.ForeColor = Color.Red;
                    label3.Text = Encoding.UTF8.GetString(buffer);
                }
                while (!terminate);
            }
            catch (Exception e)
            {
                MessageBox.Show(e.ToString());
            }
            finally
            {
                listener.Close();
            }

            listener.Close();
        }
    }

EDIT2:

I tried making a client with C# on my computer. The packet was sent but my server did not receive anything.

EDIT3: Server works fine now, but the android app refuses to run. Here's the code:

   package com.tests.contest;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import android.app.Activity;

public class MainActivity extends Activity {
      private Socket sock;
      private BufferedWriter out;
      private Thread thrd;

      @Override
      public void onResume() {
        super.onResume();
        thrd = new Thread(new Runnable() {
            public void run() {
              while (!Thread.interrupted()) {
                runOnUiThread(new Runnable() {
                  @Override
                  public void run() {
                      try {
                        sock = new Socket("THEIP", 31337);
                    } catch (UnknownHostException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                    } catch (IOException e1) {
                        // TODO Auto-generated catch block
                        e1.printStackTrace();
                    }
                      try {
                        out = new BufferedWriter(new OutputStreamWriter(sock
                              .getOutputStream()));
                        out.write("WORKS");
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                  }
                });
              }
            }
          });
          thrd.start();
      }

      @Override
      public void onPause() {
        super.onPause();
        if (thrd != null)
          thrd.interrupt();
        try {
          if (sock != null) {
            sock.getOutputStream().close();
            sock.getInputStream().close();
            sock.close();
          }
        } catch (IOException e) {}
        thrd = null;
      }

      /*private void sendText() {
        String text = "HI";
        try {
          out.write(text + "\n");
          out.flush();
        } catch (IOException e) {}
      }*/
    }

The problem occurs because I'm running network operations on the main thread, which I am clearly not doing.

Lockhead
  • 2,423
  • 7
  • 35
  • 48
  • 2
    Rather than *guessing* what it's about, have you looked at the message in the exception? – Jon Skeet Jul 26 '12 at 19:40
  • 1
    "I think it may have something to do with performing network operations on the main activity..." - you shouldn't think anything of the kind! Check the logs. Also look here: http://stackoverflow.com/questions/3353023/android-illegalstateexception-when-is-it-thrown – paulsm4 Jul 26 '12 at 19:43
  • "It says just what I assumed.": Oscar Wilde said it best: [Spell "assume"](http://uncyclopedia.wikia.com/wiki/Assume) – paulsm4 Jul 26 '12 at 19:46
  • I said that I assumed it was because of executing network operations on the main thread. But I don't know how to solve it. Anyways, added LogCat. – Lockhead Jul 26 '12 at 19:50

2 Answers2

7

The exception is telling you exactly what to do: create the Socket object in a separate Thread. You can use an AsyncTask for this as well.

The reasoning behind not allowing Sockets on the main UI thread is that it can cause the app to get the dreaded Application Not Responding message from waiting for a Socket.

edit: http://thinkandroid.wordpress.com/2010/03/27/incorporating-socket-programming-into-your-applications/

You can just skip down to the client example since you already have a desktop server.

edit2: Since I happen to also be working on a C# server for my Android app, here is how my desktop app creates a listener Socket:

IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 27015); //Port 27015
Socket listener = new Socket(AddressFamily.InterNetwork,
    SocketType.Stream, ProtocolType.Tcp); // Create a TCP/IP socket.

I'm almost certain that the problem lies within your server/network. The Android code to send out a simple UDP packet looks correct. You might also want to try connecting over localhost or 127.0.0.1 by writing a simple C# client program.

edit 3:

With this basic app you should be able to connect to the server after you put the correct IP address in. Pressing Send will send some bytes to the server. Use this to make sure that your connection is working. I've confirmed that it works on my end. If this works, then I would open a new SO question for your server issues, otherwise something is wrong with your network configuration.

telkins
  • 10,440
  • 8
  • 52
  • 79
  • I read about AsyncTask, and it says that it's good only for short operations. I need a constant stream of data. – Lockhead Jul 26 '12 at 19:58
  • If you want a constant stream of data then just use a Thread. I'll try to find an example, although there are many online. – telkins Jul 26 '12 at 20:00
  • @Lockhead I found an example for you. – telkins Jul 26 '12 at 20:06
  • EDIT: I just noticed that the IP I got from WifiInfo does not match my computer's IP. What's going on here? – Lockhead Jul 26 '12 at 20:50
  • Why are you using dev_ip as the IP to send to? That should be the IP address of the server. I'm pretty sure that `WifiInfo.getIpAddress()` returns the IP address of your phone which is not what you want. Also, do you have the internet permission set? – telkins Jul 26 '12 at 20:58
  • @Lockhead You beat me to it. :P You should just manually type the IP address of the server in. Like I said in my previous comment, `WifiInfo` retrieves info about the phone itself. – telkins Jul 26 '12 at 21:00
  • Oh, I thought that it gives the wifi host's IP. I need to automate this task, however. Is there any way to get the correct IP automatically? And yes, I have the internet permission set. – Lockhead Jul 26 '12 at 21:07
  • Okay, typed in the IP manually for testing purposes. Using wireshark to detect traffic on the intended port, I see no packets sent. So it's still not working, and the problem isn't with my server. LogCat updated. – Lockhead Jul 26 '12 at 21:13
  • EADDRNOTAVAIL means that you are trying to connect to an address that doesn't exist. You're positive that the port/address is correct? And you are sure that you are connected to the same network as the desktop server (assuming you are using a DHCP supplied IP address)? How are you sure that the server is correct, have you verified it with other software? – telkins Jul 26 '12 at 21:27
  • I know the port is at least open because I can see it being listened on with netstat, and it's only being listened on when my server program is running, so there's no way the server is the problem because I can't see any activity with wireshark on that port. I typed my standard IP(the first one you see in ipconfig) and the port is being listened on 0.0.0.0, so it should accept it. My IP is static. – Lockhead Jul 26 '12 at 21:32
  • @Lockhead I made one last edit to my post. I'll check back on this tomorrow. – telkins Jul 26 '12 at 22:40
  • I do have problems in my server as it turns out. I get a System.Net.Sockets.SocketException 0x80004005. Sometimes I also get a "Cannot a disposed object" error. Although when I use the Poll method and check the socket's readability, it returns a positive value, but I still get an exception when I receive. Updated with the server code. – Lockhead Jul 26 '12 at 23:36
  • @Lockhead Sorry for forgetting about this. I would just try this and see if you can get it to work, don't worry about threading just yet. http://msdn.microsoft.com/en-us/library/6y0e13d3 You can use the asynchronous example if you prefer that instead. I would get the "Cannot access a disposed object" error when trying to use a Socket that has been disconnected and then GC'ed, so somewhere you are closing the Socket and then trying to access it again. – telkins Aug 01 '12 at 14:24
  • Thanks for the link, I'll check it out in a bit. I still need the threading though because I'm going to perform operations while I receive information for my app. – Lockhead Aug 01 '12 at 18:04
  • Still getting the same socket exception, even with msdn's code. – Lockhead Aug 03 '12 at 01:49
  • The exception is on listener.Receive(buffer), by the way. – Lockhead Aug 03 '12 at 01:58
  • The server works fine, but the android client refuses to work. I used a different example because the one you linked to doesn't work as well(network on main thread, and my efforts to create a thread resulted in an error). I get a null pointer exception for some mystical reason. I am fed up with Java already, it is simply terrible in every way. Updated with the code I'm trying to use now. – Lockhead Aug 04 '12 at 20:03
  • I managed to solve the null pointer exception, but now I'm getting another error. Question updated. – Lockhead Aug 04 '12 at 20:16
  • @Lockhead you are receiving the "running network operations on the main thread" because the Socket is created in the Activity, not the Thread object. So the object itself is still created on the UI thread. – telkins Aug 13 '12 at 20:00
  • I changed the code to create the socket inside the thread, and I'm still getting that error. And what can I do about the socket exception I'm getting with the server? – Lockhead Aug 14 '12 at 11:02
2

I think that the problem comes from the do while loop because you are attempting to modifiy a UI component ( label3 ) in the loop which is by the way a infinite loop sins the terminate variable is always false. Try getting the code witch modifies the UI ( label3.*) out of the loop.

zizoujab
  • 7,603
  • 8
  • 41
  • 72
  • Why can't I modify a UI component in a loop? I'll try what you said anyways though. – Lockhead Jul 29 '12 at 12:51
  • 1
    Just tried, doesn't work. As I said before, the problem probably lies in my android app because I am not detecting any traffic on the intended port with Wireshark. – Lockhead Jul 29 '12 at 12:56
  • 1
    because you are getting "Cannot access a disposed object" which it can be caused by modifying the UI in a non-ending loop. the code in android side looks correct to me just check to add **uses internet** permission . but what i dont understand is why you are making a loop in c# code : `listener.receive()` is blocking and already waits to response from android. what i suggest is remove the whole loop and add internet permission to android application and test. – zizoujab Jul 29 '12 at 13:21
  • 1
    I need that infinite loop, because I'm going to need to receive many packets and not just one. There is no response from the android app, just data sending. Even if without the infinite loop I will receive one packet, my problem won't be solved, because I need that infinite loop. And still, even if it does block it should still receive the test packet. By the way, I already did try what you suggested and nothing changed. Socket exceptions and no packets like usual. – Lockhead Jul 29 '12 at 15:19