Unix服务器:读取线程行为错误

时间:2019-07-14 14:27:00

标签: c unix server

我写了一个服务器,该服务器应该是终端聊天应用程序(仅用于练习)。 为了读取传入的消息,我为每个客户端创建了一个线程,其唯一目的是读取传入的文本...

但是,此功能似乎可以执行以下操作: 如果终端输入/输出为空,则服务器打印: “客户端[消息]”,而不会发送回其他客户端。 但是,如果终端输入/输出不为空,它将发送回数据,但不打印:“ Client [message]”。我不能完全把握这个错误。此外,当客户端断开连接时,服务器退出。

这是服务器:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>

#define BUFLEN 255
#define MAX_CONNECTIONS 128
#define TRUE 1
#define FALSE 0

void* job_read(void * p);
void* job_write(void*);

//Global Variables
FILE* plogfile;
int socket_ids[MAX_CONNECTIONS];
char endprogramm = FALSE;
int open_cnncts = 0;
pthread_mutex_t mutex;

void error(const char* msg){
    perror(msg);
    exit(1);
}

int main(int argc, char* argv[]) {
    if(argc < 2){
        fprintf(stderr, "You must provide a port number");
        exit(EXIT_FAILURE);
    }
    if(argc == 3){
        plogfile = fopen(argv[2], "w");
    } else {
        plogfile = fopen("logfile.txt", "w");
    }
    stderr = plogfile;
    int sockfd, portnum;

    //Create nmutthread
    if(pthread_mutex_init(&mutex, NULL)<0){
        error("Could not initialize Mutex");
    }
    //Initialzing threads and create writethread
    pthread_t readthreads[MAX_CONNECTIONS];
    pthread_t writethread;
    pthread_create(&writethread, NULL, job_write, NULL);

    //Setup for connections
    struct sockaddr_in serv_add, cli_adr;
    socklen_t clilen;
    clilen = sizeof(cli_adr);
    bzero((char*)&serv_add, sizeof(struct sockaddr_in));

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

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0){
        error("Error opening socket.");
    }

    //Bind listening
    if(bind(sockfd, (struct sockaddr*) (&serv_add), sizeof(serv_add)) < 0){
        error("Binding failed.");
    }
    for(open_cnncts = 0; (!endprogramm) & (open_cnncts < MAX_CONNECTIONS); open_cnncts++){
        fprintf(plogfile,"Listening....");
        listen(sockfd, MAX_CONNECTIONS);
        socket_ids[open_cnncts] = accept(sockfd, (struct sockaddr*) &cli_adr, &clilen);
        fprintf(plogfile,"Client connected.\n");
        pthread_create(&readthreads[open_cnncts] , NULL, job_read, (void*)&socket_ids[open_cnncts]);
    }
    endprogramm = TRUE;
    close(sockfd);
    for(; open_cnncts != 0; open_cnncts--){
        close(socket_ids[open_cnncts]);
        pthread_join(readthreads[open_cnncts], NULL);
    }
    pthread_join(writethread, NULL);
    pthread_mutex_destroy(&mutex);
    return 0;

}

void* job_read(void * p){
    int* socketp = (int*)p;
    int newsockfd = (*socketp);
    size_t n;
    char buffer[BUFLEN];
    while(!endprogramm){
        bzero(buffer, BUFLEN);
        n = read(newsockfd, buffer, BUFLEN);
        if(n){
            error("Reading Failed");
        }
        pthread_mutex_lock(&mutex);
        for(int i = 0; i < open_cnncts; i++){
            if(socket_ids[i] == newsockfd)continue;
            n = write(socket_ids[i], buffer, strlen(buffer));
            if(n < 0){
                error("Writing failed");
            }
        }
        pthread_mutex_unlock(&mutex);
        printf("Client: %s\n", buffer);
    }
    return NULL;
}

void* job_write(void* args){
    fprintf(plogfile, "Started writing thread...\n");
    size_t n;
    char buffer[BUFLEN];
    while(!endprogramm) {
        bzero(buffer, BUFLEN);
        fgets(buffer, BUFLEN, stdin);

        pthread_mutex_lock(&mutex);
        for(int i = 0; i < open_cnncts; i++){
            n = write(socket_ids[i], buffer, strlen(buffer));
            if(n < 0){
                error("Writing failed");
            }
        }
        pthread_mutex_unlock(&mutex);
        if(strcmp("Bye", buffer) == 0){
            break;
        }
    }
    endprogramm = TRUE;
    return NULL;
}

我认为该错误位于此处:

void* job_write(void* args){
fprintf(plogfile, "Started writing thread...\n");
size_t n;
char buffer[BUFLEN];
while(!endprogramm) {
    bzero(buffer, BUFLEN);
    fgets(buffer, BUFLEN, stdin);

    pthread_mutex_lock(&mutex);
    for(int i = 0; i < open_cnncts; i++){
        n = write(socket_ids[i], buffer, strlen(buffer));
        if(n < 0){
            error("Writing failed");
        }
    }
    pthread_mutex_unlock(&mutex);
    if(strcmp("Bye", buffer) == 0){
        break;
    }
}
endprogramm = TRUE;
return NULL;
}

在这里输入终端:

Terminal 1:
./Server 9999 
...
Client: "Hello"
...
...


Terminal 2:
./Client 127.0.0.1 9999
Hello
...
Hello
Server: Hello

如果您想重现该错误,请使用以下代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <pthread.h>

#define BUFLEN 255
#define TRUE 1
#define FALSE 0

char endprogram = 0;
int sockfd;


void error(const char* msg){
    perror(msg);
    exit(1);
}

void* job_read(void* p){
    char buffer[BUFLEN];
    while(!endprogram){
        bzero(buffer, BUFLEN);
        size_t n = read(sockfd, buffer, (BUFLEN));
        if(n < 0){
            error("Error on reading");
        }
        printf("Server: %s", buffer);
        int i = strncmp("Bye", buffer, 3);
        if(i == 0){
            endprogram = TRUE;
            return NULL;
        }
    }
    return NULL;
}

int main(int argc, const char * argv[]) {
    pthread_t readt;

    int sockfd, portnum;
    struct sockaddr_in serveraddr;
    struct hostent* server;

    if(argc < 3){
        perror("You shall provide a port and a ip adress");
    }
    portnum = atoi(argv[2]);
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0){
        error("Error opening socket");
    }

    server = gethostbyname(argv[1]);
    if(!server){
        error("No such host");
    }

    bzero((char*)&serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    bcopy((char *)server->h_addr, (char *)&serveraddr.sin_addr.s_addr, sizeof(server->h_length));
    serveraddr.sin_port = htons(portnum);

    if(connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr))<0){
        error("Connection failed");
    }

    pthread_create(&readt, NULL, &job_read, NULL);

    size_t n;
    char buffer[BUFLEN];
    while(!endprogram){
        bzero(buffer, BUFLEN);
        fgets(buffer, BUFLEN, stdin);
        n = write(sockfd, buffer, strlen(buffer));
        if(n < 0){
            error("Error on writing");
        }
        n = strcmp(buffer, "Bye");
        if(n == 0){
            endprogram = TRUE;
        }
    }
    pthread_join(readt, NULL);
    close(sockfd);
    return 0;
}

编辑:服务器应该打印客户端的消息并将其写回所有其他客户端...

编辑编辑:

我希望这段代码对您来说更易于编译和阅读:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdbool.h>

#define BUFLEN 255
#define MAX_CONNECTIONS 128


void* job_read(void * p);
void* job_write(void*);

//Global Variables
FILE* plogfile;
int socket_ids[MAX_CONNECTIONS];
bool endprogramm = false;
int open_cnncts = 0;
pthread_mutex_t mutex;

void error(const char* msg){
    perror(msg);
    exit(1);
}

int main(int argc, char* argv[]) {
    if(argc < 2){
        fprintf(stderr, "You must provide a port number");
        exit(EXIT_FAILURE);
    }
    if(argc == 3){
        plogfile = fopen(argv[2], "w");
    } else {
        plogfile = fopen("logfile.txt", "w");
    }
    stderr = plogfile;
    int sockfd;
    uint16_t portnum;

    //Create nmutthread
    if(pthread_mutex_init(&mutex, NULL)<0){
        error("Could not initialize Mutex");
    }
    //Initialzing threads and create writethread
    pthread_t readthreads[MAX_CONNECTIONS];
    pthread_t writethread;
    pthread_create(&writethread, NULL, job_write, NULL);

    //Setup for connections
    struct sockaddr_in serv_add;
    struct sockaddr_in cli_adr;
    socklen_t clilen;
    clilen = sizeof(cli_adr);
    bzero((char*)&serv_add, sizeof(struct sockaddr_in));

    portnum = (uint16_t)atoi(argv[1]);
    serv_add.sin_family = AF_INET;
    serv_add.sin_addr.s_addr = INADDR_ANY;
    serv_add.sin_port = htons(portnum);

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0){
        error("Error opening socket.");
    }

    //Bind listening
    if(bind(sockfd, (struct sockaddr*) (&serv_add), sizeof(serv_add)) < 0){
        error("Binding failed.");
    }
    for(open_cnncts = 0; (!endprogramm) & (open_cnncts < MAX_CONNECTIONS); open_cnncts++){
        fprintf(plogfile,"Listening....");
        listen(sockfd, MAX_CONNECTIONS);
        socket_ids[open_cnncts] = accept(sockfd, (struct sockaddr*) &cli_adr, &clilen);
        fprintf(plogfile,"Client connected.\n");
        pthread_create(&readthreads[open_cnncts] , NULL, job_read, (void*)&socket_ids[open_cnncts]);
    }
    endprogramm = true;
    close(sockfd);
    for(; open_cnncts != 0; open_cnncts--){
        close(socket_ids[open_cnncts]);
        pthread_join(readthreads[open_cnncts], NULL);
    }
    pthread_join(writethread, NULL);
    pthread_mutex_destroy(&mutex);
    return 0;

}

void* job_read(void * p){
    int* socketp = (int*)p;
    int newsockfd = (*socketp);
    ssize_t n;
    char buffer[BUFLEN];
    while(!endprogramm){
        bzero(buffer, BUFLEN);
        n = read(newsockfd, buffer, BUFLEN);
        if(n){
            error("Reading Failed");
        }
        pthread_mutex_lock(&mutex);
        for(int i = 0; i < open_cnncts; i++){
            if(socket_ids[i] == newsockfd){
                continue;
            }
            n = write(socket_ids[i], buffer, strlen(buffer));
            if(n < 0){
                error("Writing failed");
            }
        }
        pthread_mutex_unlock(&mutex);
        printf("Client: %s\n", buffer);
    }
    pthread_exit( NULL );
}

void* job_write(void* args){
    (void)args;
    fprintf(plogfile, "Started writing thread...\n");
    ssize_t n;
    char buffer[BUFLEN];
    while(!endprogramm) {
        fgets(buffer, BUFLEN, stdin);

        pthread_mutex_lock(&mutex);
        for(int i = 0; i < open_cnncts; i++){
            n = write(socket_ids[i], buffer, strlen(buffer));
            if(n < 0){
                error("Writing failed");
            }
        }
        pthread_mutex_unlock(&mutex);
        if(strcmp("Bye", buffer) == 0){
            break;
        }
    }
    endprogramm = true;
    pthread_exit( NULL );
}

客户:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <pthread.h>
#include <stdbool.h>

#define BUFLEN 255


bool endprogram = false;
int sockfd;


void error(const char* msg){
    perror(msg);
    exit(1);
}

void* job_read(void* p){
    (void)p;
    char buffer[BUFLEN];
    while(!endprogram){
        bzero(buffer, BUFLEN);
        size_t n = read(sockfd, buffer, (BUFLEN));
        if(n < 0){
            error("Error on reading");
        }
        printf("Server: %s", buffer);
        int i = strncmp("Bye", buffer, 3);
        if(i == 0){
            endprogram = true;
            return NULL;
        }
    }
    return NULL;
}

int main(int argc, const char * argv[]) {
    pthread_t readt;

    int sockfd;
    int16_t portnum;
    struct sockaddr_in serveraddr;
    struct hostent* server;

    if(argc < 3){
        perror("You shall provide a port and a ip adress");
    }
    portnum = atoi(argv[2]);
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0){
        error("Error opening socket");
    }

    server = gethostbyname(argv[1]);
    if(!server){
        error("No such host");
    }

    bzero((char*)&serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    bcopy((char *)server->h_addr, (char *)&serveraddr.sin_addr.s_addr, sizeof(server->h_length));
    serveraddr.sin_port = htons(portnum);

    if(connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr))<0){
        error("Connection failed");
    }

    pthread_create(&readt, NULL, &job_read, NULL);

    ssize_t n;
    char buffer[BUFLEN];
    while(!endprogram){
        fgets(buffer, BUFLEN, stdin);
        n = write(sockfd, buffer, strlen(buffer));
        if(n < 0){
            error("Error on writing");
        }
        n = strcmp(buffer, "Bye");
        if(n == 0){
            endprogram = false;
        }
    }
    pthread_join(readt, NULL);
    close(sockfd);
    return 0;
}

EDIT EDIT EDIT:

我得到的错误在读数线程中:“错误读数:未定义的错误”。如果我使用xCode启动服务器,则似乎服务器多次崩溃而未编写控制台。

已知错误:

  • 如果客户端断开了对线程的处理,则 文件描述符?

2 个答案:

答案 0 :(得分:1)

发布的代码无法完全编译!。

以下列出了主要问题。

编译时,请始终启用警告,然后修复这些警告。

对于gcc,至少要使用:-Wall -Wextra -Wconversion -pedantic -std=gnu11

注意:其他编译器使用不同的选项来执行相同的操作。

untitled.c: In function ‘main’:
untitled.c:61:31: warning: conversion to ‘uint16_t {aka short unsigned int}’ from ‘int’ may alter its value [-Wconversion]
     serv_add.sin_port = htons(portnum);
                               ^~~~~~~

untitled.c: In function ‘job_read’:
untitled.c:98:13: warning: conversion to ‘size_t {aka long unsigned int}’ from ‘ssize_t {aka long int}’ may change the sign of the result [-Wsign-conversion]
         n = read(newsockfd, buffer, BUFLEN);
             ^~~~

untitled.c:105:17: warning: conversion to ‘size_t {aka long unsigned int}’ from ‘ssize_t {aka long int}’ may change the sign of the result [-Wsign-conversion]
             n = write(socket_ids[i], buffer, strlen(buffer));
                 ^~~~~

untitled.c:106:18: warning: comparison of unsigned expression < 0 is always false [-Wtype-limits]
             if(n < 0){
                  ^

untitled.c: In function ‘job_write’:
untitled.c:126:17: warning: conversion to ‘size_t {aka long unsigned int}’ from ‘ssize_t {aka long int}’ may change the sign of the result [-Wsign-conversion]
             n = write(socket_ids[i], buffer, strlen(buffer));
                 ^~~~~

untitled.c:127:18: warning: comparison of unsigned expression < 0 is always false [-Wtype-limits]
             if(n < 0){
                  ^

untitled.c:116:23: warning: unused parameter ‘args’ [-Wunused-parameter]
 void* job_write(void* args){

一旦您干净地编译了代码,请进行更新(添加EDIT),我们将为您提供帮助

答案 1 :(得分:0)

您的代码存在一些与使用read的返回值有关的问题,如注释中所述。通常,应使用ssize_t数据类型作为返回值,并分别检查三种情况n > 0(成功),n == 0(另一边关闭连接)和{ {1}}(错误)。

此外,n < 0bcopy在POSIX中已过时,应替换为bzeromemcpy。此外,我必须用memset替换server->h_addr才能编译代码。该手册页提到server->h_addr_list[0]仅用于向后兼容。

由于导致消息传递异常的主要问题是:永远不要初始化h_addr中的全局sockfd变量,因为它会被client.c中的声明所遮盖。由于它具有静态作用域,因此它会自动初始化为零,即标准输入。因此,mainread(sockfd, ...)中的job_read实际上是从标准输入而不是从服务器读取的。客户端从不从服务器读取。相反,您输入的每一行都将在client.c中处理,然后发送到服务器。其他行将在main中处理,然后以不正确的前缀job_read打印。

解决方法很简单:在Server:的{​​{1}}中删除行int sockfd;