1

I am currently trying to send a message from my Android-Smartphone to a raspberry pi. I implemented an Android-Client by the example code given here:

Really simple TCP client

I changed the ip-address an port so it works with my server running on the pi. This server is written in c and before switching to android, I implemented a C-Client which worked so far without any problems.

The server just needs to receive a message and then it prints it and stops, so it is very simple.

EDIT 3

I created new outputs for debugging and analysis, I may got an idea now what is not working. As far as I can see, the SendMessageTask is never executed.

The Server outputs are now:

Success - create socket

Success - Binding

Listening

Success - Accept

now it stalls, "reading..." is never printed! (should happen in the while loop)

In Android I receive the following neccessary outputs:

D/TCP Client: C: Connecting...

D/TcpOptimizer: TcpOptimizer-ON

--> Connection happened

When I push the Send button:

D/myMessage1: button pushed

D/myMessage: testing

But I do not get the messages which should be logged in the SendMessageTask

When I click the disconnect button the server prints:

Client disconnected

And now Android logs:

D/RESPONSE FROM SERVER: S: Received Message: 'null'

D/sendMessageTask: try to send message

D/TcpClient: Cannot send --> mBufferOut is null

Therefore my guess is, the SendMessageTask never runs until it fails due to disconnection, but why?

Edit 3 - End

This is the (edited) server code:

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>


int main(){
    int welcomeSocket, newSocket, read_size;
    char buffer[10000];
    struct sockaddr_in serverAddr;
    struct sockaddr_storage serverStorage;
    socklen_t addr_size;

    /*---- Create the socket. The three arguments are: ----*/
    /* 1) Internet domain 2) Stream socket 3) Default protocol (TCP in this case) */
    welcomeSocket = socket(AF_INET, SOCK_STREAM, 0);
    if (welcomeSocket == -1)    {
        printf("Could not create socket\n");
    }else {
        printf("Success - create socket\n");
    }

    /*---- Configure settings of the server address struct ----*/
    serverAddr.sin_family = AF_INET;
    serverAddr.sin_port = htons(7891);
    serverAddr.sin_addr.s_addr = INADDR_ANY;
    /* Set all bits of the padding field to 0 */
    memset(serverAddr.sin_zero, '\0', sizeof serverAddr.sin_zero);

    /*---- Bind the address struct to the socket ----*/
    if(bind(welcomeSocket, (struct sockaddr *) &serverAddr, sizeof(serverAddr)) < 0){
        printf("Error - Binding\n");
    }else {
        printf("Success - Binding\n");
    }

    /*---- Listen on the socket, with 5 max connection requests queued ----*/
    if(listen(welcomeSocket,5)==0)
        printf("Listening\n");
    else
        printf("Error\n");

    /*---- Accept call creates a new socket for the incoming connection ----*/
    addr_size = sizeof serverStorage;
    newSocket = accept(welcomeSocket, (struct sockaddr *) &serverStorage, &addr_size);
    if(newSocket < 0){
        printf("Error - Accept\n");
    }else {
        printf("Success - Accept\n");
    }

    while( (read_size = recv(newSocket, buffer, 10000, 0)) > 0){
        write(newSocket, buffer, strlen(buffer));
        printf("reading...");
    }

    if(read_size == 0){
        printf("Client disconnected");
        fflush(stdout);
    }
    else if(read_size == -1){
        printf("Error - recv failed");
    }
    return 0;
}

I am not quite sure if the parameters in

welcomeSocket = socket(AF_INET, SOCK_STREAM, 0);

are still correct?

The Client-Code in Android is:

import android.util.Log;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;

public class TcpClient {

    public static final String TAG = TcpClient.class.getSimpleName();
    public static final String SERVER_IP = "192.168.4.1"; //server IP address
    public static final int SERVER_PORT = 7891;
    // message to send to the server
    private String mServerMessage;
    // sends message received notifications
    private OnMessageReceived mMessageListener = null;
    // while this is true, the server will continue running
    private boolean mRun = false;
    // used to send messages
    private PrintWriter mBufferOut;
    // used to read messages from the server
    private BufferedReader mBufferIn;

    /**
     * Constructor of the class. OnMessagedReceived listens for the messages received from server
     */
    public TcpClient(OnMessageReceived listener) {
        mMessageListener = listener;
    }

    /**
     * Sends the message entered by client to the server
     *
     * @param message text entered by client
     */
    public void sendMessage(final String message) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                if (mBufferOut != null) {
                    Log.d(TAG, "Sending: " + message);
                    mBufferOut.println(message);
                    mBufferOut.flush();
                }else{
                    Log.d(TAG, "Cannot send --> mBufferOut is null");
                }
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();
    }

    /**
     * Close the connection and release the members
     */
    public void stopClient() {

        mRun = false;

        if (mBufferOut != null) {
            mBufferOut.flush();
            mBufferOut.close();
        }

        mMessageListener = null;
        mBufferIn = null;
        mBufferOut = null;
        mServerMessage = null;
    }

    public void run() {

        mRun = true;

        try {
            //here you must put your computer's IP address.
            InetAddress serverAddr = InetAddress.getByName(SERVER_IP);

            Log.d("TCP Client", "C: Connecting...");

            //create a socket to make the connection with the server
            Socket socket = new Socket(serverAddr, SERVER_PORT);

            try {

                //sends the message to the server
                mBufferOut = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);


                //receives the message which the server sends back
                mBufferIn = new BufferedReader(new InputStreamReader(socket.getInputStream()));


                //in this while the client listens for the messages sent by the server
                while (mRun) {
                    /*
                    mServerMessage = mBufferIn.readLine();

                    if (mServerMessage != null && mMessageListener != null) {
                        //call the method messageReceived from MyActivity class
                        mMessageListener.messageReceived(mServerMessage);
                    }
*/
                }

                Log.d("RESPONSE FROM SERVER", "S: Received Message: '" + mServerMessage + "'");

            } catch (Exception e) {
                Log.e("TCP", "S: Error", e);
            } finally {
                //the socket must be closed. It is not possible to reconnect to this socket
                // after it is closed, which means a new socket instance has to be created.
                socket.close();
            }

        } catch (Exception e) {
            Log.e("TCP", "C: Error", e);
        }

    }

    //Declare the interface. The method messageReceived(String message) will must be implemented in the Activity
    //class at on AsyncTask doInBackground
    public interface OnMessageReceived {
        public void messageReceived(String message);
    }

}

And my (edited) Mainactivity including the AsyncTask for connection is

import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.BottomNavigationView;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Button;

public class MyActivity extends AppCompatActivity {

    private TextView mTextMessage;
    private TextView myAwesomeTextView;

    private TcpClient mTcpClient;

    private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
            = new BottomNavigationView.OnNavigationItemSelectedListener() {

        @Override
        public boolean onNavigationItemSelected(@NonNull MenuItem item) {
            switch (item.getItemId()) {
                case R.id.navigation_home:
                    mTextMessage.setText(R.string.title_home);
                    return true;
                case R.id.navigation_dashboard:
                    mTextMessage.setText(R.string.title_dashboard);
                    return true;
                case R.id.navigation_notifications:
                    mTextMessage.setText(R.string.title_notifications);
                    return true;
            }
            return false;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_myActivity);

        mTextMessage = (TextView) findViewById(R.id.message);
        BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
        navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);

        myAwesomeTextView = (TextView)findViewById(R.id.editText);

        //TCP connect
        new ConnectTask().execute("");

        //Buttons
        final Button button_Send = findViewById(R.id.button_Send);
        button_Send.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {

                //Set text (debugging)
                myAwesomeTextView.setText("My Awesome Text");
                Log.d("myMessage1", "button pushed ");

                //sends the message to the server
               String message = "testing\0";
                //String message = "testing\n\r";

                if (mTcpClient != null) {
                    new SendMessageTask().execute(message);

                    //not needed just for debugging
                    Log.d("myMessage", "testing ");
                }

            }
        });

        final Button button_Disconnect = findViewById(R.id.button_Disconnect);
        button_Disconnect.setOnClickListener(new View.OnClickListener() {
            public void onClick(View v) {

                if (mTcpClient != null) {
                    mTcpClient.stopClient();
                }

            }
        });
    }

    @Override
    protected void onPause() {
        super.onPause();

        if (mTcpClient != null) {
            // disconnect
            new DisconnectTask().execute();
        }

    }

    /**
     * Sends a message using a background task to avoid doing long/network operations on the UI thread
     */
    public class SendMessageTask extends AsyncTask<String, Void, Void> {

        @Override
        protected Void doInBackground(String... params) {

            // send the message
            mTcpClient.sendMessage(params[0]);

            Log.d("sendMessageTask", "try to send message ");


            return null;
        }

        @Override
        protected void onPostExecute(Void nothing) {
            super.onPostExecute(nothing);

            //nothing to do yet
        }
    }

    /**
     * Disconnects using a background task to avoid doing long/network operations on the UI thread
     */
    public class DisconnectTask extends AsyncTask<Void, Void, Void> {

        @Override
        protected Void doInBackground(Void... voids) {

            // disconnect
            mTcpClient.stopClient();
            mTcpClient = null;

            return null;
        }

        @Override
        protected void onPostExecute(Void nothing) {
            super.onPostExecute(nothing);

            //nothing to do yet
        }
    }

    public class ConnectTask extends AsyncTask<String, String, TcpClient> {

        @Override
        protected TcpClient doInBackground(String... message) {

            //we create a TCPClient object and
            mTcpClient = new TcpClient(new TcpClient.OnMessageReceived() {
                @Override
                //here the messageReceived method is implemented
                public void messageReceived(String message) {
                    //this method calls the onProgressUpdate
                    publishProgress(message);
                }
            });
            mTcpClient.run();

            return null;
        }

        @Override
        protected void onProgressUpdate(String... values) {
            super.onProgressUpdate(values);

            Log.d("test", "response " + values[0]);
            //process server response here....
        }
    }
}

EDIT #2 (failing behaviour still remains)

  • uncommented while(mRun) Loop in run-method of TcpClient --> thought maybe the socket is closed by finally before the message has been sent.

  • moved the initial connection into onCreate(), so the button will just handle the sending and the connection is established before.

  • removed closing the socket after sending the message

Community
  • 1
  • 1
Dubbox
  • 191
  • 1
  • 2
  • 20
  • You forget to check for errors in most of the server program, including checking for "connection closed" (which I believe what's happening here). And you don't terminate the "string" you read. – Some programmer dude Jan 31 '19 at 13:09
  • Thank you for your response, where in my code shall i check for connection closed? Like after printing the message? Terminating the string means "\n"? Sorry I am very new to this stuff. – Dubbox Jan 31 '19 at 13:27
  • In C a `char` string is really called ***null-terminated** byte string*. You need to add that *null-terminator* to the string you read. As for the other problem, read the manual pages for all the functions in the C source. And a couple of network-programming tutorials. They should all tell you everything you need to know. – Some programmer dude Jan 31 '19 at 13:30
  • Okay thank you I will provide an answer or further questions when I have done so. – Dubbox Jan 31 '19 at 13:39
  • I think I should have applied all the changes you have mentioned, but still the output remains without the sent text message. I also provided the output which android gives me, I do not see anything going wrong here? The server just prints an empty message and shuts down, just as he did receive an empty string. I am not quite sure if "\0" means the same thing in "Java" as it does in c? But my guess was, that a c-server reads it so it may work? – Dubbox Feb 01 '19 at 08:09
  • You have no idea how many bytes you actually receive with your `recv()` call. `recv()` returns a value for a reason. You need to use it. – Andrew Henle Feb 01 '19 at 11:06
  • I think I implemented what you suggested, but when you see Edit 3, you can see that it is propably the Android Client which fails to send. So recv is never receiving anything at all? – Dubbox Feb 02 '19 at 11:22

1 Answers1

2

I managed to got this working.

I changed the line

new SendMessageTask().execute(message);

to

new SendMessageTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, message);

This got the sending working.

I also changed the server code to this:

#define MAX 2048
#define PORT 7891
#define SA struct sockaddr

// Function designed for chat between client and server.
void communicate(int sockfd)
{
    char buff[MAX];
    int n;
    // infinite loop for chat
    for (;;) {
        bzero(buff, MAX);

        // read the message from client and copy it in buffer
        read(sockfd, buff, sizeof(buff));
        // print buffer which contains the client contents
        printf("From client: %s\t To client : ", buff);
        bzero(buff, MAX);
        n = 0;
        // copy server message in the buffer
        while ((buff[n++] = getchar()) != '\n');

        // and send that buffer to client
        write(sockfd, buff, sizeof(buff));

        // if msg contains "Exit" then server exit and chat ended.
        if (strncmp("exit", buff, 4) == 0) {
            printf("Server Exit...\n");
            break;
        }

    }
}

// Driver function
int main()
{
    int sockfd, connfd, len;
    struct sockaddr_in servaddr, cli;

    // socket create and verification
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd == -1) {
        printf("socket creation failed...\n");
        exit(0);
    }
    else
        printf("Socket successfully created..\n");
    bzero(&servaddr, sizeof(servaddr));

    // assign IP, PORT
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(PORT);

    // Binding newly created socket to given IP and verification
    if ((bind(sockfd, (SA*)&servaddr, sizeof(servaddr))) != 0) {
        printf("socket bind failed...\n");
        exit(0);
    }
    else
        printf("Socket successfully binded..\n");

    // Now server is ready to listen and verification
    if ((listen(sockfd, 5)) != 0) {
        printf("Listen failed..\n");
        exit(0);
    }
    else
        printf("Server listening...\n");
    len = sizeof(cli);

    // Accept the data packet from client and verification
    connfd = accept(sockfd, (SA*)&cli, &len);
    if (connfd < 0) {
        printf("server acccept failed...\n");
        exit(0);
    }
    else
        printf("server acccept the client..\n");

    // Function for chatting between client and server
    communicate(connfd);

    // After chatting close the socket
    if(close(sockfd) < 0){
        printf("Error - closing socket...\n");
    }
    else{
        printf("Socket successfully closed..\n");
    }
}
Dubbox
  • 191
  • 1
  • 2
  • 20