2010-12-11 9 views
5

He estado buscando y no he podido encontrar una solución a este problema en particular. Perdónenme si se trata de un error de principiante, recién salido de la escuela, así que leo tantos libros como puedo para ponerme al día con la programación de dispositivos móviles.Datos basura transmitidos en WiFi Conexión TCP desde el escritorio a Android

El objetivo: datos de transmisión desde un servidor de socket basado en PC a un cliente basado en Android de forma inalámbrica (802.11 b/g), que será entonces proceso de dichos datos para la salida al usuario.

El problema: Se está recibiendo una gran cantidad de datos erróneos de basura en las memorias intermedias de flujo de entrada en el teléfono Android.

El procedimiento: He escrito y/o modificado tres piezas de código diferentes. Primero es el programa del servidor que se ejecuta en mi computadora portátil. El código fuente original se puede encontrar aquí: beej.us/guide/bgnet/examples/server.c (¡gracias a Beej por su código fuente!). Lo modifiqué para eliminar advertencias/errores, y agregué mi propio bucle continuo de entrada de datos para fines de prueba. Aquí está el código modificado:

/* 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; 
} 

Y aquí es el código de 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); 
       } 
      } 


     } 

    } 
} 

Tareas y Análisis: puedo trasmitir exitosamente una cadena, pero todo después de eso es basura ininteligible en el Conexión PC-Android. Queriendo hacer mi tarea primero, quería eliminar el código del lado del servidor. Tomé el código del lado del cliente de Beej (beej.us/guide/bgnet/examples/client.c) y lo modifiqué para que fuera un buen oyente. Pude transmitir varias cadenas al cliente en una conexión TCP basada en PC-PC. Redirigí la salida del cliente a un archivo y lo abrí bajo un editor hexadecimal. Y he aquí, no se encontraron datos erróneos. Probé esto con una computadora portátil conectada a un enrutador 802.11b/g que es el servidor, y con una computadora de escritorio fija que es el cliente. Eliminé los problemas de hardware y los problemas de código de prueba en el lado del servidor. DEBE estar en alguna parte de cómo estoy implementando el código del lado del cliente de Android. También probé con la clase BufferedReader para hacer mi entrada, así como el manejo manual de la entrada con una clase InputStreamReader. Ambos reciben la misma salida de basura después de la primera cadena. Esto me lleva a creer que está en algún lugar del flujo de entrada del socket, pero ¿cómo lo arreglaría? ¿Alguien tiene alguna sugerencia?

Este es el código para la prueba en el cliente basado en PC:

/* 
** 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; 
} 

Gracias por la ayuda! Moscro

+0

FYI, si bien no con apoyo oficial puede compilar su prueba de cliente de PC con el NDK de gcc como un ejecutable independiente y pruébalo por teléfono. También algunas versiones de Android tienen un ejecutable de línea de comando netcat (nc) que es ideal para probar en ambos extremos. –

+0

Gracias Chris! Lo investigaré en mi próxima ronda. Ya tengo la infraestructura configurada, así que lo dejo como está por ahora, pero eso será útil más adelante. – moscro

Respuesta

1

Cliente:

Usted debe crear la cadena usando new String(buffer, 0, numread) ya que no toda la memoria intermedia puede haber sido llenado, por lo que los personajes ya no se sobrescribe con el read() aparecerá en la cadena.

También debe verificar numread == -1, ya que esto indica que la conexión está cerrada.

Servidor:

sizeof(some_array) devuelve el tamaño (en bytes) de toda la matriz, lo que significa que su servidor está enviando 30 bytes cada vez. La mayoría de ellos serán nulos (\0) lo que explica por qué parece funcionar el cliente C, ya que printf asume que el primer byte nulo indica el final de la cadena. Sin embargo, Java no lo hace, y es por eso que los bytes adicionales aparecen en el mensaje de registro como basura.

Una solución podría ser:

  • modificar el servidor para enviar el número exacto de caracteres en el mensaje que incluye el \n terminación, lo que sería strlen(string) + 1
  • entonces en el cliente, zanja el segundo InputStreamReader y sólo tiene que utilizar BufferedReader 's método readLine(), que lee hasta el siguiente carácter de nueva línea
+0

Ah, error mío, debería haber incluido salidas de registro. Aquí están los datos de registro que obtengo después de ingresar una sola palabra: 12-11 11: 28: 56.610: INFO/info (512): ¡Recibí una cadena! Enviado a este hilo principal: hola 12-11 11: 28: 56.610: INFO/Información (512): ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ 12-11 11: 29: 26.710: INFO/Información (512): cantidad de caracteres leer en: 30 Y aquí hay otra palabra: 12-11 11: 29: 26.720: INFO/info (512): ¡Recibió una cadena! Enviado a este hilo principal: Prueba de 12-11 11: 29: 26.720: INFO/Información (512): ÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀÀ INFO/Información (512): cantidad de caracteres leer en: 30 – moscro

+0

Debido numread se establece en 30 en lugar de 5, 4 o -1 para cualquiera de las salidas, esto me lleva a creer que no es un problema de inicialización de cadena o un socket TCP inactivo. ¿Alguna causa posible para solo datos de basura continuos como ese? – moscro

+0

Sí, envía 30 bytes cada vez y la mayoría son basura :) Vea la respuesta actualizada. – SimonJ

Cuestiones relacionadas