2

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)

MeepMu
  • 475
  • 5
  • 6
  • 3
    Take a look at http://stackoverflow.com/q/6736536/1938163. You should probably manage your own buffers from different threads (and handle the synchronization yourself). – Marco A. Dec 27 '15 at 11:09
  • A separate thread is likely an overkill. You need event (de)multiplexing. – Karoly Horvath Dec 27 '15 at 12:32

2 Answers2

4

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

stek29
  • 395
  • 4
  • 14
  • The only "multiplatform" api I know of is `select`. And the windows implementation is... lacking to say the least. I'd say using `std::thread` with some basic sync and callbacks should do the job. – Zdeněk Jelínek Dec 27 '15 at 11:57
  • You could also check sockets with `select` too, so it's really best solution IMHO – stek29 Dec 27 '15 at 13:16
  • This is a windows (WinSock) question, and you're not sharing windows-compatible code. Even if I swapped out `STDIN_FILENO` for its actual value, I still get an unresolved external symbol error when actually trying to use `select` as-written, and there is no `unistd.h` for Windows. – kayleeFrye_onDeck Oct 03 '18 at 20:59
0

The above answer is quite helpful.

Here is an example (base code taken from SELECT man page):

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <iostream>

int main(void)
{
  fd_set rfds, save_rfds;
  struct timeval tv;
  int retval;

  /* Watch stdin (fd 0) to see when it has input. */
  FD_ZERO(&rfds);
  FD_SET(0, &rfds);

  /* Make a copy of rfds, as after running select, it gets reset */
  save_rfds = rfds;

  /* Wait for zero seconds. */
  tv.tv_sec = 0;
  tv.tv_usec = 0;

  while(true){

    retval = select(1, &rfds, NULL, NULL, &tv);
    rfds = save_rfds;

    if (retval == -1)
      perror("select()");
    else if (retval){
      /* Runs as soon as you enter a value and press enter. */

      std::cout<<"Data is available now.\n";

      std::string s;
      getline(std::cin, s);
      std::cout<<"Data Input: "<<s<<"\n";

      /* FD_ISSET(0, &rfds) will be true. */
    }
  }

  exit(EXIT_SUCCESS);
}