c - NCurses 聊天行为不当,阻止选择

标签 c sockets select networking ncurses

我为社交网络编写了一个 C 应用程序,还编写了一个简单的基于房间的聊天。我使用了 ncurses、套接字和基本的网络东西。

问题是我的函数使用 select() 从服务器套接字和标准输入读取,因此当我开始写入消息时,输出窗口会卡住,并且仅在我按 Enter 键后显示来自其他客户端的消息。

我尝试了一切可能的方法..有办法解决这个问题吗?

我还尝试强制 nocbreak()。它工作正常,但如果我这样做,当我编写消息时,回显将被禁用,并且当我键入时,输入窗口中不会显示任何内容,即使消息在那里,但就像“隐形”一样。

这是代码:

ssize_t safePrefRead(int sock, void *buffer)
{
    size_t length = strlen(buffer);

    ssize_t nbytesR = read(sock, &length, sizeof(size_t));
    if (nbytesR == -1)
    {
        perror("read() error for length ! Exiting !\n");
        exit(EXIT_FAILURE);
    }

    nbytesR = read(sock, buffer, length);
    if (nbytesR == -1)
    {
        perror("read() error for data ! Exiting !\n");
        exit(EXIT_FAILURE);
    }

    return nbytesR;
}

ssize_t safePrefWrite(int sock, const void *buffer)
{
    size_t length = strlen(buffer);

    ssize_t nbytesW = write(sock, &length, sizeof(size_t));
    if (nbytesW == -1)
    {
        perror("write() error for length ! Exiting !\n");
        exit(EXIT_FAILURE);
    }

    nbytesW = write(sock, buffer, length);
    if (nbytesW == -1)
    {
        perror("write() error for data ! Exiting !\n");
        exit(EXIT_FAILURE);
    }

    return nbytesW;
}

void activeChat(int sC, const char *currentUser, const char *room)
{
    char inMesg[513], outMesg[513];
    char user[33];


    int winrows, wincols;
    WINDOW *winput, *woutput;

    initscr();
    nocbreak();
    getmaxyx(stdscr, winrows, wincols);
    winput = newwin(1, wincols, winrows - 1, 0);
    woutput = newwin(winrows - 1, wincols, 0, 0);
    keypad(winput, true);
    scrollok(woutput, true);
    wrefresh(woutput);
    wrefresh(winput);



    fd_set all;
    fd_set read_fds;
    FD_ZERO(&all);
    FD_ZERO(&read_fds);
    FD_SET(0, &all);
    FD_SET(sC, &all);

    wprintw(woutput, "Welcome to room '%s' \n Use /quitChat to exit !\n!", room);
    wrefresh(woutput);

    while (true)
    {
        memcpy( &read_fds, &all, sizeof read_fds );
        if (select(sC + 1, &read_fds, NULL, NULL, NULL) == -1)
        {
            perror("select() error or forced exit !\n");
            break;
        }

        if (FD_ISSET(sC, &read_fds))
        {
            memset(inMesg, 0, 513);
            safePrefRead(sC, user);
            safePrefRead(sC, inMesg);
            wprintw(woutput, "%s : %s\n", user, inMesg);
            wrefresh(woutput);
            wrefresh(winput);
        }

        if (FD_ISSET(0, &read_fds))
        {

            //wgetnstr(winput, "%s", outMesg);

            int a, i = 0;

            while ( i < MAX_BUF_LEN && (a = wgetch(winput)) != '\n')
            {
                outMesg[i] = (char)a;
                i++;
            }
            outMesg[i] = 0;


            if (outMesg[0] == 0)
                continue;
            if (strcmp(outMesg, "/quitChat") == 0)
            {
                safePrefWrite(sC, outMesg);
                break;
            }
            safePrefWrite(sC, outMesg);
            delwin(winput);
            winput = newwin(1, wincols, winrows - 1, 0);
            keypad(winput, true);
            wrefresh(winput);
        }
    }

    delwin(winput);
    delwin(woutput);
    endwin();
}

-safePrefWrite 和 safePrefRead 是预读/写和错误处理的包装器 -sC 是服务器套接字。

LE:我尝试使用 fork 和线程。使用 fork 的行为是相同的,而线程是一场灾难,终端被搞乱了。

谢谢。

最佳答案

修改 while(true) 循环,以便一次仅处理标准输入的一个字符。

这主要意味着对于标准输入,读取单个字符:

如果 char 是 '\n' 则按当前方式处理,

否则,只需将 char 附加到缓冲区即可写入。

始终在将字符附加到要写入的缓冲区之前,检查缓冲区是否已满。

添加代码来处理要写入的缓冲区已满的情况

使用以下序列结束函数:

delwin(winput);
delwin(woutput);
endwin();
endwin();

结束两个窗口。

在处理套接字输入期间不要调用 endwin()。

select() 返回错误条件时,不要调用 endwin()

fd_set 不是 C 中的固有大小,因此请使用 memcpy() 来设置 来自全部read_fds。建议:

memcpy( &read_fds, &all, sizeof read_fds );

参数:currentUser未使用,建议插入行:

 (void)currentUser;

消除编译器警告消息。

为了提高可读性和易于理解性,建议使用有意义的名称#define 魔数(Magic Number) 513 和 33,然后在整个代码中使用这些有意义的名称。

#define MAX_BUF_LEN (513)
#define MAX_USER_LEN (33)  

这一行:outMesg[i] = a; 引发编译器警告,建议:

outMesg[i] = (char)a;

这一行:while ( (a = wgetch(winput)) != '\n') 可以允许缓冲区 outMesg[] 溢出,从而导致未定义的行为,并可能导致段错误事件。建议:

while ( i < MAX_BUF_LEN && (a = wgetch(winput)) != '\n')

建议发布 safePrefWrite() 和 safePrefRead() 函数的原型(prototype),类似于:

void safePrefRead( int, char * );
void safePrefWrite( int, char * );

关于c - NCurses 聊天行为不当,阻止选择,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34626439/

相关文章:

IN where 语句中的 mysql 用户变量

uzzfizz 程序中的代码重复(包括负数)

linux - 从未绑定(bind)的 UDP 套接字发送到

c - 未打印链表的最后一个节点值

C# 现有连接被远程主机强制关闭 : socket programming

c++ - 处理客户端连接的最有效方式(套接字编程)

javascript - 使用 jquery 选择器将选定的值传递给函数

mysql - SQL 语法错误 SelectWhere Like

将多维数组转换为另一种类型的多维数组

c++ - 如何在 Linux 中为系统托盘编写应用程序