在多线程聊天应用程序中使用 fgets 显示命令提示符

Display command prompt with fgets in multithreaded chat application

我有一个命令行聊天应用程序,但我似乎无法弄清楚如何为每个消息条目显示 "Enter a message:" 提示。这是一个超级简单的问题,但是多线程和 client/server 关系让我很困惑。

客户端代码:

#define PORT 12000
#define BUFFER 4096

void * receive(void * socket) {
    int socket_fd, response;
    char buffer[BUFFER];
    socket_fd = (int) socket;
    while(true) {
        response = recvfrom(socket_fd, buffer, BUFFER, 0, NULL, NULL);
        if (response) {
            printf("Server: %s", buffer);
        }
    }
}

int main(int argc, char**argv) {
    struct sockaddr_in address, cl_addr;
    char * server_address;
    int socket_fd, response;
    char buffer[BUFFER];
    pthread_t thread;

    if (argc < 2) {
        printf("Usage: client [IP Address]\n");
        exit(1);
    }

    server_address = argv[1];
    address.sin_family = AF_INET;
    address.sin_addr.s_addr = inet_addr(server_address);
    address.sin_port = PORT;
    socket_fd = socket(AF_INET, SOCK_STREAM, 0);

    response = connect(socket_fd, (struct sockaddr *) &address, sizeof(address));
    if (response < 0) {
        printf("Failed to connect\n");
        exit(1);
    }

    printf("Connected\n");

    // Create new thread to receive messages
    pthread_create(&thread, NULL, receive, (void *) socket_fd);

    // Get message from stdin and send to server
    printf("Enter a message: ");
    while (fgets(buffer, BUFFER, stdin) != NULL) {
        sendto(socket_fd, buffer, BUFFER, 0, (struct sockaddr *) &address, sizeof(address));
    }

    close(socket_fd);
    pthread_exit(NULL);
    return 0;
}

问题是 "Enter a message:" 只显示一次。我已经尝试将它添加到 while(fgets) 循环中,它成功地将提示放在客户端写入的每一行上,但是当服务器发送某些内容时,它会被丢弃。

Connected
Enter a message: foo
Enter a message: bar
Enter a message: Server: foo
Server: bar

我试过在各个地方添加换行符并在 receive 函数后添加另一个提示,但无论我做什么,总会有一些边缘情况把事情搞砸。有什么方法可以给 fgets 一个默认提示或什么的吗?我在本应如此简单的事情上花费了太多时间。

Is there some way to just give fgets a default prompt or something?

据我所知,除非您想开始使用像 ncurses 这样的库来进行更高级的终端管理(对于玩具程序,您可能不需要)。

你可以做的是:

  1. printf("Enter a message: "); 调用后立即调用 fflush(stdout);,以强制立即显示提示文本(而不是仅在打印下一个换行符后)。

  2. 在 server-printf() 的前面放一个 carriage-return 字符,例如printf("\rServer: %s\n", buffer); 这会将终端的 text-cursor 移回行首,这样 server-printf() 文本将覆盖提示而不是附加到提示。 (请注意,如果您的 server-text 不够长,您可能需要在末尾添加一些额外的空格以确保所有提示的文本都被覆盖)