Сокеты Linux: второе чтение () не работает на локальном - PullRequest
0 голосов
/ 07 декабря 2018

У меня есть сервер, который подтверждает команду, а затем отправляет данные.Он отлично работает с командной строкой: echo "show version" | nc -q1 127.0.0.1 5000 дает:

Command received: show version
Beta

У меня есть клиент, который должен вести себя точно так же, как тест командной строки, но он зависает при втором вызове read(), если только язапустить его на другом сервере.У меня была такая же проблема с доменными сокетами unix, за исключением того, что они иногда работают.

Почему бы не работать только на localhost?

Источник клиента

#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 голосов
/ 07 декабря 2018

Я только что понял, что и подтверждение, и данные были использованы в первом read() при запуске клиента на локальном хосте.

Поэтому мне нужно проанализировать результат первого чтения, прежде чем пытаться выполнить второе.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...