我正在尝试用 C++ 制作一个简单的聊天应用程序。它有效,但是,如果有人在别人打字的时候输入了一些东西。它就像写他们正在输入的内容。我将图片链接为下面的示例。
我用于客户端和服务器的代码可以在这里找到:
图片:
之前 之后
一种方法是在用户第一次开始输入一行文本时设置一个标志——然后,如果您在设置该标志时从网络接收数据,只需将接收到的数据排队到某个本地数据结构中(即不要打印它)直到本地用户按下返回。当本地用户按下回车键时,您将打印出在他键入时排队的所有数据,并取消设置标志。
当然,这种方法有几个缺点:
另一种方法是将本地用户的文本和远程用户的文本保存在物理上分开的区域(例如,窗口的上半部分和下半部分,就像许多聊天程序一样)。要做到这一点,需要比 vanilla C/C++ stdin/stdout/cin/cout API 提供更多的控制;您需要使用两个单独的文本小部件创建一个 GUI 窗口(使用 Win32 或 Qt 或其他一些 GUI API),或者如果您想将所有内容保存在 MS-DOS 窗口中,您可以使用类似PDCurses的东西来实现那。
但是,这些选项中的任何一个都不是微不足道的——与您的聊天应用程序的其余部分相比,它们可能需要更多的时间和精力来实现。如果是我,而聊天应用程序只是一个学习练习,我会倾向于简单地将当前行为记录为“已知限制”,而不用担心修复它。
杰里米的帖子非常彻底。我将添加另一个可供您使用的选项:跟踪正在键入的用户输入的字符,直到他最终按下 ENTER。这样,当远程用户输入文本时,您所要做的就是:
向终端写入适当数量的退格字符('\b')(即与本地用户输入的文本长度一样多),然后输出新传入的文本行,然后将所有字符输出到本地用户之前输入过。然后照常继续。
它看起来好像新传入的文本“滑动”到位。
#include <Conio.h>
#include <mutex> // C++11, if not C++11 find your own mutex
std::vector<char> inputBuffer;
std::mutex inputGuard;
struct Locker {
std::mutex* m;
Locker(std::mutex& m_):m(&m_) { m->lock(); }
void release() { if (m) m->unlock(); m = 0; }
~Locker() { release(); }
}
void AddToInputBuffer( char c ) {
Locker lock(inputGuard);
inputBuffer.push_back(c);
printf("%c",c);
}
std::string FinishInputBuffer() {
Locker lock(inputGuard);
std::string retval( inputBuffer.begin(), inputBuffer.end() );
inputBuffer.clear();
printf("\n");
return retval;
}
void OverlappedPrintln( std::string s ) {
Locker lock(inputGuard);
for (int i = 0; i < inputBuffer.size(); ++i) {
printf("%c", 8 /*backspace*/);
}
printf("%s\n", s.c_str());
for (int i = 0; i < inputBuffer.size(); ++i) {
printf("%c", inputBuffer[i]);
}
}
std::string readCharactersFromUser() {
// TODO: Handle exiting
while( int input = getch() ) {
if (input == '\n') {
return FinishInputBuffer();
AddToInputBuffer(input);
}
}
Now, replace cin.getline(message, 256);
with std::string message = readCharactersFromuser()
, then check the length of the string
before doing (message[0] == 's' && message[1] == 'a' && message[2] == 'y' && message[3] == ' '
that.
Elsewhere in the program, where you do printf
, instead build a std::string
and call OverlappedPrintln
with it.
The effect of this is that when you want to print something out, you lock a mutex, backspace over the user input, print out the message, do a newline, then print out the text you backspaced over, and release your mutex. The mutex isn't needed in 99% of situations, especially with modern computer speed, but it is good practice.
The above code was not tested or compiled. Nor is it of the highest quality.
The choice of non-echoing get character was because I cannot read from user input in a lock, and asynchronously echoing to the output might cause garbled text. So I read without echo, then echo within the mutex, such that inputBuffer
always contains the characters that the user has both typed and I've echoed to the screen.
This is an implementation of @NikBougalis 's answer.