当前位置: 首页 > 知识库问答 >
问题:

Android套接字读取不工作,除非在超时之前关闭连接

陈畅
2023-03-14

我试图在我的桌面上做一个套接字服务器,并从我的Android系统连接到它,但是,Android系统不能从套接字读取,除非服务器在发送数据后关闭连接。

桌面服务器代码:

#define SERVER_BUFLEN   512
#define SERVER_PORT     "27033"

SOCKET ListenSocket = INVALID_SOCKET;
SOCKET ClientSocket = INVALID_SOCKET;

int main () {
    setvbuf(stdout, NULL, _IONBF, 0);

    WSADATA wsaData;
    int iResult;

    struct addrinfo *result = NULL;
    struct addrinfo hints;

    int iSendResult;
    char recvbuf[SERVER_BUFLEN];
    int recvbuflen = SERVER_BUFLEN;

    // 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, SERVER_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);
        ListenSocket = INVALID_SOCKET;
        WSACleanup();
        return 1;
    }

    freeaddrinfo(result);


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

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

    //int flag = 1;
    //setsockopt(ClientSocket, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));

    do {
        iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
        if (iResult > 0) {
            printf("Bytes received: %d\n", iResult);

            iSendResult = send( ClientSocket, recvbuf, iResult, 0 );
            if (iSendResult == SOCKET_ERROR) {
                printf("send failed with error: %d\n", WSAGetLastError());
            }
            printf("Bytes sent: %d\n", iSendResult);
            //iResult = shutdown(ClientSocket, SD_SEND);
        } else if (iResult == 0) {
            printf("Connection closing...\n");
        } else  {
            printf("recv failed with error: %d\n", WSAGetLastError());
        }
    } while (iResult > 0);

    // 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);
    printf ("client left\n");

    if (ListenSocket != INVALID_SOCKET) {
        closesocket(ListenSocket);
    }
    WSACleanup();
    return 0;
}

Android客户端代码:

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d(TAG, "onCreate");


        StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
        StrictMode.setThreadPolicy(policy);

        try {
            Socket sock = new Socket();
            //sock.setReceiveBufferSize(10);
            sock.connect(new InetSocketAddress("192.168.0.101", 27033));

            OutputStream os = sock.getOutputStream();
            InputStream is = sock.getInputStream();

            String msg = "Hello World!\r\n\0";
            byte[] arr = msg.getBytes(Charset.forName("UTF-8"));

            os.write(arr);
            os.flush();

            Log.d(TAG, "RECV MSG: " + is.read ());

            sock.close();
        } catch (Exception e) {
            Log.d(TAG, e.getMessage());
        }


    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy");
    }
}

服务器的输出为:

waiting for new client
new client connected
Bytes received: 15
Bytes sent: 15
recv failed with error: 10060
client left

客户端的输出为:

08-23 18:29:55.997 26472-26472/com.example.greg.systemremote D/MainActivity: onCreate
08-23 18:29:55.997 26472-26472/com.example.greg.systemremote D/libc-netbsd: [getaddrinfo]: hostname=192.168.0.101; servname=(null); netid=0; mark=0
08-23 18:29:55.997 26472-26472/com.example.greg.systemremote D/libc-netbsd: [getaddrinfo]: ai_addrlen=0; ai_canonname=(null); ai_flags=4; ai_family=0
08-23 18:33:54.254 26472-26472/com.example.greg.systemremote D/MainActivity: recvfrom failed: ETIMEDOUT (Connection timed out)

我试着和你唠叨

int flag = 1;
setsockopt(ClientSocket, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));

但是,如果我取消注释该行

iResult = shutdown(ClientSocket, SD_SEND);

android的输出是

08-23 18:47:41.984 13164-13164/com.example.greg.systemremote D/MainActivity: onCreate
08-23 18:47:41.985 13164-13164/com.example.greg.systemremote D/libc-netbsd: [getaddrinfo]: hostname=192.168.0.101; servname=(null); netid=0; mark=0
08-23 18:47:41.985 13164-13164/com.example.greg.systemremote D/libc-netbsd: [getaddrinfo]: ai_addrlen=0; ai_canonname=(null); ai_flags=4; ai_family=0
08-23 18:47:42.318 13164-13164/com.example.greg.systemremote D/MainActivity: RECV MSG: 72

(这是名义上的)

所以,我的问题是,在不关闭连接的情况下,如何在这个套接字连接上发送数据?另外请注意,我计划发送二进制数据,而不是文本。

共有1个答案

柴衡
2023-03-14

您在C端的错误来源是:do-time循环

do {
        iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
        if (iResult > 0) {
            printf("Bytes received: %d\n", iResult);

            iSendResult = send( ClientSocket, recvbuf, iResult, 0 );
            if (iSendResult == SOCKET_ERROR) {
                printf("send failed with error: %d\n", WSAGetLastError());
            }
            printf("Bytes sent: %d\n", iSendResult);
            //iResult = shutdown(ClientSocket, SD_SEND);
        } else if (iResult == 0) {
            printf("Connection closing...\n");
        } else  {
            printf("recv failed with error: %d\n", WSAGetLastError());
        }
    } while (iResult > 0);

对于第一次迭代,它进入循环,接收数据并成功地将数据发送到Android设备。一切都很完美。然后,在Android端,只有在接受一个响应后才能关闭连接。

try {
            Socket sock = new Socket();
            //sock.setReceiveBufferSize(10);
            sock.connect(new InetSocketAddress("192.168.0.101", 27033));

            OutputStream os = sock.getOutputStream();
            InputStream is = sock.getInputStream();

            String msg = "Hello World!\r\n\0";
            byte[] arr = msg.getBytes(Charset.forName("UTF-8"));

            os.write(arr);
            os.flush();

            Log.d(TAG, "RECV MSG: " + is.read ());

            sock.close();
        } catch (Exception e) {
            Log.d(TAG, e.getMessage());
        }

由于C代码处于do while循环中,因此使用iResult

因此,您需要检查连接是否打开,然后只有您可以继续接收数据。目前,您的编码逻辑很难接收数据。

它将在当前状态下继续抛出异常。

另外,从错误代码Windows套接字错误代码10060:

WSAETIMEDOUT 10060

连接超时。

由于连接方在一段时间后没有正确响应,连接尝试失败,或者由于连接的主机没有响应,建立的连接失败。

编辑:

Android端的错误源似乎是在onCreate()方法中创建网络连接!这在任何地方都是不鼓励的(在Android中)。

您应该仅使用onCreate()方法来创建和实例化将在应用程序中使用的对象。不应该有任何阻塞调用,就像您的情况一样(sock.connect(IP,port))。

Android开发者指南中的服务:

服务是一个应用程序组件,表示应用程序希望在不与用户交互的情况下执行较长时间的运行操作,或者提供供其他应用程序使用的功能。

请注意,与其他应用程序对象一样,服务在其宿主进程的主线程中运行。这意味着,如果您的服务要执行任何CPU密集型(如MP3播放)或阻塞(如网络)操作,那么它应该生成自己的线程来执行该工作(emphasis mine)。

另外,从进程和线程:

Android可能会在某个时候决定关闭一个进程,此时内存不足,其他进程需要更直接地为用户服务。

当决定杀死哪些进程时,Android系统会权衡它们对用户的相对重要性。例如,与托管可见活动的进程相比,它更容易关闭托管屏幕上不再可见的活动的进程。因此,是否终止进程的决定取决于在该进程中运行的组件的状态。

此外,Android的单线程模型有两条规则:

>

  • 不要阻塞UI线程

    不要从UI线程外部访问Android UI工具包。

    建议的解决办法:

    创建一个单独的服务以实现网络连接的建立(检查本地服务示例)。您可以参考如何通过服务实现网络连接的链接,而不是活动本身!

  •  类似资料:
    • 我在Android蓝牙API中连接客户端套接字时遇到问题。我已经读过这些帖子了:这篇,这篇,这篇,还有这篇。根据这些帖子,我编写了如下代码,但仍然不起作用: 下面是包含套接字上返回的错误的日志。connect(): 问题似乎出现在Android4.3之后,因为我在Android7.1和8.1上使用的套接字的端口值。有人知道如何在Android上使用蓝牙吗?

    • 这是来自.properties得my DB配置: 这是config.xml: 实际上,我可以很容易地访问我们的本地web应用程序,并且可以在日志中看到到DB的连接跟踪,但对于批处理来说,情况并非如此。 在我有了这个之后: 有人帮忙吗?

    • 我有一个netty服务器,它接收来自客户端的请求,向另一个服务器发出请求,然后在对原始客户端请求的响应中使用来自第二个请求的响应。我希望对第二台服务器的请求有一个较短的超时(~40ms),以便在超时时发送一般响应,但我不想关闭与第二台服务器的连接。相反,我将在超时请求到达时放弃响应,然后将连接返回到我的池。 在Netty最好的方法是什么?我尝试过ReadTimeoutHandler,但当超时发生时

    • 我想把我的Android设备(Android4.2.2)与BluetothAdapter创建的另一个Android设备配对。startDiscovery()。我在运行BluetoothDevice时发现了什么。connect()请求配对开始。但我在运行connect()时遇到了问题 我在中尝试了impliment fallback和gatclass方法 IOException:读取失败,套接字可能

    • 我在android中保持套接字连接有麻烦。 我在应用程序中使用Socket.IO客户端java库。 屏幕打开时保持插座连接。 但是,如果屏幕关闭,套接字会因ping超时而断开连接。 我该如何解决这个问题? 我像这样打开连接。 这是我的服务器端代码 ping间隔为25秒,超时为60秒。 当android屏幕关闭时,客户端不适用于。其他事件正常工作。 服务器与日志断开连接(ping超时)。

    • 问题内容: 我试图呼吁一个非常繁重的过程。平均工作时间估计为9-10分钟。 当我执行该过程时,我为一个巨大的数字设置了超时时间:99999999。 2分钟后,出现以下错误: java.net.SocketTimeoutException:读取超时 我尝试对其进行更多处理,并将超时设置为3000,并且在预期的3秒钟后出现了相同的错误。 您对为什么将其设置为最大120000 有任何想法吗? 问题答案:

    • 我们在gitlab ci构建中使用Sonarqube进行代码分析,现在我们在使用mvn clean org时遇到了问题。jacoco:jacocomaven插件:准备代理安装-Dmaven。测验失败ignore=true-DskipTests sonar:sonar要运行gitlab管道,管道中的错误与问题Jenkins/sonar timeout:Socket closed相同。 sonarqu

    • 我正在使用heroku和android客户端node.jsweb服务器进行websocket.io通信 我想在服务器信息发送到客户端Android系统时收到它,即使Android系统屏幕关闭 所以我基本上是做一个服务,一个套接字。在侦听器上,线程 也确实应用了部分_唤醒_锁定、前台服务、发送通知、每5秒乒乓一次... 当Android的屏幕打开时,我的系统运行良好。 但是在Android屏幕关闭后