Make c++ not wait for user input

2019-05-20 00:13发布

问题:

So, i am trying to make a c++ WinSock2 chatter, just for learning. It is a console application and i want to take user input (for sending to the counterpart), but i still want to be able to recive. (so you can write a message while still being able to recive one)...

When using cin >> input; the program "pauses" until the user has enterd something, that way it is "turn based" (one user writes something and sends it, then the other user writes something and sends it).

Is there a way to make the user be able to write something WHILE the recive stuff is still running? (Preferably something else than multi threading)

回答1:

What about checking if buffer isn't empty? But code wouldn't be really portable then, because you need to make some system calls as I know. See this.
But maybe you can do it with some C code, I'll do some research and update my answer.

UPD: Ok, I did it. What you need is select function.
It could wait till stdin is ready for read, and that's what we need. But looks like it don't work with C++ streams, so you need to use only C code for reading.

Let's jump to definition:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout)

Ok, we need to check only read buffer, so writefds and errorfds could be NULL. And we need to check only stdin, so nfds is 1 (number of fdses)

What about timeout? It's should be 0, so we need to initialize variable struct timeval timeout and set seconds and nanoseconds to 0 by timeout.tv_sec = 0 and timeout.tv_usec = 0.

So, only readfds left. It's pretty simple too: we need to initialize variable, "zero" it, and add stdin.
It can be done with fd_set readfds, FD_ZERO(&readfds) and FD_SET(STDIN_FILENO, &readfds).

Okay, final step: function call. It should be select(1, &readfds, NULL, NULL, &timeout).

This would return 0 if input buffer is empty and 1 if it's not.

UPD2: Looks like it's not C++ streams, something strange happens and it breaks when buffer is empty at very first call. I'll try to catch the problem.

UPD3: Ok, Now I figured it out. Looks like you can use select with C++ streams.

Select has VERY strange (IMHO) feature: it resets readfds. I'm not sure how to prevent It from doing this, so I just used one more fd_set variable to hold it, so you need to add fd_set savefds = readfds after readfds initialization, and readfds = savefds after each call. That's awful solution, but I don't know how could I improve it.

So code is:

Initialization:

fd_set readfds;
FD_ZERO(&readfds);
FD_SET(STDIN_FILENO, &readfds);
fd_set savefds = readfds;  

Timeout initialization:

struct timeval timeout;
timeout.tv_sec = 0;
timeout.tv_usec = 0;

And usage:

if (select(1, &readfds, NULL, NULL, &timeout)) {
  cin >> input;
  send(input);
}
readfds = savefds;

UPD4: Don't forget to include unistd.h and cstdlib