Linux套接字:第二个read()在本地主机上失败

时间:2018-12-06 21:16:58

标签: c linux sockets

我有一个确认命令然后发送数据的服务器。在命令行上可以正常使用:echo "show version" | nc -q1 127.0.0.1 5000给出:

Command received: show version
Beta

我有一个客户端,其行为应与命令行测试完全相同,但是除非我在其他服务器上运行它,否则它将挂在第二个read()调用上。对于Unix域套接字,我也有同样的问题,只是它们偶尔会起作用。

为什么只在本地主机上失败?

客户来源

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define ERR_FAIL_CONNECT        -1
#define ERR_SOCK_SELECT         -2
#define ERR_SOCK_READ           -3
#define ERR_SOCK_WRITE          -3
#define ERR_SOCK_REMOTE_CLOSED  -4

int tcpConnect (char *ipAddr, int port);
int sendCommand (char *buf, int bufSize);
int readWithTimeout (int sock, char *buf, int bufSize, struct timeval *timeout);

int main() {
    char buf[64];
    int nBytes;

    strcpy(buf, "show version");
    nBytes = sendCommand(buf, sizeof(buf));
    printf("Response: %s\n", buf);

    return 0;
}

int sendCommand (char *buf, int bufSize) {
    int apiSock;
    int nBytes = ERR_SOCK_SELECT;
    int len;
    struct timeval timeout;

    apiSock = tcpConnect("127.0.0.1", 5000);
    if (!apiSock) return ERR_FAIL_CONNECT;

    len = strlen(buf);
    nBytes = write(apiSock, buf, len);
    if (nBytes < 0) {
        perror("ERROR writing to socket");
        nBytes = ERR_SOCK_WRITE;
    }
    else if (nBytes < len) {
        fprintf(stderr, "Command truncated at %d/%d\n", nBytes, len);
        nBytes = ERR_SOCK_WRITE;
    }
    else {
        timeout.tv_sec = 3;
        timeout.tv_usec = 0;
        nBytes = readWithTimeout(apiSock, buf, bufSize, &timeout);
        if (nBytes > 0) {
            timeout.tv_sec = 20;
            timeout.tv_usec = 0;
            nBytes = readWithTimeout(apiSock, buf, bufSize, &timeout);
        }
    }
    close(apiSock);

    return nBytes;
}

int tcpConnect (char *ipAddr, int port) {
    struct sockaddr_in addr;
    int sock;

    sock = socket(AF_INET, SOCK_STREAM, 0);
    if (sock == -1) {
        perror("ERROR: Could not create TCP socket");
        return 0;
    }

    addr.sin_addr.s_addr = inet_addr(ipAddr);
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);

    if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
        perror("ERROR: Could not connect");
        return 0;
    }

    return sock;
}

int readWithTimeout (int sock, char *buf, int bufSize, struct timeval *timeout) {
    int res;
    int nBytes = ERR_SOCK_SELECT;
    fd_set set;

    fprintf(stderr, "readWithTimeout(sock=%d, buf='%s', bufSize=%d, timeout{tv_sec=%d, tv_usec=%d})\n",
            sock, buf, bufSize, timeout->tv_sec, timeout->tv_usec);
    FD_ZERO(&set);
    FD_SET(sock, &set);

    res = select(sock+1, &set, NULL, NULL, timeout);
    if (res < 0) perror("ERROR waiting for data");
    else if (res == 0) fprintf(stderr, "Timed out waiting for data\n");
    else {
        nBytes = read(sock, buf, bufSize);
        if (nBytes < 0) {
            perror("ERROR reading from socket");
            nBytes = ERR_SOCK_READ;
        }
        else if (nBytes == 0) {
            fprintf(stderr, "Remote end closed socket\n");
            shutdown(sock, 2);
            close(sock);
            nBytes = ERR_SOCK_REMOTE_CLOSED;
        }
    }

    return nBytes;
}

服务器源

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#define TCP_PORT 5000
#define BUF_SIZE    512

int readCommand(int clientSockFd);
void myWrite (int fileDescriptor, const void *buf, size_t nbytes);

int main (void) {
    socklen_t client_len;
    int optval;
    int flags;
    struct sockaddr_in serv_addr, client_addr;

    int serverSockFd;
    int clientSockFd;

    fd_set set;
    struct timeval timeout;
    int rv;

    serverSockFd = socket(AF_INET, SOCK_STREAM, 0);
    if(serverSockFd < 0) perror("ERROR opening socket");

    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = INADDR_ANY;
    serv_addr.sin_port = htons(TCP_PORT);

    if(bind(serverSockFd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
        perror("Unable to bind TCP socket");
    }

    listen(serverSockFd, 5);
    client_len = sizeof(client_addr);
    flags = fcntl(serverSockFd, F_GETFL, 0);
    if (flags < 0) perror("Unable to read TCP socket flags");
    flags = flags|O_NONBLOCK;
    fcntl(serverSockFd, F_SETFL, flags);

    // Wait for client connections
    while(1) {
        clientSockFd = accept(serverSockFd, (struct sockaddr *) &client_addr, &client_len);
        if(clientSockFd < 0) {
            usleep(50000);
            continue;
        }

        //After connected, inner loop to read and write multiple packages
        while(1) {
            FD_ZERO(&set);
            FD_SET(clientSockFd, &set);
            timeout.tv_sec = 15;
            timeout.tv_usec = 0;

            rv = select(clientSockFd+1, &set, NULL, NULL, &timeout);
            if(rv == -1) {
                perror("select");
                continue;
            }
            else if(rv == 0) {
                printf("TCP timeout, closing client connection.\n");
                shutdown(clientSockFd, 2);
                break;
            }

            if (!readCommand(clientSockFd)) break;
        }

        close(clientSockFd);
    }

    close(serverSockFd);

    return 0;
}

int readCommand(int sock) {
    int nBytes;
    int len;
    char inBuf[BUF_SIZE];
    char outBuf[BUF_SIZE];

    nBytes = read(sock, inBuf, BUF_SIZE);
    if(nBytes < 0) perror("ERROR reading from TCP socket");
    else if(nBytes == 0) {
        printf("Client closed TCP socket\n");
        shutdown(sock, 2);
        return nBytes;
    }
    else {
        // Acknowledge command
        len = sprintf(outBuf, "Command received: %s", inBuf);
        if (write(sock, outBuf, len+1) < 0) {
            perror("ERROR writing to TCP socket");
        }

        // Send response data
        if (!strncmp("show version", inBuf, 12)) strcpy(outBuf, "Beta");
        else strcpy(outBuf, "Invalid command");
        if (write(sock, outBuf, strnlen(outBuf, BUF_SIZE-1)+1) < 0) {
            perror("ERROR writing to TCP socket");
        }
    }

    return nBytes;
}

1 个答案:

答案 0 :(得分:1)

当在本地主机上运行客户端时,我刚刚意识到确认和数据都在第一个read()中使用了。

因此,在尝试第二次读取之前,我需要解析第一次读取的结果。