简体   繁体   English

C套接字编程-服务器的write()写入服务器而不是客户端

[英]C socket programming - write() from server writes to server instead of client

I'm working on a TCP client server program which is supposed to support several clients using threads. 我正在研究一个TCP客户端服务器程序,该程序应支持使用线程的多个客户端。

The socket creation, connection, bind and accept work as expected since I receive no errors when running the code. 套接字创建,连接,绑定和接受工作按预期进行,因为在运行代码时我没有收到任何错误。 However whenever I try to read() from the server the code enters an infinite loop and nothing happens. 但是,每当我尝试从服务器读取()时,代码都会进入无限循环,并且什么也没有发生。

I tried writing from the server first and the write result was written to the server's terminal. 我首先尝试从服务器写入,然后将写入结果写入服务器的终端。

Client code: 客户代码:

#include <stdlib.h>
#include <stdio.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <dirent.h>

#define FILE_ADDR   "/dev/urandom"

int main(int argc, char *argv[]) {

    //Get command line arguments
    unsigned int port = atoi(argv[2]);
    int length = atoi(argv[3]); //Number of bytes to read
    char* buffer = malloc(length * sizeof(char)); //Buffer to hold data read from file
    char* recvBuf = malloc(10 * sizeof(char)); // Buffer to hold response from server

    struct addrinfo hints, *servinfo, *p;
    struct sockaddr_in serv_addr;
    int sockfd = -1;
    //int rv;
    //char ip[100];

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    int rv = getaddrinfo(argv[1], argv[2], &hints, &servinfo);
    if (rv != 0) {
        perror("getaddrinfo error\n");
        exit(1);
    }
    for (p = servinfo; p != NULL; p = p->ai_next) {
        //Initialize socket
        sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
        if (sockfd < 0)
            continue;
        //Initialize connection
        rv = connect(sockfd, p->ai_addr, (socklen_t) p->ai_addrlen);
        if (rv == 0)
            break;
        close(sockfd);
        sockfd = -1;
    }
    //   inet_aton(ip, &h.sin_addr);
    freeaddrinfo(servinfo);

    //Open file for reading
    FILE *fp;
    fp = fopen(FILE_ADDR, "r");
    if (fp == NULL) {
        perror("Error in file open\n");
    }
    printf("file opened\n");
    size_t numOfbytesRead = fread(buffer, sizeof(char), length, fp);
    if (numOfbytesRead != length) {
        perror("Error reading from file\n");
    }
    printf("Buffer is %s\n", buffer);

    char* ptr;
    unsigned int N = strtoul(argv[3],&ptr,10);
    int convertedNum = htonl(N);

    if (write(sockfd, &convertedNum, sizeof(unsigned int)) < 0) {   //Send number of bytes
        perror("error writing to socket");
    }
    if (write(sockfd, buffer, sizeof(buffer) < 0)) {//Send bytes read from file
        perror("error writing to socket");

    }

    printf("send is done \n");

    int bytes_read = read(sockfd, recvBuf, sizeof(recvBuf)); //Recieve response from server
    if (bytes_read <= 0) {
        perror("Error in recieving result from server\n");
    }

    unsigned int C = 0;
    sprintf(recvBuf[0], "%d", C);

    fclose(fp);
    printf("# of printable characters: %u\n", C);
    exit(0);
    free(buffer);
    free(recvBuf);

}

Server code: 服务器代码:

#include <stdlib.h>
#include <stdio.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <dirent.h>
#include <pthread.h>
#include <signal.h>

static volatile int keepRunning = 1;
int pcc_total[159];

void intHandler(int dummy) {
    keepRunning = 0;
}

void *compute(void *socket_desc) {
    int count = 0;
    int sock = *(int*) socket_desc;
    printf("now will allocate N \n");
    int n=0;
    if (write(sock, "hi", 2) < 0) { //Send number of bytes
        perror("error writing to socket\n");
    }
    if (read(sock, &n, sizeof(unsigned int)) < 0) {
        perror("Error reading from socket\n");
        exit(1);
    }
    int N = ntohl(n);
    printf("len is %d\n", N);
    char* data = calloc(N, sizeof(char));
    int len = read(sock, data, N);
    printf("data is %s\n", data);
    if (len < 0) {
        perror("Error reading from socket\n");
        exit(1);
    }
    for (int i = 0; i < len; i++) {
        int tmp = 0;
        sprintf(data[i], "%d", tmp);
        if (tmp >= 32 & tmp <= 126) {
            count++;

            __sync_fetch_and_add(&pcc_total[tmp], 1);
        }
    }
    char scount[100];
    atoi(count);
    write(sock, count, strlen(scount));
    free(data);
    pthread_exit(NULL);
    close(sock);
    exit(0);
}

int main(int argc, char *argv[]) {

    unsigned int port = atoi(argv[1]);
    signal(SIGINT, intHandler);

    int socket_desc, client_sock, c, *new_sock;
    struct sockaddr_in server, client;
    c = sizeof(struct sockaddr_in);

    socket_desc = socket( AF_INET, SOCK_STREAM, 0);
    if (socket_desc == -1) {
        perror("Could not create socket");
        exit(1);
    }
    printf("socket created\n");
    memset(&server, 0, c);

    server.sin_family = AF_INET;
    server.sin_addr.s_addr = htonl(INADDR_ANY);
    server.sin_port = htons(port);

    if (0 != bind(socket_desc, (struct sockaddr*) &server, sizeof(server))) {
        perror("\n Error : Bind Failed \n");
        exit(1);
    }
    printf("bind created\n");
    if (0 != listen(socket_desc, 10)) {
        perror("\n Error : Listen Failed \n");
        exit(1);
    }
    printf("listen created\n");
    while (keepRunning) {
        client_sock = accept(socket_desc, (struct sockaddr *) &client,
                (socklen_t*) &c);
        if (client_sock < 0) {
            perror("\n Error : Accept Failed\n");
            exit(1);
        }
        printf("accept created\n");
        pthread_t tid;
        new_sock = malloc(100*sizeof(int));
        *new_sock = client_sock;
        if ((pthread_create(&tid, NULL, compute, (void*) new_sock)) < 0) {
            perror("could not create thread\n");
            exit(1);
        }
        printf("thread created\n");
        // close socket
        close(client_sock);
        free(new_sock);
        pthread_join(tid, NULL);
    }

    exit(0);

}

I run the code with the following commands: 我使用以下命令运行代码:

gcc -std=c99 -O3 -Wall -o pcc_server pcc_server.c -pthread gcc -std = c99 -O3 -Wall -o pcc_server pcc_server.c -pthread

gcc -std=gnu99 -O3 -Wall -o pcc_client pcc_client.c gcc -std = gnu99 -O3 -Wall -o pcc_client pcc_client.c

There are a number of problems with your code. 您的代码有很多问题。

On the client side: 在客户端:

  • When calling fread() , you need to use "rb" instead of "r" . 调用fread() ,需要使用"rb"而不是"r"

  • when calling printf() to output the file data that was actually read, you are not null-terminating buffer , or passing its length to printf() . 当调用printf()输出实际读取的文件数据时,您不是以null结尾的buffer ,也不是将其length传递给printf() You need to do so. 您需要这样做。

  • You are assigning the return value of htonl() to an int instead of an unsigned int . 您正在将htonl()的返回值分配给int而不是unsigned int

  • when calling write() to send the buffer , you are using sizeof(buffer) when you should be using length or N instead (and why are you using two separate variables to hold the same command-line parameter value?). 当调用write()发送buffer时,您应该使用sizeof(buffer)来代替使用lengthN (为什么要使用两个单独的变量来保存相同的命令行参数值?)。 buffer is a pointer to memory allocated with malloc() , so sizeof(buffer) is the same as sizeof(void*) , which is not what you want. buffer是指向通过malloc()分配的内存的指针,因此sizeof(buffer)sizeof(void*) ,这不是您想要的。 Also, you are not even calling write() correctly, because your parenthesis are all wrong (they are correct on the previous write() call when sending convertedNum ). 同样,您甚至都没有正确调用write() ,因为您的括号全都错了(在发送convertedNum时,它们在上一个write()调用中是正确的)。

  • likewise, when calling read() to read the recvBuf , you are using sizeof(recvBuf) when you should be using 10 instead, sicne recvBuf is also a pointer to malloc ed memory. 同样,当调用read()读取recvBuf时,如果应使用sizeof(recvBuf) ,而应使用10 ,则sicne recvBuf也是指向已分配malloc的指针。

  • you are not reading the "hi" greeting that the server sends to the client upon connection, so you lump in those bytes with the bytes of the following size value of the next message, and thus end up with a corrupted C value. 您没有读取服务器在连接时发送给客户端的"hi"问候,因此您将这些字节与下一条消息的以下size值的字节混为一谈,从而导致C值损坏。

On the server side: 在服务器端:

  • your compute thread routine sends a "hi" greeting to the client, but it does not use any kind of delimiter, like prefixing the greeting with its length, or terminating it with a line break or null character or other unique character, to separate it from any subsequent data. 您的compute线程例程将"hi"问候语发送给客户端,但它不使用任何类型的定界符(例如,以问候语的长度作为前缀,或以换行符,空字符或其他唯一字符将其终止)来将其分隔从任何后续数据。 You should always delimit your messages in some manner. 您应该始终以某种方式分隔消息。

  • you are closing the accept ed socket and freeing the malloc ed new_sock as soon as you create a worker thread to handle that client. 创建工作线程来处理该客户端后,您将关闭accept套接字并释放malloc new_sock You are ripping away memory from behind the thread's proverbial back. 您正在从线程众所周知的后面剥离内存。 The thread needs to be the one to close the socket and free the memory when it is done using them, not the accept loop. 使用完线程后,线程必须是关闭套接字并释放内存的线程,而不是accept循环。

    The thread does attempt to close the socket (but not free the memory), but after it calls pthread_exit() first, which is wrong. 线程确实尝试关闭套接字(但不释放内存),但是它首先调用pthread_exit() ,这是错误的。 pthread_exit() terminates the calling thread, so it needs to be the last thing that the thread calls (DO NOT call exit() !). pthread_exit()终止了调用线程,因此它必须是线程调用的最后一件事(不要调用exit() !)。 In fact, don't even call pthread_exit() directly at all, just return from compute() , the pthreads library will then call pthread_exit() for you, passing it whatever void* value you choose to return . 实际上,甚至根本不直接调用pthread_exit() ,仅从compute() return ,然后pthreads库将为您调用pthread_exit() ,并将您选择return任何void*值传递给它。

  • your accept loop should not be calling pthread_join() at all. 您的accept循环根本不应该调用pthread_join() It blocks the calling thread until the specified thread terminates. 它阻塞调用线程,直到指定线程终止。 That defeats the whole purpose of using threads to handle your clients, and prevents your server from accepting more than 1 client at a time. 这破坏了使用线程来处理您的客户端的整个目的,并阻止您的服务器一次接受一个以上的客户端。 If you are going to use pthread_join() at all, it should be after the accept loop has ended, so you can wait for any worker threads that may still be running before exiting the app. 如果要使用pthread_join() ,则应该在accept循环结束之后使用,因此可以在退出应用程序之前等待可能仍在运行的任何辅助线程。 But that also means keeping track of the pthread_t values that pthread_create() returns, which is more work. 但这还意味着跟踪pthread_create()返回的pthread_t值,这需要更多工作。

With that said, try this code instead: 话虽如此,请尝试以下代码:

Client code: 客户代码:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <dirent.h>

#define FILE_ADDR   "/dev/urandom"

char* readMsg(int sockfd, size_t *msgSize)
{
    *msgSize = 0;

    unsigned int length = 0;
    int bytes_read = read(sockfd, &length, sizeof(length)); //Receive number of bytes
    if (bytes_read <= 0) {
        perror("Error in receiving message from server\n");
        return NULL;
    }
    length = ntohl(length);

    char *buffer = malloc(length+1);
    if (!buffer) {
        perror("Error in allocating memory to receive message from server\n");
        return NULL;
    }

    char *pbuf = buffer;
    unsigned int buflen = length;
    while (buflen > 0) {
        bytes_read = read(sockfd, pbuf, buflen); // Receive bytes
        if (bytes_read <= 0) {
            perror("Error in receiving message from server\n");
            free(buffer);
            return NULL;
        }
        pbuf += bytes_read;
        buflen -= bytes_read;
    }

    *msgSize = length;
    return buffer;
}

int sendMsg(int sockfd, char *msg, size_t msgSize)
{
    unsigned int convertedNum = htonl(msgSize);
    if (write(sockfd, &convertedNum, sizeof(convertedNum)) < 0) { //Send number of bytes
        perror("error writing to socket");
        return -1;
    }

    if (write(sockfd, msg, msgSize) < 0) { //Send bytes
        perror("error writing to socket");
        return -1;
    }

    return 0;
}

int main(int argc, char *argv[]) {

    char* ptr;

    //Get command line arguments
    unsigned int port = atoi(argv[2]);
    unsigned int length = strtoul(argv[3], &ptr, 10); //Number of bytes to read
    char* buffer = malloc(length); //Buffer to hold data read from file

    struct addrinfo hints, *servinfo, *p;
    struct sockaddr_in serv_addr;
    int sockfd = -1;

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_protocol = IPPROTO_TCP;

    int rv = getaddrinfo(argv[1], argv[2], &hints, &servinfo);
    if (rv != 0) {
        perror("getaddrinfo error\n");
        return 1;
    }
    for (p = servinfo; p != NULL; p = p->ai_next) {
        //Initialize socket
        sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
        if (sockfd < 0)
            continue;
        //Initialize connection
        rv = connect(sockfd, p->ai_addr, (socklen_t) p->ai_addrlen);
        if (rv == 0)
            break;
        close(sockfd);
        sockfd = -1;
    }
    freeaddrinfo(servinfo);

    if (sockfd == -1) {
        perror("socket create/connect error\n");
        return 1;
    }

    size_t msgSize;
    char *msg = readMsg(sockfd, &msgSize);
    if (!msg) {
        close(sockfd);
        return 1;
    }
    printf("%.*s\n", (int)msgSize, msg);
    free(msg);

    //Open file for reading
    FILE *fp = fopen(FILE_ADDR, "rb");
    if (fp == NULL) {
        perror("Error in file open\n");
        close(sockfd);
        return 1;
    }
    printf("file opened\n");

    if (fread(buffer, 1, length, fp) != length) {
        perror("Error reading from file\n");
        fclose(fp);
        close(sockfd);
        return 1;
    }
    fclose(fp);
    printf("Buffer is %.*s\n", (int)length, buffer);

    if (sendMsg(sockfd, buffer, length) != 0) {
        free(buffer);
        close(sockfd);
        return 1;
    }
    free(buffer);
    printf("send is done \n");

    msg = readMsg(sockfd, &msgSize); // response from server
    if (!msg) {
        close(sockfd);
        return 1;
    }
    printf("# of printable characters: %.*s\n", (int)msgSize, msg);
    free(msg);

    return 0;
}

Server code: 服务器代码:

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <dirent.h>
#include <pthread.h>
#include <signal.h>

static volatile int keepRunning = 1;
int pcc_total[159];

void intHandler(int dummy) {
    keepRunning = 0;
}

char* readMsg(int sockfd, size_t *msgSize)
{
    *msgSize = 0;

    unsigned int length = 0;
    int bytes_read = read(sockfd, &length, sizeof(length)); //Receive number of bytes
    if (bytes_read <= 0) {
        perror("Error in receiving message from server\n");
        return NULL;
    }
    length = ntohl(length);

    char *buffer = malloc(length+1);
    if (!buffer) {
        perror("Error in allocating memory to receive message from server\n");
        return NULL;
    }

    char *pbuf = buffer;
    unsigned int buflen = length;
    while (buflen > 0) {
        bytes_read = read(sockfd, pbuf, buflen); // Receive bytes
        if (bytes_read <= 0) {
            perror("Error in receiving message from server\n");
            free(buffer);
            return NULL;
        }
        pbuf += bytes_read;
        buflen -= bytes_read;
    }

    *msgSize = length;
    return buffer;
}

int sendMsg(int sockfd, char *msg, size_t msgSize)
{
    unsigned int convertedNum = htonl(msgSize);
    if (write(sockfd, &convertedNum, sizeof(convertedNum)) < 0) { //Send number of bytes
        perror("error writing to socket");
        return -1;
    }

    if (write(sockfd, msg, msgSize) < 0) { //Send bytes
        perror("error writing to socket");
        return -1;
    }

    return 0;
}

void *compute(void *socket_desc) {
    int sock = * (int*) socket_desc;
    free(socket_desc);

    if (sendMsg(sock, "hi", 2) != 0) {
        perror("error writing to socket\n");
        close(sock);
        return NULL;
    }

    size_t length = 0;
    char *data = readMsg(sock, &length);
    if (!msg) {
        close(sock);
        return NULL;
    }
    printf("len is %d\n", (int)length);
    printf("data is %.*s\n", (int)length, data);

    int count = 0;
    for (size_t i = 0; i < length; i++) {
        // ...
    }
    free(data);

    char scount[20];
    sprintf(scount, "%d", count);
    sendMsg(sock, scount, strlen(scount));

    close(sock);
    return NULL;
}

int main(int argc, char *argv[]) {

    unsigned int port = atoi(argv[1]);
    signal(SIGINT, intHandler);

    int socket_desc, client_sock, c, *new_sock;
    struct sockaddr_in server, client;

    socket_desc = socket( AF_INET, SOCK_STREAM, 0);
    if (socket_desc == -1) {
        perror("Could not create server socket");
        return 1;
    }
    printf("server socket created\n");

    memset(&server, 0, c);
    server.sin_family = AF_INET;
    server.sin_addr.s_addr = htonl(INADDR_ANY);
    server.sin_port = htons(port);

    if (bind(socket_desc, (struct sockaddr*) &server, sizeof(server)) < 0) {
        perror("\n Error : Bind Failed \n");
        close(socket_desc);
        return 1;
    }
    printf("bind created\n");

    if (listen(socket_desc, 10) < 0) {
        perror("\n Error : Listen Failed \n");
        close(socket_desc);
        return 1;
    }
    printf("listening\n");

    while (keepRunning) {
        c = sizeof(client);
        client_sock = accept(socket_desc, (struct sockaddr *) &client,
                            (socklen_t*) &c);
        if (client_sock < 0) {
            perror("\n Error : Accept Failed\n");
            continue;
        }
        printf("client accepted\n");

        new_sock = malloc(sizeof(int));
        if (!new_sock) {
            perror("\n Error : Malloc Failed\n");
            close(client_sock);
            continue;
        }
        *new_sock = client_sock;

        pthread_t tid;
        if (pthread_create(&tid, NULL, &compute, new_sock) != 0) {
            perror("\n Error : Thread Create Failed\n");
            free(new_sock);
            close(client_sock);
            continue;
        }
        printf("thread created\n");
    }

    close(socket_desc);
    return 0;
}

I think you should remove the two lines 我认为您应该删除这两行

close(client_sock);
free(new_sock);

in the server code because the newly created thread can't perform on those variables and memory area if it is freed up at such an early point. 在服务器代码中,因为如果在这么早的时间释放了新创建的线程,它们将无法在这些变量和内存区域上执行。 Can you try the code again without it? 如果没有它,您可以再次尝试代码吗?

Your server closes the connected socket and frees the memory in which you stored its file handle immediately after launching the thread to handle that connection. 服务器在启动处理该连接的线程后立即关闭连接的套接字并释放存储文件句柄的内存。 You're unlucky that the server only hangs as a result, but you have data races, so formally your program's behavior is undefined. 您很不幸服务器只会因此而挂起,但是您遇到了数据争用问题,因此正式而言,程序的行为是不确定的。

Since the server isn't going to do anything else until the thread has finished, you might as well move the close() and free() after the pthread_join() . 由于服务器在线程完成之前不会执行任何其他操作,因此您最好将pthread_join() 之后close()free()移到该线程上。 Or, considering that you do join before creating any other threads, how about just calling compute() synchronously instead of creating a new thread for it to run in? 或者,考虑到您确实在创建任何其他线程之前加入了,如何仅同步调用compute()而不是创建一个新线程以使其运行?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM