NCurses聊天行为不正常,在select中被阻止
我为一个社交网络和一个简单的聊天室编写了一个C应用程序。我使用了网络课程、插座和基本的网络工具 问题是,我的函数使用select()读取服务器套接字和stdin,因此当我开始编写消息时,输出窗口将冻结,并且仅在我按enter键后显示来自其他客户端的消息 我尽了一切可能。。有办法解决这个问题吗 我还尝试强制nocbreak()。它可以正常工作,但如果我这样做,当我编写消息时,回音将被禁用,并且在我键入时,输入窗口中不会显示任何内容,即使消息在那里,但类似于“不可见” 代码如下:NCurses聊天行为不正常,在select中被阻止,c,sockets,select,networking,ncurses,C,Sockets,Select,Networking,Ncurses,我为一个社交网络和一个简单的聊天室编写了一个C应用程序。我使用了网络课程、插座和基本的网络工具 问题是,我的函数使用select()读取服务器套接字和stdin,因此当我开始编写消息时,输出窗口将冻结,并且仅在我按enter键后显示来自其他客户端的消息 我尽了一切可能。。有办法解决这个问题吗 我还尝试强制nocbreak()。它可以正常工作,但如果我这样做,当我编写消息时,回音将被禁用,并且在我键入时,输入窗口中不会显示任何内容,即使消息在那里,但类似于“不可见” 代码如下: ssize_t s
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();
}
ssize\t安全预读(int-sock,void*buffer)
{
大小\u t长度=strlen(缓冲区);
ssize_t nbytesR=read(sock,&length,sizeof(size_t));
如果(nbytesR==-1)
{
perror(“读取()长度错误!正在退出!\n”);
退出(退出失败);
}
nbytesR=读取(sock、缓冲区、长度);
如果(nbytesR==-1)
{
perror(“数据读取()错误!正在退出!\n”);
退出(退出失败);
}
返回nbytesR;
}
ssize\u t safeprewrite(int sock,const void*缓冲区)
{
大小\u t长度=strlen(缓冲区);
ssize_t nbytesW=写入(sock,&length,sizeof(size_t));
如果(nbytesW==-1)
{
perror(“写入()长度错误!正在退出!\n”);
退出(退出失败);
}
nbytesW=写入(sock、缓冲区、长度);
如果(nbytesW==-1)
{
perror(“数据写入()错误!正在退出!\n”);
退出(退出失败);
}
返回nbytesW;
}
void activeChat(int sC,const char*currentUser,const char*room)
{
char inMesg[513],outMesg[513];
字符用户[33];
int winrows,wincols;
窗口*winput,*woutput;
initscr();
nocbreak();
getmaxyx(stdscr、winrows、wincols);
winput=newwin(1,wincols,winrows-1,0);
woutput=newwin(winrows-1,wincols,0,0);
键盘(winput,真);
scrollok(woutput,true);
鲜活(woutput);
wrefresh(winput);
fd_设置全部;
fd_设置读取_fds;
FD_零和全部;
FD_零(&read_fds);
FD_集(0和全部);
FD_集(sC和全部);
wprintw(woutput,“欢迎来到房间'%s'\n使用/quitChat退出!\n!”,房间);
鲜活(woutput);
while(true)
{
memcpy(&read_fds,&all,read_fds的大小);
如果(选择(sC+1,&read_fds,NULL,NULL,NULL)=-1)
{
perror(“选择()错误或强制退出!\n”);
打破
}
if(FD_ISSET(sC和读取fds))
{
memset(inMesg,0513);
安全预读(sC,用户);
安全预读(sC、inMesg);
wprintw(输出,“%s:%s\n”,用户,inMesg);
鲜活(woutput);
wrefresh(winput);
}
if(FD_-ISSET(0,读取&u-fds))
{
//wgetnstr(winput,“%s”,outMesg);
int a,i=0;
而(i
-safePrefWrite和safePrefRead是用于预处理读/写和错误处理的包装器
-sC是服务器套接字
乐:我试过用叉子和线。使用fork的行为与此相同,线程是一场灾难,终端被弄糟了
谢谢。修改while(true)
循环,一次只处理stdin的一个字符
对于stdin,这主要意味着读取单个字符:
如果char为'\n',则按当前方式处理
否则,只需将char追加到缓冲区进行写入
通常,在向缓冲区添加字符进行写入之前,请检查缓冲区是否已满
添加代码以处理要写入的缓冲区已满的情况
按以下顺序结束函数:
delwin(winput);
delwin(woutput);
endwin();
endwin();
结束两个窗口
在处理套接字输入期间不要调用endwin()
当select()
返回错误条件时,不要调用endwin()
在C语言中,fd_集合
不是一个固有的大小,因此使用memcpy()
进行设置
从all
读取。建议:
memcpy( &read_fds, &all, sizeof read_fds );
while ( i < MAX_BUF_LEN && (a = wgetch(winput)) != '\n')
参数:currentUser
未使用,建议插入行:
(void)currentUser;
消除编译器警告消息
为了便于阅读和理解,建议#使用有意义的名称定义魔法数字513和33,然后在整个代码中使用这些有意义的名称
#define MAX_BUF_LEN (513)
#define MAX_USER_LEN (33)
此行:outMesg[i]=a代码>引发编译器警告,建议:
outMesg[i] = (char)a;
while ( i < MAX_BUF_LEN && (a = wgetch(winput)) != '\n')
此行:while((a=wgetch(winput))!='\n')
会允许缓冲区溢出[],导致未定义的行为,并可能导致seg故障事件。建议:
while ( i < MAX_BUF_LEN && (a = wgetch(winput)) != '\n')
正如@user3629249所指出的,有几个批评可以应用于示例代码。然而,这些改进并没有解决OP的问题
OP似乎忽略了这些功能:
- 或者,使
wgetch
读取未缓冲的数据,即不等待'\n'
- 或者,控制等待输入的时间量
顺便说一句,让select
与curses程序一起工作会对curses库的内部行为做出假设:让它可靠地工作可能会很麻烦。最后只使用大循环就解决了这个问题
如果将来有人遇到同样的问题,下面是代码: