TCP并发回显服务器无法从第一个客户端读取字符串

时间:2014-09-30 05:01:35

标签: c linux sockets tcp

我正在实现一个简单的TCP进程每客户端并发回显服务器。服务器无法从第一个客户端读取字符串。但是对于其他客户端,服务器会正​​确回送字符串。我觉得阅读字符串可能有些问题;服务器端无法检测到EOF。我尝试使用send()/ recv()而不是read()/ write()但没有成功。我还尝试了从stdin读取字符串的其他函数 - 即fgets(),scanf()等。

这个问题的可能原因是什么;即服务器无法仅从第一个客户端读取?

服务器和客户端的代码位于 -

之下
// server.c
#include <signal.h>
#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#define BUF_SIZE 4096
#define MAXPENDING 256

static void zombieReaper(int sig)
{
    while (waitpid(-1, NULL, WNOHANG) > 0)
        continue;
}

static void handleRequest(int cfd)
{
    char buf[BUF_SIZE];
    int numread;
    int size = BUF_SIZE;
    int i;

    printf("\n inside handling request fd : %d ", cfd);
    fflush(stdout);

    while (1) {
        numread = read(cfd, buf, size);
        if (numread > 0) {
            int l = strlen(buf);
            printf("Echo server numread : %d len : %d buf : %s\n", numread, l, buf);
            fflush(stdout);
            buf[l] = '\0';
            write(cfd, buf, size);
        }
    }
}

int main(int argc, char *argv[])
{
    int lfd, cfd;
    struct sigaction sa;

    sigemptyset(&sa.sa_mask);
    sa.sa_handler = zombieReaper;
    if (sigaction(SIGCHLD, &sa, NULL) == -1) {
        printf("SIGCHILD signal");
        exit(0);
    }

    struct sockaddr_in serv_addr, clnt_addr;
    lfd = socket(AF_INET, SOCK_STREAM, 0);
    if (lfd == -1) {
        printf("Socket Failed");
        exit(0);
    }

    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(atoi(argv[1]));
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);

    if (bind(lfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
        printf("bind() failed");
        exit(0);
    }

    if (listen(lfd, MAXPENDING) < 0) {
        printf("listen() failed");
        exit(0);
    }

    socklen_t clen = sizeof(clnt_addr);
    int status = 0;
    for (;;) {
        if (cfd = accept(lfd, (struct sockaddr *) &clnt_addr, &clen) < 0) {
            printf("accept() failed");
        } else {
            printf("Accepted client cfd : %d\n", cfd);
            switch (fork()) {
            case -1:
                close(cfd);
                break;
            case 0:            // Child
                close(lfd);
                handleRequest(cfd);
                exit(1);
            default:           // Parent
                close(cfd);
                break;
            }
        }
    }
}

// client.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#define BUF_SIZE 4096

int main(int argc, char *argv[])
{
    int clnt_fd;
    struct sockaddr_in serv_addr;
    //memset(&serv_addr, 0, sizeof(serv_addr));
    char cmd[BUF_SIZE];
    char buf[BUF_SIZE];

    clnt_fd = socket(PF_INET, SOCK_STREAM, 0);
    if (clnt_fd == -1) {
        printf("socket() failed");
        exit(0);
    }

    // prepare for connect
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(atoi(argv[2]));
    serv_addr.sin_addr.s_addr = inet_addr(argv[1]);

    // connect to server
    if (connect(clnt_fd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0) {
        printf("connect() failed");
        exit(0);
    }

    int size = BUF_SIZE;
    while (1) {
        printf("\n Enter a string (q to Exit) : \n ");
        gets(cmd);
        if (!strcmp(cmd, "q"))
            break;

        int n = write(clnt_fd, cmd, strlen(cmd));
        n = read(clnt_fd, buf, BUF_SIZE);
        printf("\n Received Results : \n %s\n", buf);
    }
    close(clnt_fd);

    return 0;
}

2 个答案:

答案 0 :(得分:1)

代码中的问题是,

if (cfd = accept(lfd, (struct sockaddr *) &clnt_addr, &clen) < 0)

这里当你接受时,你正在使用关系运算符

验证后,返回值0存储在cfd。

假设原始的accept套接字id为4,但返回值为0

根据您在程序中执行某些操作的返回值。

尝试使用条件的优先级,例如

if ((cfd = accept(lfd, (struct sockaddr *) &clnt_addr, &clen)) < 0)

现在它打印完全接受的套接字ID。

答案 1 :(得分:0)

if (numread > 0) {
        int l = strlen(buf);

这里假设缓冲区中有空值。相反,您应该只使用下面的numread

        printf("Echo server numread : %d len : %d buf : %s\n", numread, l, buf);

这里再次假设缓冲区中存在空值。它应该是

        printf("Echo server numread : %d buf : %.*s\n", numread, numread, buf);

现在:             buf [l] =&#39; \ 0&#39 ;;

如果您找到它,那么您将在您已经找到它的地方放置一个null。删除。

        write(cfd, buf, size);

在这里,您将错误的数据量写回发件人。它应该是write(cfd, buf, numread).         }

在这里,如果numread <= 0,你将无法摆脱循环。

printf("socket() failed");
printf("connect() failed");
printf("bind() failed");
printf("listen() failed");
printf("accept() failed");

在这些地方中,您打印的是您自己设计的相当无用的错误消息,并忽略实际错误。不要这样做。在每个地方将printf更改为perror,或将strerror()的结果添加到邮件中。