【问题标题】:Unix-Server: Reading Thread behaves faultyUnix-Server:读取线程行为错误
【发布时间】:2019-11-23 11:28:54
【问题描述】:

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

但是,这个函数似乎做了以下事情: 如果终端输入/输出为空,服务器会打印: “客户端[消息]”而不将其发送回其他客户端。 但是,如果终端输入/输出不为空,它会发回数据,但不打印:“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;
}

编辑编辑编辑:

我得到的错误是在读数线程中:“错误阅读:未定义的错误”。如果我使用 xCode 启动服务器,似乎服务器在没有写入控制台的情况下崩溃了很多次。

已知错误:

  • 如果客户端断开线程和 文件描述符?

【问题讨论】:

  • OT:关于:#define TRUE 1 #define FALSE 0 C 标准库包含头文件:stdbool.h,它公开:truefalsebool
  • 关于:size_t n; ... n = read(newsockfd, buffer, BUFLEN); if(n){ error("Reading Failed"); }n中的值可以是正数(读取的字节数--或-- 0(连接的另一端关闭连接--或- -
  • OT:关于:if(socket_ids[i] == newsockfd)continue; 为了便于阅读和理解:,请遵循公理:每行只有一个语句,每个语句(最多)一个变量声明。
  • 关于:size_t n;write() 和/或read() 返回的值可以是0。所以变量n 必须是ssize_t 类型而不是size_t
  • 我会解决的,谢谢!

标签: c unix server


【解决方案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){

一旦您干净地编译了代码,请更新(添加编辑),我们可以帮助您

【讨论】:

  • 我的 xCode 使用 clang-compiler 完全没问题...对不起...我会去的!
  • 在使用xCode时,您仍然需要启用警告。否则编译器不会输出警告语句
  • 我不知道该怎么做,我在互联网上找不到类似的东西。
  • xCode 中的某些位置应该是cflags 的定义。这就是需要添加所需编译器选项的地方。我不使用xCode,所以我无法帮助您进行实际修复。
【解决方案2】:

您的代码有几个与使用read 的返回值相关的问题,正如 cmets 中所提到的。一般情况下,应该使用ssize_t数据类型作为返回值,分别检查n &gt; 0(成功)、n == 0(对方关闭连接)和n &lt; 0(错误)三种情况。

此外,bcopybzero 在 POSIX 中已弃用,应替换为 memcpymemset。此外,我必须将server-&gt;h_addr 替换为server-&gt;h_addr_list[0] 才能编译代码。手册页提到 h_addr 仅用于向后兼容。

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

解决方法很简单:在main 中删除int sockfd; 中的行client.c

【讨论】:

  • 谢谢!我修好了它。但是,我现在一直从服务器获得“”输入。这不应该与 fgets() 一起使用吗?据我所知, fgets() 仅在读取某些内容时返回。
  • 从客户端读取时,服务器仍然收到错误。
  • 更新:服务器甚至很好地(正确地)接收到消息,但仍然抛出错误。为什么?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-26
  • 1970-01-01
  • 1970-01-01
  • 2011-06-29
  • 1970-01-01
  • 2021-09-28
相关资源
最近更新 更多