简体   繁体   English

从桌面到Android的WiFi TCP连接传输的垃圾数据

[英]Garbage data transmitted in WiFi TCP connection from desktop to Android

I've been looking around, and haven't been able to find a solution to this particular issue. 我一直在四处寻找,并且无法找到解决这一特定问题的方法。 Forgive me if this is a newbie mistake, I'm fresh out of school so I'm reading as many books as I can to get caught up on mobile device programming. 请原谅我,如果这是一个新手的错误,我刚刚离开学校,所以我正在阅读尽可能多的书籍,以便了解移动设备编程。

The Goal: Transmit data from a PC based socket server to an Android based client wirelessly (802.11 b/g), which will then process said data for output to the user. 目标:将基于PC的套接字服务器的数据无线传输到基于Android的客户端(802.11 b / g),然后处理所述数据以输出给用户。

The Problem: A large amount of erroneous garbage data is being received in the input stream buffers on the Android phone. 问题: Android手机上的输入流缓冲区正在接收大量错误的垃圾数据。

The Procedure: I've written and/or modified three different pieces of code. 程序:我编写和/或修改了三段不同的代码。 First is the server side program running on my laptop. 首先是我的笔记本电脑上运行的服务器端程序。 The original source code can be found here: beej.us/guide/bgnet/examples/server.c (thanks to Beej for his source code!). 原始源代码可以在这里找到:beej.us/guide/bgnet/examples/server.c(感谢Beej的源代码!)。 I've modified it to remove warnings/errors, and added my own continuous data input loop for testing purposes. 我修改了它以删除警告/错误,并添加了我自己的连续数据输入循环以进行测试。 Here is the modified code: 这是修改后的代码:

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

And here is the Android code: 这是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);
                }
            }


        }

    } 
}

Homework and Tests: I can successfully transmit one string, but everything after that is unintelligible garbage in the PC-Android connection. 家庭作业和测试:我可以成功传输一个字符串,但之后的所有内容都是PC-Android连接中无法理解的垃圾。 Wanting to do my homework first, I wanted to eliminate the server side code. 想要先做作业,我想消除服务器端代码。 I took Beej's client side code (beej.us/guide/bgnet/examples/client.c) and modified it to just be a good listener. 我使用了Beej的客户端代码(beej.us/guide/bgnet/examples/client.c)并将其修改为一个很好的倾听者。 I was able to transmit several strings to the client on a PC-PC based TCP connection. 我能够在基于PC-PC的TCP连接上向客户端传输多个字符串。 I redirected the client's output to a file, and opened it under a hex editor. 我将客户端的输出重定向到一个文件,并在十六进制编辑器下打开它。 Lo and behold, there was no erroneous data to be found. 瞧,没有找到错误的数据。 I tested this with a laptop connected to an 802.11b/g router being the server, and with a hard wired desktop being the client. 我用连接到作为服务器的802.11b / g路由器的笔记本电脑进行了测试,并且硬连线桌面是客户端。 I've eliminated hardware issues, and test code issues on the server side. 我已经消除了硬件问题,并在服务器端测试代码问题。 It MUST be somewhere in how I'm implementing the Android client side code. 它必须在我如何实现Android客户端代码的某个地方。 I also tried the automated BufferedReader class to do my input, as well as manually handling the input with an InputStreamReader class. 我还尝试使用自动BufferedReader类来完成输入,以及使用InputStreamReader类手动处理输入。 Both receive the same garbage output after the first string. 两者都在第一个字符串后收到相同的垃圾输出。 This leads me to believe it's somewhere in the socket's input stream, but how would I fix that? 这让我相信它在套接字输入流中的某个地方,但我该如何解决? Does anybody have any suggestions? 有人有什么建议吗?

Here is the code for the PC based client side test: 以下是基于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;
} 

Thanks for the help! 谢谢您的帮助! Moscro Moscro

Client: 客户:

You should create the string using new String(buffer, 0, numread) since not all of the buffer may have been filled, so existing characters not overwritten by the read() will appear in the string. 您应该使用new String(buffer, 0, numread)创建字符串new String(buffer, 0, numread)因为并非所有缓冲区都已填充,因此read()不会覆盖的现有字符将出现在字符串中。

You should also check for numread == -1 , since this indicates the connection being closed. 您还应检查numread == -1 ,因为这表示连接已关闭。

Server: 服务器:

sizeof(some_array) returns the size (in bytes) of the entire array, which means your server is sending 30 bytes each time. sizeof(some_array)返回整个数组的大小(以字节为单位),这意味着您的服务器每次发送30个字节。 Most of those will be null ( \\0 ) which explains why the C client appears to work, since printf assumes that the first null byte indicates the end of the string. 其中大多数将为null( \\0 ),这解释了为什么C客户端似乎工作,因为printf假定第一个空字节表示字符串的结尾。 Java doesn't, however, which is probably why the extra bytes appear in the log message as garbage. 但是,Java并不是为什么额外的字节在日志消息中显示为垃圾的原因。

One solution might be: 一种解决方案可能是:

  • modify the server to send the exact number of characters in the message including the terminating \\n , which would be strlen(string) + 1 修改服务器以发送消息中的确切字符数,包括终止\\n ,这将是strlen(string) + 1
  • then in the client, ditch the second InputStreamReader and just use BufferedReader 's readLine() method, which reads up to the next newline character 然后在客户端,抛弃第二个InputStreamReader并使用BufferedReaderreadLine()方法,该方法读取下一个换行符

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM