2

I have simple TCP server written in Java, and I'm trying to write a simple TCP client for Android that will communicate with the TCP server running on the local machine.

I can get the server to receive messages, but oddly it is only receiving the message AFTER I stop the Application process through the "Devices" window in Eclipse.

On the Android client, I have the main UI thread which contains fields to enter the IP address and Port number(this all works fine so I am not including the code for it). When the connect button is clicked, it starts a new ASyncTask to do the TCP socket work.

Here is the code for the ConnectionTask class's doInBackground method:

    protected Void doInBackground(Void... params) 
    {   
    String authMessage = "Authorize";
    boolean connected=false;
    try 
    {           
        Socket clientSocket = new Socket();
        SocketAddress serverAddress = new InetSocketAddress(address,port);
        Log.v("Connection Thread", serverAddress.toString());
        clientSocket.connect(serverAddress, 15);
        if(clientSocket.isConnected())
        {
            connected = true;
            Log.v("Connection Thread", "Connection Successful");
            DataOutputStream outToServer = new DataOutputStream(clientSocket.getOutputStream());
            BufferedReader inFromServer = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            outToServer.writeBytes(authMessage);
            Log.v("Connection Thread", "Sent Auth Message");
            String response = inFromServer.readLine();

            Log.v("TCP Client", "Response: " + response);
            hash = computeHash(response.getBytes());
            //send the computed hash to the server
            outToServer.writeBytes(hash);
            outToServer.close();
            inFromServer.close();
            clientSocket.close();
        }
        else
        {
            Log.v("Connection Thread", "Not Connected yet...");
        }
    } 
    catch (Exception e) 
    {
        Log.v("Connection Thread", e.getMessage());
    }   
    return null;
}

When I press the connect button, in the emulator I see the logs up to "Sent Auth Message" but my server does not receive the message "Authorize" until I kill the process through DDMS.

Here is the server code:

    public class Server
{
       private static SecureRandom random = new SecureRandom();
       public static void main(String argv[]) throws Exception
       {
             String rawClientMessage,lcClientMessage;
             ServerSocket welcomeSocket = new ServerSocket(5000);
             System.out.println("Starting Server...");
             while(true)
             {
                Socket connectionSocket = welcomeSocket.accept();
                BufferedReader inFromClient = new BufferedReader(new InputStreamReader(connectionSocket.getInputStream()));
                DataOutputStream outToClient = new DataOutputStream(connectionSocket.getOutputStream());
                rawClientMessage = inFromClient.readLine();
                if(rawClientMessage!=null)
                {
                    lcClientMessage = rawClientMessage.toLowerCase() + '\n';
                    System.out.println("Received Message! Contents: " + rawClientMessage);
                    if(lcClientMessage=="authorize")
                    {
                        System.out.println("Received auth request message, generating key...");
                        String key = generateKey();
                        //send key back
                        outToClient.writeBytes(key);
                        System.out.println("Key sent!");
                    }
                }
                try
                {
                    inFromClient.close();
                    outToClient.close();
                    connectionSocket.close();
                }
                catch(Exception e)
                {
                    System.out.println(e.getMessage());
                }
             }
       }

       private static String generateKey()
       {
             return new BigInteger(130, random).toString(32);
       }
}

EDIT:

Here is a step by step summary of what happens:

  1. I start the server (it blocks waiting for connection and reads "Server started...")
  2. I type in 192.168.0.100 port 5000 into android client and hit "connect" button
  3. ASyncTask is created upon click and my socket code is listed above
  4. Nothing is received on server console yet...
  5. (I could press connect again and nothing would happen) (LogCat also reads up to the "Send Auth Message" log)
  6. I kill process on emulator through Eclipse devices window
  7. Server console prints: Received Message! Contents: Authorize (message is displayed twice if I clicked connect twice)

Basically it seems like it's storing the message and is letting them all out onto the wire after the close of the program.

Any help is appreciated, thanks! Can post more code including main UI thread and rest of ConnectionTask class which extends ASyncTask (if needed).

Thanks!

Josh
  • 451
  • 8
  • 22
  • I think you need to have another while loop within the current one for your server side for reading bytes from the accepted connection. – tartar Apr 16 '12 at 06:04
  • I know this may sound stupid, but is there any particular reason you can't just use HTTP or some other existing protocol to do the same thing? –  Apr 16 '12 at 06:24
  • @tartar - The message comes in fine so it's reading all the bytes in the message. 1. Start Server 2. The server blocks reading "Server started..." 3. I type in server info into client on Android emulator 4. Press connect 5. Nothing on server side... 6. I kill process in DDMS 7. The output on server reads: "Received Message! Contents: Authorize" – Josh Apr 16 '12 at 06:44
  • 1
    Solved. I switched to a PrintWriter and used println() instead of the DataOutputStream. Seems to work when I set autoflush to true in the PrintWriter instantiation: PrintWriter outToServer = new PrintWriter(clientSocket.getOutputStream(),true); – Josh Apr 16 '12 at 07:10
  • OT but after calling `connect()`, testing `isConnected()` on the next line is futile. If it wasn't, an exception would have been thrown. – user207421 Apr 16 '12 at 08:16
  • @Josh Good idea with the PrintWriter. When you call BufferedReader.readLine() server side it waits for a newline to terminate the read, but writeBytes() on the client never sends one. PrintWriter.println() does send a newline. Good work. – jarvisteve Apr 16 '12 at 17:47

3 Answers3

3

You are reading lines but you aren't writing lines. readLine() will block until it receives a line terminator, and you are never sending one. And as the data you are receiving is binary, you shouldn't be trying to read it with readLine() anyway. Reconsider your data streams.

user207421
  • 305,947
  • 44
  • 307
  • 483
2

writeBytes(message) writes the bytes to the output stream, but they aren't sent down the pipe until the stream is flushed. When you kill the app, the stream is closed, and before the stream gets closed it flushes itself.

I think if you add outToClient.flush() after the outToClient.writeBytes(authMessage) you'll be rockin'.

Jibran Khan
  • 3,236
  • 4
  • 37
  • 50
jarvisteve
  • 926
  • 8
  • 14
  • I assume you meant outToServer.flush(). I tried adding that--still the same result (only messages are sent when program process is stopped). – Josh Apr 16 '12 at 06:37
  • This answer is not correct. There is no buffering in a `DataOutputStream,` or in `Socket.getOutputStream()` either, and the OP has no `BufferedOutputStream` in between that would motivate this recommendation. – user207421 Mar 04 '13 at 22:54
  • @EJP Looking at the docs, specifically [flush for DataOutputStream](http://docs.oracle.com/javase/6/docs/api/java/io/DataOutputStream.html#flush()), you can see it does buffer data and that my answer is accurate. The OP is using a DataOutputStream. Bummer it didn't work, but it was a sound attempt. – jarvisteve Mar 12 '13 at 02:32
  • Looking at the docs you can see that all it does is call the flush() method of the underlying output stream. There is no buffering. – user207421 Jul 13 '13 at 08:13
1

You have to redirect your port if you run your app in emulator. Otherwise you can't communicate client with server. Take a look

Suvam Roy
  • 1,282
  • 2
  • 11
  • 21
  • but the server does receive the message, only after i exit the app process...? – Josh Apr 16 '12 at 06:53
  • I know that, your connection will establish and your client will send the message to server, but your server can't receive the message from the connected port. You have to redirect the port and the server receive the message from different port number. Take look from my hyperlink mentioned above. Which will help you how to redirect the port. – Suvam Roy Apr 16 '12 at 07:14
  • After run your emulator. type it in command prompt - "telnet localhost ", then type "redir add tcp:5000:6000". Connect client with port number 5000 and open tcp server with port number 6000. Then you can receive client message from tcp server. – Suvam Roy Apr 16 '12 at 07:19