我为社交网络写了一个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表现相同,线程是一场灾难,终端搞砸了。
谢谢。
答案 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似乎忽略了这些功能:顺便说一下,让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;就像第一篇文章一样。