【问题标题】:Why my android client cannot receive string message from winsock c++ server?为什么我的 android 客户端无法接收来自 winsock c++ 服务器的字符串消息?
【发布时间】:2023-03-15 13:11:01
【问题描述】:

我正在尝试构建一个 android 应用程序,该应用程序使用 TCP 套接字与在 Windows 上运行的 c++ 服务器进行通信。我成功地从应用程序向服务器发送了一个字符串,但我无法反其道而行之。请帮助我,因为我一直在尝试解决这个问题。

Android 客户端:

String mystr="Hello World...";
char[] buffin=new char[128];
String input =null;
Integer count = 0;
class TextRcv extends AsyncTask<Void, Void, Integer>
{

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

        //Sending a string (WORKING)
        Socket clientSocket = null;
        try {
            clientSocket= new Socket("192.168.1.5",8889);
            DataOutputStream oos= new DataOutputStream(clientSocket.getOutputStream());
            oos.writeBytes(mystr);
            //oos.writeBytes(String.valueOf(mystr.length()));//write the length of the string first
            //byte[] bufferout=mystr.getBytes();
            //oos.write(bufferout, 0, bufferout.length);
            oos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        //Recieving a String (Not working)
        try {
            BufferedReader in = null;
            if (clientSocket != null)
            {
                in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            }
            else
            {
                Log.e("samer ","CLIENTSOCKET NULL");
            }

            if (in != null)
            {
                count=in.read(buffin);
            }
            else
            {
                Log.e("samer ","BUFFERREADER NULL");
            }

            if(count>0)
            {
                input=new String(buffin,0,count);
            }
            if (clientSocket != null) {
                clientSocket.close();
            }

        } catch (IOException e) {
            Log.i("samer count: ",String.valueOf(count));//RETURNS 0 !!
            Log.i("buff samer: ", String.valueOf(buffin));//Returns garbage
            e.printStackTrace();
        }
        return count;
    }

    @Override
    protected void onPostExecute(Integer count) {
        super.onPostExecute(count);

        Toast toast=Toast.makeText(getApplicationContext(),String.valueOf(count),Toast.LENGTH_LONG);
        toast.show();
    }
}

尽管在 windows c++ 服务器端它说它已成功发送 25 个字节,但“计数”始终返回 0。

C++ Winsock 服务器:

int  main(int argc, const char *argv[])
{
    WSADATA wsaData;
    int iResult;
    SOCKET ListenSocket = INVALID_SOCKET;
    SOCKET ClientSocket = INVALID_SOCKET;

    struct addrinfo *result = NULL;
    struct addrinfo hints;
    int valread;
    int iSendResult;
    char * recvbuf;
    char *recvstr;
    int recvbuflen = DEFAULT_BUFLEN;
    recvbuf = (char*)malloc((recvbuflen + 1) * sizeof(char));
    recvstr = (char*)malloc((recvbuflen + 1) * sizeof(char));
    char* AndroidID;
    char *sendbuf = "Client: sending data test";

    // Initialize Winsock
    iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (iResult != 0) {
        printf("WSAStartup failed with error: %d\n", iResult);

        return 1;
    }

    ZeroMemory(&hints, sizeof(hints));
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;
    hints.ai_flags = AI_PASSIVE;

    // Resolve the server address and port
    iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
    if (iResult != 0) {
        printf("getaddrinfo failed with error: %d\n", iResult);
        WSACleanup();

        return 1;
    }

    // Create a SOCKET for connecting to server
    ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
    if (ListenSocket == INVALID_SOCKET) {
        printf("socket failed with error: %ld\n", WSAGetLastError());
        freeaddrinfo(result);
        WSACleanup();

        return 1;
    }

    // Setup the TCP listening socket
    iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
    if (iResult == SOCKET_ERROR) {
        printf("bind failed with error: %d\n", WSAGetLastError());
        freeaddrinfo(result);
        closesocket(ListenSocket);
        WSACleanup();
        return 1;
    }

    freeaddrinfo(result);

    iResult = listen(ListenSocket, SOMAXCONN);
    if (iResult == SOCKET_ERROR) {
        printf("listen failed with error: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();

        return 1;
    }

    // Accept a client socket
    ClientSocket = accept(ListenSocket, NULL, NULL);
    if (ClientSocket == INVALID_SOCKET) {
        printf("accept failed with error: %d\n", WSAGetLastError());
        closesocket(ListenSocket);
        WSACleanup();

        return 1;
    }

    if (ClientSocket != INVALID_SOCKET)
    {
        cout << "Start Receving" << endl;

        //------Start Receiving
        //-----------------------------------
        int lengthofrecv=recv(ClientSocket, recvbuf, recvbuflen, 0);
        recvbuf[lengthofrecv] = '\0';//append \0 to use with printf()
        AndroidID = recvbuf;
        printf("AndroidID - %s \n", recvbuf);
        cout << " \n Done ";

        //---Start Sending to android
        //-----------------------------------------
        iResult = send(ClientSocket, sendbuf, (int)strlen(sendbuf), 0);
        if (iResult == SOCKET_ERROR) 
        {
            wprintf(L"send failed with error: %d\n", WSAGetLastError());
            closesocket(ClientSocket);
            WSACleanup();
            return 1;
        }
        printf("Bytes Sent: %d\n", iResult);

        cout << WSAGetLastError() << endl;
    }

    // shutdown the connection since we're done
    iResult = shutdown(ClientSocket, SD_SEND);
    if (iResult == SOCKET_ERROR) {
        printf("shutdown failed with error: %d\n", WSAGetLastError());
        closesocket(ClientSocket);
        WSACleanup();
        return 1;
    }

    //cleanup
    closesocket(ClientSocket);
    WSACleanup();

    return 0;
}

服务器控制台输出:

开始接收

AndroidID - Hello World...

发送的完成字节数:25

0

按任意键继续。 . .

【问题讨论】:

  • 如果 Java 读取代码在发送代码发送之前运行会发生什么情况,它是读取 0 字节还是阻塞?
  • @RichardCritten:read(char[]) 文档说:“此方法将阻塞,直到某些输入可用,发生 I/O 错误,或到达流的末尾。”
  • 所以你建议我的代码中的读取操作实际上是在服务器发送之前运行的。正确的?如果是这样,你认为我应该怎么做?我是否关闭套接字并为每个读取或写入操作重新打开它?或者我应该在读取操作之前延迟,以便给服务器一些时间将数据放到网络上?处理它的最佳方法是什么? (如果我理解正确)
  • "我是否关闭套接字并在每次读取或写入操作时重新打开它" - 当然不是!真正的问题是您的代码在有机会读取之前关闭了套接字。你需要解决这个问题。

标签: java android c++ sockets


【解决方案1】:

问题是您在将初始字符串发送到服务器后调用oos.close()。

DataOutputStream 扩展了FilterOutputStream,并记录了其close() 方法的行为:

FilterOutputStream.close()

FilterOutputStream的close方法调用其flush方法,然后调用其底层输出流的close方法。

在这种情况下,“底层输出流”是套接字的 outputStream,它的close() 行为也被记录在案:

Socket.getOutputStream()

关闭返回的 OutputStream 将关闭关联的套接字。

Socket.close()

关闭此套接字。

当前在此套接字上的 I/O 操作中阻塞的任何线程都将抛出 SocketException。

一旦套接字被关闭,它就不能用于进一步的网络使用(即不能重新连接或重新连接)。需要创建一个新的套接字。

关闭这个socket也会关闭这个socket的InputStream和OutputStream。

如果此套接字具有关联的通道,则该通道也将关闭。

因此,您的客户端在读取服务器的回复字符串之前关闭了套接字连接。您需要删除对oos.close() 的调用,以便保持连接打开,直到您完成阅读。

在服务器端,仅仅由于时间原因,您的代码可能能够在套接字检测到断开连接之前调用send() 并将回复字符串放入套接字的出站缓冲区。缓冲区实际上并不是由send() 自己传输的,它被排队到操作系统内核,它会在后台传输数据。所以send()如果在检测到断开连接之前缓冲了回复字符串,将返回成功而不是失败。

【讨论】:

  • 你是一个绝对的传奇。非常感谢。正如你所说的那样工作。
猜你喜欢
  • 1970-01-01
  • 2013-03-28
  • 1970-01-01
  • 1970-01-01
  • 2018-10-03
  • 1970-01-01
  • 2018-04-30
  • 2019-10-13
  • 2023-03-31
相关资源
最近更新 更多