Мусорные данные передаются по WiFi TCP соединению с рабочего стола на Android - PullRequest
5 голосов
/ 11 декабря 2010

Я оглядывался по сторонам и не смог найти решение этой конкретной проблемы. Простите, если это ошибка новичка, я только что закончил школу, поэтому я читаю как можно больше книг, чтобы научиться программировать на мобильных устройствах.

Цель: Передача данных с сокет-сервера на базе ПК на клиент на базе Android по беспроводной связи (802.11 b / g), который затем обрабатывает эти данные для вывода пользователю.

Проблема: Большое количество ошибочных данных мусора поступает в буферы входного потока на телефоне Android.

Процедура: Я написал и / или изменил три разных куска кода. Во-первых, на моем ноутбуке работает серверная программа. Оригинальный исходный код можно найти здесь: beej.us/guide/bgnet/examples/server.c (спасибо Beej за его исходный код!). Я изменил его, чтобы удалить предупреждения / ошибки, и добавил свой собственный непрерывный цикл ввода данных для целей тестирования. Вот модифицированный код:

    /* A simple server in the internet domain using TCP
   The port number is passed as an argument */
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h> 
#include <sys/socket.h>
#include <netinet/in.h>

void error(char *msg)
{
    perror(msg);
    exit(1);
}

int main(int argc, char *argv[])
{
     int sockfd, newsockfd, portno;  //, clilen;

     //Modified this fromt he original author's code, as
     //the function is looking for clilen to be of type
     //socklen_t, not int
     socklen_t clilen;

     char buffer[256];
     struct sockaddr_in serv_addr, cli_addr;
     int n;

     if (argc < 2) 
     {
         fprintf(stderr,"ERROR, no port provided\n");
         exit(1);
     }

     sockfd = socket(AF_INET, SOCK_STREAM, 0);
     if (sockfd < 0) 
        error("ERROR opening socket");

     bzero((char *) &serv_addr, sizeof(serv_addr));
     portno = atoi(argv[1]);
     serv_addr.sin_family = AF_INET;
     serv_addr.sin_addr.s_addr = INADDR_ANY;
     serv_addr.sin_port = htons(portno);

     if (bind(sockfd, (struct sockaddr *) &serv_addr,
              sizeof(serv_addr)) < 0) 
              error("ERROR on binding");

     //Added this for some clarity 
     printf("Starting to listen on the socket now..\n");
     listen(sockfd,5);


     clilen = sizeof(cli_addr);

     newsockfd = accept(sockfd, 
                 (struct sockaddr *) &cli_addr, 
                 &clilen);

     //Let me know a socket connection has been established
     printf("Socket established!\n");


     if (newsockfd < 0) 
          error("ERROR on accept");

     bzero(buffer,256);

     //Don't want this socket to block and read, only to write 
     //Modified from the original author
     //n = read(newsockfd,buffer,255);

     //if (n < 0) error("ERROR reading from socket");
     //printf("Here is the message: %s\n",buffer);

     //n = write(newsockfd,"Hello, socket!",18);

     const int SIZE_OF_STRING = 30;
     char string[SIZE_OF_STRING];
     int i = 0;

     for (i = 0; i < SIZE_OF_STRING; ++i)
     {
         string[i] = '\0';
     }

     //Ask input from the user until the word "quit" is seen
     //then close the socket 
     while (!strstr(string, "quit"))
     {
        printf("Please enter something to send to the phone.\n");
        scanf("%s", string);
        strcat(string, "\n");
        n = write(newsockfd, string, sizeof(string));
        if (n < 0) error("ERROR writing to socket");
     }

     printf("\n\nexiting..\n");

     close(newsockfd);
     close(sockfd);

     return 0; 
}

А вот код Android:

public class SmartSawLineDrawSocketThread extends Thread 
{

    private Handler smartSawMainThreadCommunicationHandle_;
    private Socket smartSawSocketHandle_;
    private InputStream smartSawSocketInputStreamHandle_;
    private BufferedReader smartSawSocketInputBuffer_;
    private InputStreamReader smartSawSocketInputStreamReader_;
    private boolean threadRunning_ = false;
    private boolean isConnected_;
    private char buffer[] = new char[50];



    //Grab the thread's communication handle for use with sending messages to the UI
    //Thread
    public SmartSawLineDrawSocketThread(Handler handle)
    {
        smartSawMainThreadCommunicationHandle_ = handle;
        threadRunning_ = true;
        isConnected_ = false;
    }

    //Attempt a connection to the host
    public int SmartSawLineDrawSocketThreadConnect(String hostIP, String hostPort)
    {
        int rval = 0;
        Log.i("info", "hostIP = " + hostIP);
        Log.i("info", "hostPort = " + Integer.parseInt(hostPort.trim()));
        try 
        {
            smartSawSocketHandle_ = new Socket(hostIP.trim(), Integer.parseInt(hostPort.trim()));
            if (rval == 0)
            {
                smartSawSocketInputBuffer_ = new BufferedReader(new InputStreamReader(smartSawSocketHandle_.getInputStream()));
                smartSawSocketInputStreamReader_ = new InputStreamReader(smartSawSocketHandle_.getInputStream());
                if (smartSawSocketInputBuffer_ != null)
                {
                    isConnected_ = true;
                }
                else
                {
                    Log.e("error", "Input buffer pointer was null!");
                }
            }
        } 
        catch (UnknownHostException e) 
        {
            rval = 1;
            Log.i("info", "unknown host message e when connecting:" + e);
            e.printStackTrace();
            isConnected_ = false;
        }
        catch (IOException e) 
        {
            rval = 2;
            Log.i("info", "unknown IOException e when connecting:" + e);
            e.printStackTrace();
            isConnected_ = false;
        }
        catch (SecurityException e)
        {
            rval = 3;
            Log.i("info", "Need to set a security setting somewhere");
        }


        Log.i("info", "Rval returned with " + rval);    

        return rval;
    }

    //Disconnect from the server
    public void SmartSawLineDrawSocketThreadDisconnect()
    {
        try 
        {
            smartSawSocketHandle_.close();
            isConnected_ = false;
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    //Once the thread has started running, make sure we're connected, and start
    //trying to listen for messages
    @Override 
    public void run()
    {
        Log.i("info", "Made it into the run() loop of the thread.");
        while (threadRunning_)
        {
            if (isConnected_ == true)
            {
                try
                {
                    if (smartSawSocketInputStreamReader_.ready() == true)
                    //(smartSawSocketInputBuffer_.ready() == true)
                    {
                        int numread = 0;
                        numread = smartSawSocketInputStreamReader_.read(buffer);

                        Log.i("info", "amount of characters read in: " + numread);

                        Message mainThreadMessage_ = Message.obtain();
                        Bundle mainThreadDataBundle_ = new Bundle();
                        mainThreadDataBundle_.putString("Zero", new String(buffer)); //smartSawSocketInputBuffer_.readLine());
                        mainThreadMessage_.setData(mainThreadDataBundle_);
                        mainThreadMessage_.setTarget(smartSawMainThreadCommunicationHandle_);
                        mainThreadMessage_.sendToTarget();
                        Log.i("info", "Received a string! Sent this to main thread: " + mainThreadDataBundle_.getString("Zero"));


                    }
                }
                catch (IOException e)
                {
                    Log.i("info","IO Exception in thread main loop, e was: " + e);
                }
            }


        }

    } 
}

Домашнее задание и тесты: Я могу успешно передать одну строку, но все, что после этого - неразборчивый мусор в соединении ПК-Android. Желая сначала сделать домашнее задание, я хотел исключить код на стороне сервера. Я взял код на стороне клиента Биджа (beej.us/guide/bgnet/examples/client.c) и изменил его, чтобы он был хорошим слушателем. Мне удалось передать несколько строк клиенту по TCP-соединению на базе ПК-ПК. Я перенаправил вывод клиента в файл и открыл его в шестнадцатеричном редакторе. И вот, ошибочных данных найти не удалось. Я проверил это на ноутбуке, подключенном к маршрутизатору 802.11b / g, являющимся сервером, и на жестком проводном рабочем столе в качестве клиента. Я устранил проблемы с оборудованием и проверил проблемы с кодом на стороне сервера. Это должно быть где-то в том, как я реализую код на стороне клиента Android. Я также попробовал автоматизированный класс BufferedReader, чтобы выполнить мой ввод, а также вручную обработать ввод с помощью класса InputStreamReader. Оба получают одинаковый вывод мусора после первой строки. Это наводит меня на мысль, что это где-то во входном потоке сокета, но как мне это исправить? У кого-нибудь есть предложения?

Вот код для проверки на стороне клиента на ПК:

/*
** client.c -- a stream socket client demo
*/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>

#include <arpa/inet.h>

#define PORT "27015" // the port client will be connecting to 

#define MAXDATASIZE 100 // max number of bytes we can get at once 

// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
    if (sa->sa_family == AF_INET) {
        return &(((struct sockaddr_in*)sa)->sin_addr);
    }

    return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

int main(int argc, char *argv[])
{
    int sockfd, numbytes;  
    char buf[MAXDATASIZE];
    struct addrinfo hints, *servinfo, *p;
    int rv;
    char s[INET6_ADDRSTRLEN];

    if (argc != 2) {
        fprintf(stderr,"usage: client hostname\n");
        exit(1);
    }

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;

    if ((rv = getaddrinfo(argv[1], PORT, &hints, &servinfo)) != 0) {
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
        return 1;
    }

    // loop through all the results and connect to the first we can
    for(p = servinfo; p != NULL; p = p->ai_next) {
        if ((sockfd = socket(p->ai_family, p->ai_socktype,
                p->ai_protocol)) == -1) {
            perror("client: socket");
            continue;
        }

        if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
            close(sockfd);
            perror("client: connect");
            continue;
        }

        break;
    }

    if (p == NULL) {
        fprintf(stderr, "client: failed to connect\n");
        return 2;
    }

    inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr),
            s, sizeof s);
    printf("client: connecting to %s\n", s);

    freeaddrinfo(servinfo); // all done with this structure

    //Modified from the original to spit out all strings transmitted from the server, then close the socket
    //when finished.
    while (!strstr(buf, "close"))
    {
        numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0);
        if (numbytes)
        {
            printf("Received: \n");
            printf("%s", buf);
            printf("\n");
        }
    }

    if ((numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1) {
        perror("recv");
        exit(1);
    }

    buf[numbytes] = '\0';

    printf("client: received '%s'\n",buf);

    close(sockfd);

    return 0;
} 

Спасибо за помощь! Moscro

1 Ответ

1 голос
/ 11 декабря 2010

Клиент:

Вы должны создать строку, используя new String(buffer, 0, numread), так как не весь буфер мог быть заполнен, поэтому в строке появятся существующие символы, не перезаписанные read().

Вам также следует проверить numread == -1, так как это указывает на то, что соединение закрыто.

Сервер:

sizeof(some_array) возвращает размер (в байтах) всего массива, что означает, что ваш сервер каждый раз отправляет 30 байтов. Большинство из них будут нулевыми (\0), что объясняет, почему клиент C работает, поскольку printf предполагает, что первый нулевой байт указывает конец строки. Однако Java этого не делает, поэтому, вероятно, дополнительные байты появляются в сообщении журнала как мусор.

Одним из решений может быть:

  • изменить сервер, чтобы отправить точное количество символов в сообщении, включая завершающий \n, который будет strlen(string) + 1
  • затем в клиенте отключите второй InputStreamReader и просто используйте метод BufferedReader readLine(), который читает до следующего символа новой строки
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...