You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言网络程序中如何规整收发消息与用户输入以优化显示

如何解决C语言网络程序中输入与接收消息显示混乱的问题?

我开发的C语言网络程序包含两个线程,分别处理send(发送)和recv(接收)逻辑。用户通过fgets()输入待发送消息,同时recv线程会展示收到的消息。当用户正在输入消息时收到新消息,会出现显示混乱的情况,例如:

>>I'm composing a me<new message>ssage

我认为问题根源在于输入与输出共用同一窗口,请问有没有办法优化显示,让新消息与用户当前输入分隔开?

原收发逻辑代码

void* talk(void* sockfd){
    char msg[MSG_LEN_LIMIT];

    while(1){
        //printf("me: ");
        fgets(msg, MSG_LEN_LIMIT, stdin);
        msg[strcspn(msg, "\n")] = 0;
        send(*((int*)sockfd), msg, MSG_LEN_LIMIT, 0);
        //printf("me: %s\n", msg);
    }
    return NULL;
}

void* listento(void *sockfd){
    int byteReceived;
    char msg[MSG_LEN_LIMIT];

    while(1){
        if((byteReceived = recv(*((int*)sockfd), msg, MSG_LEN_LIMIT, 0) )== -1){
            perror("recv");
            continue;
        }else if(byteReceived == 0){
            fprintf(stderr, "connection lost");
            exit(1);
        }
        //msg[byteReceived] = '\0';
        printf("otherside: %s\n", msg);
    }
}

解决方案实现

尝试了ncurses库后,实现了一个可用的文本界面,输入框固定在终端底部,收发消息可通过上下箭头滚动查看,代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <pthread.h>
#include <semaphore.h>
#include <ncurses.h>
#include <arpa/inet.h>
#include <stdbool.h>


#define CLIENTPORT "4000"
#define SERVERPORT "3490"
#define MSG_LEN_LIMIT 256
#define BACKLOG 10
#define PAD_LENGTH 100

pthread_t threads[2];

typedef struct s_threadArgs{
    int *sockfd;
    WINDOW *pad;
    int maxRow;
    int maxCol;
    int *pad_top;
    int *pad_left;
    int *writeLine;
    
} threadArgs;

void *get_in_addr(struct sockaddr *sa)
{
    if (sa->sa_family == AF_INET) {
        return &(((struct sockaddr_in*)sa)->sin_addr);
    }

    return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

int sendAll(int sockdf, char *buf, int bufLen){
    int total = 0;
    int byteLeft = bufLen;
    int byteSent;

    while(total < bufLen){
        if( (byteSent = send(sockdf, buf+total, byteLeft, 0) )  == -1 ){
            return -1;
        }
        total +=byteSent;
        byteLeft -=byteSent;
    }

    return byteSent==-1?-1:0;
}

int tryConenct(char *connectAddr){
    int sockfd;
    struct addrinfo hints, *servinfo, *p;
    struct sockaddr their_addr;
    int rv, numbytes;
    
    char msg[MSG_LEN_LIMIT];


    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;

    if( (rv = getaddrinfo(connectAddr, SERVERPORT, &hints, &servinfo)) != 0 ){
        fprintf(stderr, "getaddrinfo %s\n", gai_strerror(rv));
        return -1;
    }

    for(p = servinfo; p != NULL; p = p->ai_next){
        if( (sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1 ){
            perror("socket");
            continue;
        }

        if( connect(sockfd, p->ai_addr, p->ai_addrlen) == -1 ){
            close(sockfd);
            perror("connect");
            continue;
        }
        break;
    }

    if(p == NULL){

        fprintf(stderr, "talker: socket failed\n");
        return -2;
    }

    freeaddrinfo(servinfo);
    //close(sockfd);
    return sockfd;
}

int spinServer(){
    int sockfd, new_fd;
    struct addrinfo hints, *servinfo, *p;
    struct sockaddr_storage their_addr;
    int rv;
    int yes = 1;
    int numbytes;
    socklen_t addr_size;
    char buf[MSG_LEN_LIMIT];
    socklen_t addr_len;
    char s[INET6_ADDRSTRLEN];

    memset(&hints, 0, sizeof hints);
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;

    if((rv = getaddrinfo(NULL, SERVERPORT, &hints, &servinfo)) != 0){
        fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
        return -1;
    }

    for(p = servinfo; p != NULL; p = p->ai_next){
        if((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1){
            perror("socket");
            continue;
        }

        if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) {
            perror("setsockopt");
            exit(1);
        }

        if(bind(sockfd, p->ai_addr, p->ai_addrlen) == -1){
            perror("bind failed");
            close(sockfd);
            continue;
        }

        break;
    }

    if(p == NULL){
        fprintf(stderr, "listener: failed to bin socket\n");
        return -2;
    }

    if (listen(sockfd, BACKLOG) == -1) {
        perror("listen");
        return -3;
    }

    printf("listener: waiting to receive...\n");

    addr_len = sizeof their_addr;

    addr_size = sizeof their_addr;
    new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &addr_size);
    if(new_fd == -1){
        perror("accept");
        return -4;
    }

    inet_ntop(their_addr.ss_family, get_in_addr( (struct sockaddr*)&their_addr ), s, sizeof s);
    printf("me: accepted incoming connection from %s\n", s);

    freeaddrinfo(servinfo);
    close(sockfd);
    return new_fd;
}

void* talk(void* args){
    char msg[MSG_LEN_LIMIT];
    threadArgs *targs = (threadArgs*)args;
    int visible_height = targs->maxRow - 2;
    int visible_width = targs->maxCol - 2;
    int pad_height = PAD_LENGTH;
    int pad_width = targs->maxCol - 2;

    if(targs->pad == NULL){
        printf("pad not available in talk\n");
        exit(1);
    }
    int ch;
    while ((ch = getch()) != 'q') {
        switch (ch) {
            case KEY_UP:
                if (*(targs->pad_top) > 0) (*(targs->pad_top))--;
                break;
            case KEY_DOWN:
                if (*(targs->pad_top) + visible_height < pad_height) (*(targs->pad_top))++;
                break;
            // case KEY_LEFT:
            //     if (pad_left > 0) pad_left--;
            //     break;
            // case KEY_RIGHT:
            //     if (pad_left + visible_width < pad_width) pad_left++;
            //     break;
            case 'a':
                echo();
                move((targs->maxRow)-1, 0);
                clrtoeol();
                refresh();
                mvprintw((targs->maxRow) - 1, 0, ":");
                mvgetstr((targs->maxRow) - 1, 2,  msg);
                send(*( targs->sockfd ), msg, MSG_LEN_LIMIT, 0);
                //integrate the first letter to rest of msg
                mvwprintw(targs->pad, *(targs->writeLine), 0, "me: %s", msg);
                clrtoeol();
                refresh();
                noecho();
                (*(targs->writeLine))++;
                move((targs->maxRow)-1, 0);
                if (*(targs->writeLine) > visible_height) (*(targs->pad_top))++;
                break;
            default:
                break;
        }

        // Display the pad content within the visible window
        prefresh(targs->pad, (*(targs->pad_top)), (*(targs->pad_left)), 0, 0, visible_height, visible_width);
    }

    delwin(targs->pad);
    endwin();
    return NULL;
}

void* listento(void *args){
    int byteReceived;
    char msg[MSG_LEN_LIMIT];

    threadArgs *targs = (threadArgs*)args;
    int visible_height = targs->maxRow - 2;
    int visible_width = targs->maxCol - 2;
    int pad_height = PAD_LENGTH;
    int pad_width = targs->maxCol - 2;

    if(targs->pad == NULL){
        printf("pad not available in talk\n");
        exit(1);
    }

    while(1){
        pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
        if((byteReceived = recv(*( targs->sockfd ), msg, MSG_LEN_LIMIT, 0) )== -1){
            perror("recv");
            continue;
        }else if(byteReceived == 0){
            fprintf(stderr, "\nconnection lost");
            exit(1);
        }
        //msg[byteReceived] = '\0';
        //printf("otherside: %s\n", msg);
        mvwprintw(targs->pad, *(targs->writeLine), 0, "they: %s", msg);
        (*(targs->writeLine))++;
        if (*(targs->writeLine) > visible_height) (*(targs->pad_top))++;
        prefresh(targs->pad, (*(targs->pad_top)), (*(targs->pad_left)), 0, 0, visible_height, visible_width);

    }
}

int main(int argc, char *argv[]){
    int sockfd;
    bool isServer;
    char msg[MSG_LEN_LIMIT];
    int *fd = (int*)malloc(sizeof(int));

    //establish connection
    char ipAddr[] = "::1";
    if((sockfd = tryConenct(ipAddr)) < 0){
        //failed to connect, spin up the listenning procedure, this client will serve as server
        sockfd = spinServer();
        if(sockfd < 0){
            printf("server spin: failed\n");
            return 1;
        }else{
            printf("accepted: sockfd %d\n", sockfd);
            isServer = true;
        }
    }else{
        printf("connected, sockfd %d\n", sockfd);
        isServer = false;
    }
    
    //connection established, communication begins
    *fd = sockfd;

    //pad setup
    initscr();
    cbreak();
    noecho();
    keypad(stdscr, TRUE);
    int rows, cols, writeLine;
    getmaxyx(stdscr, rows, cols);
    int pad_height = 100;
    int pad_width = cols - 2;
    WINDOW *pad = newpad(pad_height, pad_width);
    if (pad == NULL) {
        endwin();
        fprintf(stderr, "Error creating pad\n");
        exit(1);
    }
    int pad_top = 0;
    int pad_left = 0;
    writeLine = 0;

    mvprintw(rows - 1, 0, "type 'a' for messaging , 'q' to quit: ");
    refresh();

    threadArgs *tArgs = (threadArgs*)malloc(sizeof(threadArgs));
    tArgs->maxCol = cols;
    tArgs->maxRow = rows;
    tArgs->pad = pad;
    tArgs->pad_left = &pad_left;
    tArgs->pad_top = &pad_top;
    tArgs->writeLine = &writeLine;
    tArgs->sockfd = fd;


    pthread_create(&threads[0], NULL, &talk, tArgs);
    pthread_create(&threads[1], NULL, &listento, tArgs);
    
    pthread_join(threads[0], NULL);
    pthread_cancel(threads[1]);
    pthread_join(threads[1], NULL);



    return 0;
}

内容的提问来源于stack exchange,提问作者oliver li

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 11:29:49