我为社交网络编写了一个 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/