首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Android套接字读取无法工作,除非连接在超时之前关闭。

Android套接字读取无法工作,除非连接在超时之前关闭。
EN

Stack Overflow用户
提问于 2016-08-23 17:00:28
回答 1查看 948关注 0票数 0

我试图在我的桌面上制作一个套接字服务器并从我的android连接到它,但是,除非服务器在发送数据后关闭连接,否则android无法从套接字中读取。

桌面服务器代码:

代码语言:javascript
复制
#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客户端代码:

代码语言:javascript
复制
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");
    }
}

来自服务器的输出是:

代码语言:javascript
复制
waiting for new client
new client connected
Bytes received: 15
Bytes sent: 15
recv failed with error: 10060
client left

客户端的输出是:

代码语言:javascript
复制
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)

注意到客户端没有收到任何消息.

我试过禁用纳格尔

代码语言:javascript
复制
int flag = 1;
setsockopt(ClientSocket, IPPROTO_TCP, TCP_NODELAY, (char *) &flag, sizeof(int));

但是它并没有改变任何事情,所以我尝试用Wireshark检查网络流量,从输出中可以看到,数据被发送到android:

(192.168.0.183是android设备)

但是,如果我取消评论的话

代码语言:javascript
复制
iResult = shutdown(ClientSocket, SD_SEND);

因此,在发送数据后关闭连接,在android端接收该连接,网络流量如下:

而android的输出是

代码语言:javascript
复制
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

(名义数字)

因此,我的问题是,在不关闭连接的情况下,如何才能将数据传回和发送到这个套接字连接上?另外,请注意,我计划发送二进制数据,而不是文本。

EN

回答 1

Stack Overflow用户

发布于 2016-08-23 17:39:08

C++端的错误来源是:do-while loop

代码语言:javascript
复制
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端,只有在接受了一个响应之后才关闭连接。

代码语言:javascript
复制
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>0的,所以它将进入循环,并再次尝试接收数据;但是,不幸的是,连接已经在Android端关闭。

因此,您需要检查连接是否打开,然后只有您才能继续接收数据。目前,您的编码逻辑无法接收数据。

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

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

WSAETIMEDOUT 10060 连接超时。 连接尝试失败是因为连接方在一段时间后没有正确响应,或者建立的连接失败是因为连接主机没有响应。

编辑:

Android方面的错误来源似乎是您在onCreate()方法中创建了网络连接!这在任何地方都是不受欢迎的(在Android中)。

您应该只使用onCreate()方法来创建和实例化应用程序中将要使用的对象。不应该有任何阻塞调用,在您的情况下(sock.connect(IP,端口))。

来自Android开发人员服务指南

服务是一个应用程序组件,它表示应用程序希望在不与用户交互的情况下执行运行时间较长的操作,或者提供其他应用程序使用的功能。 请注意,与其他应用程序对象一样,服务在其宿主进程的主线程中运行。这意味着,如果您的服务要执行任何CPU密集型操作(如MP3回放)或阻塞(如网络)操作,那么它应该产生自己的线程来完成该工作--(重点雷)。

另外,来自进程和线程

Android可能会决定在内存较低的某个时候关闭一个进程,而其他进程则需要这些进程才能更好地为用户服务。 在决定终止哪些进程时,Android系统会权衡它们对用户的相对重要性。例如,与托管可见活动的进程相比,它更容易关闭屏幕上不再可见的进程宿主活动。因此,是否终止进程的决定取决于该进程中运行的组件的状态。 另外,Android的单线程模型有两个简单的规则:

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

建议的解决方案:

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

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39106923

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档