NCurses聊天行为不端,在选择中阻止

时间:2016-01-06 05:46:54

标签: c sockets select networking ncurses

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

问题是我的函数使用select()从服务器套接字和stdin读取,所以当我开始写一条消息时,输出窗口会冻结,并且只有在我按下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和thread。使用fork表现相同,线程是一场灾难,终端搞砸了。

谢谢。

3 个答案:

答案 0 :(得分:0)

修改while(true)循环,一次只为stdin处理一个char。

这主要意味着stdin,读取一个char:

如果char为'\ n',则按当前处理,

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

总是,在将一个char附加到缓冲区进行写入之前,检查缓冲区是否已满。

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

用这个序列结束函数:

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

结束两个窗口。

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

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

fd_set不是C中的固有大小,因此请使用memcpy()进行设置  来自read_fds的{​​{1}}。建议:

all

参数:memcpy( &read_fds, &all, sizeof read_fds ); 未使用,建议插入以下行:

currentUser

消除编译器警告消息。

为了便于阅读,易于理解,建议#define神奇的数字513和33使用有意义的名称,然后在整个代码中使用这些有意义的名称。

 (void)currentUser;

这一行:#define MAX_BUF_LEN (513) #define MAX_USER_LEN (33) 引发编译器警告,建议:

outMesg[i] = a;

这一行:outMesg[i] = (char)a; 可以允许缓冲区outMesg []溢出,导致未定义的行为并导致seg fault事件。建议:

while ( (a = wgetch(winput)) != '\n')

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

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

答案 1 :(得分:0)

如@ user3629249所述,有几个批评可以应用于示例代码。但是,这些改进并没有解决OP的问题。

OP似乎忽略了这些功能:

  • cbreakraw,使wgetch读取无缓冲数据,即不等待'\n'
  • nodelaytimeout,用于控制wgetch等待投入的时间。

顺便说一下,让select使用curses程序会对curses库的内部行为做出假设:让它可靠地工作会很麻烦。

答案 2 :(得分:0)

最后只使用大循环修复了它。

如果以后有人遇到同样的问题,请输入以下代码:

if (FD_ISSET(0, &read_fds))
    {


        inChar = wgetch(winput);

        if (inChar == 27)
        {
            safePrefWrite(sC, "/quit");
            break;
        }

        if (inChar == KEY_UP || inChar == KEY_DOWN || inChar == KEY_LEFT || inChar == KEY_RIGHT)
            continue;

        if (inChar == KEY_BACKSPACE || inChar == KEY_DC || inChar == 127)
        {
            wdelch(winput);
            wrefresh(winput);
            if (i != 0)
            {
                outMesg[i - 1] = 0;
                i--;
            }
        }
        else
        {
            outMesg[i] = (char)inChar;
            i++;
        }


        if (outMesg[i - 1] == '\n')
        {
            outMesg[i - 1] = 0;
            i = 0;

            if (outMesg[0] == 0)
                continue;

            if (strcmp(outMesg, "/quit") == 0)
            {
                safePrefWrite(sC, outMesg);
                break;
            }

            safePrefWrite(sC, outMesg);
            delwin(winput);
            winput = newwin(1, wincols, winrows - 1, 0);
            keypad(winput, true);
            wrefresh(winput);
            memset(outMesg, 0, 513);
        }
    }

我还使用raw()来禁用信号并按照我想要的方式处理代码。 除此之外的任何其他内容&#34;如果&#34;就像第一篇文章一样。