My project is a Client-Server app (C++), more specifically a Group Chat.
I have built the Client and Server in 2 different solution in VS17.
Now, my problem is that when I want to send the message from one client to server-and the message to be redirected to the other clients connected- I do not want to be a blocking function, so I used _kbhit() function, but I doesn't work properly. Is there any alternative for input in the Client rather than kbhit() + getch() or cin ?
Client
char buffer[20];
int point = 0;
while (1)
{
if (!_kbhit()) {
char cur = _getch();
if (point > 19)
point = 19;
std::cout << cur;
if (cur != 13)
buffer[point++] = cur;
else {
buffer[point] = '\0';
point = 0;
}
}
BytesReceived = recv(sock, buf, 4096, 0);
if (BytesReceived != 0 && BytesReceived != -1)
{
cout << BytesReceived << endl;
cout << string(buf, 0, BytesReceived) << endl;
ZeroMemory(buf, 4096);
}
//cin >> userInput;
// non blocking input
//if(userInput.size()>0)
//int sendResult = send(sock, userInput.c_str(), userInput.size(), 0);
int sendResult = send(sock, buffer, point+1, 0);
In ncurses you could try nodelay() function to turn getch() into a non-blocking call, which returns ERR if no key-press is available.
I think that should be:
if (_kbhit () != 0) {
....
See: https://msdn.microsoft.com/en-us/library/58w7c94c.aspx
Related
My goal is to create a user-server connection. Most importantly I'm not willing to use threads.
For now, I want it to work as a simple chat. I believe The issue is that It goes from one user to another in a loop waiting for their getline input, so technically only one user can send a message at a time. What I wish is that my code could handle multiple messages from one user continuously, not having to wait for all other users to send a message
That's the running loop inside the server:
while (running)
{
fd_set copy = master;
// See who's talking to us
int socketCount = select(0, ©, nullptr, nullptr, nullptr);
// Loop through all the current connections / potential connect
for (int i = 0; i < socketCount; i++)
{
// Makes things easy for us doing this assignment
SOCKET sock = copy.fd_array[i];
// Is it an inbound communication?
if (sock == listening)
{
// Accept a new connection
sockaddr_in client;
int clientSize = sizeof(client);
SOCKET clientsocket = accept(listening, (sockaddr*)&client, &clientSize);
// Add the new connection to the list of connected clients
FD_SET(clientsocket, &master);
// Send a welcome message to the connected client
string welcomeMsg = "Welcome to the Awesome Chat Server!\r\n";
send(clientsocket, welcomeMsg.c_str(), welcomeMsg.size() + 1, 0);
}
else // It's an inbound message
{
char buf[4096];
ZeroMemory(buf, 4096);
// Receive message
int bytesIn = recv(sock, buf, 4096, 0);
if (bytesIn <= 0)
{
// Drop the client
closesocket(sock);
FD_CLR(sock, &master);
}
else
{
// Send message to other clients, and definiately NOT the listening socket
for (int i = 0; i < master.fd_count; i++)
{
SOCKET outSock = master.fd_array[i];
if (outSock != listening && outSock != sock)
{
ostringstream ss;
ss << "SOCKET #" << sock << ": " << buf << "\r\n";
string strOut = ss.str();
send(outSock, strOut.c_str(), strOut.size() + 1, 0);
}
}
}
}
}
}
I belive this code should work, the issue is in my client code:
char buf[4096];
string userInput;
do
{
// Prompt the user for some text
cout << "> ";
getline(cin, userInput); //THATS THE ISSUE, IT WAITS FOR GETLINE
if (userInput.size() > 0) // Make sure the user has typed in something
{
// Send the text
int sendResult = send(sock, userInput.c_str(), userInput.size() + 1, 0);
if (sendResult != SOCKET_ERROR)
{
// Wait for response
ZeroMemory(buf, 4096);
int bytesReceived = recv(sock, buf, 4096, 0);
if (bytesReceived > 0)
{
// Echo response to console
cout << "SERVER> " << string(buf, 0, bytesReceived) << endl;
}
}
}
} while (userInput.size() > 0);
My question is if its really caused by getline and if so, how can it be fixed?
The answer to your question is "yes," but with a big proviso: a properly designed server shouldn't care what the client is doing. You might want to look into select() or, if you anticipate a large user community, poll(). You don't want a multi-user server to depend on/wait for a single client.
I'm currently writing a server and client app that attempts to transfer a screenshot but it's not working properly. I implemented it like this:
SOCKET sock;
char buf[4096];
DWORD WINAPI thread_function()
{
bool file_transfer = false;
bool loop = true;
while (1)
{
ZeroMemory(buf, 4096);
int bytesReceived = recv(sock, buf, 4096, 0);
if (bytesReceived > 0)
{
std::string received(buf, 0, bytesReceived);
if (received == "Sending file.")
{
file_transfer = true;
}
if (file_transfer == false)
{
std::cout << "\nSERVER> " << std::string(buf, 0, bytesReceived) << std::endl;
std::cout << "> ";
}
else if (file_transfer == true)
{
loop = true;
TCHAR *szfname = "screenshot.bmp";
FILE* f = fopen(szfname, "wb");
if (NULL == f)
{
std::cerr << "Error opening file" << std::endl;
return 1;
}
while ((bytesReceived = recv(sock, buf, 4096, 0)) > 0 && loop == true)
{
received = buf;
if (received == "File transfer completed !")
{
loop = false;
std::cout << "File transfer completed !" << std::endl;
std::cout << "> ";
}
else
{
fwrite(buf, 1, bytesReceived, f);
}
}
file_transfer = false;
}
}
}
}
I call the function with this
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)thread_function, 0, 0, 0);
The thing is I believe this is not a very clean way of doing it and also it's not working perfectly. After a file is received I don't correctly receive what the server is sending.
This is the server code which I think is fine.
send(clientSocket, TEXT("Attempting to take a screenshot."), sizeof(TEXT("Attempting to take a screenshot...")), 0);
HWND win = GetDesktopWindow();
HDC dc = GetDC(win);
if (HDCToFile("screenshot.bmp", dc, { 0, 0, 1920, 1080 }) == true)
{
send(clientSocket, TEXT("Sending file."), sizeof(TEXT("Sending file.")), 0);
FILE *fp = fopen("screenshot.bmp", "rb");
if (fp == NULL)
{
std::cerr << "Error : Cannot open file." << std::endl;
return 1;
}
while (1)
{
char buff[4096] = { 0 };
int nread = fread(buff, 1, 4096, fp);
if (nread > 0)
{
send(clientSocket, buff, sizeof(buff), 0);
}
if (nread < 4096)
{
if (feof(fp))
{
std::cout << "File transfer completed !" << std::endl;
send(clientSocket, TEXT("File transfer completed !"), sizeof(TEXT("File transfer completed !")), 0);
}
if (ferror(fp))
std::cerr << "Error reading." << std::endl;
break;
}
}
}
else
{
send(clientSocket, TEXT("Screen capture failed...."), sizeof(TEXT("Screen capture failed....")), 0);
}
Thanks for your time and help.
TCP is a streaming protocol. It has no concept of messages, so when the server sends "Sending file." there is no separation between the string and the beginning of the file being sent. Everything just goes into the stream one byte after the next and when the network stack decides it's time, usually because a packet has been filled or it's been too long since data was last added, a packet is sent, possibly containing multiple messages.
So
int bytesReceived = recv(sock, buf, 4096, 0);
very likely reads the full 4096 bytes, Attempting to take a screenshot.\0Sending file.\0 plus the first four thousand-or-so bytes of the bitmap. The client code consumes the string and discards the rest of the buffer.
You need to establish a communication protocol that sits between the the socket and the writing of the file. There are a whole bunch of different ways to handle this. Common tricks for reading strings are
Write the length of the string before writing the string so that the protocol handler knows how many bytes to read ahead of the time
Sender
uint16_t len = str.length(); // size is exactly 16 bits
len = htons(len); // endian is known
int sent = send(sock, (char*)&len, sizeof(len), 0);
// test sent for success (did not fail, sent all the bytes)
sent = send(sock, str.c_str(), len, 0);
// test sent for success (did not fail, sent all the bytes)
// may need to loop here if the string is super long.
Receiver
uint16_t len;
int recd = recv(sock, (char*)&len, sizeof(len), MSG_WAITALL);
// test recd for success (did not fail, read all the bytes)
// MSG_WAITALL will read exactly the right number of bytes or die trying.
len = ntohs(len); // ensure correct endian
std::string msg(len, ' '); // allocate a big enough string
char * msgp = &msg[0]; // or msg.data() if C++17 or better.
// Never seen &msg[0] fail, but this is not guaranteed by C++
while (len) // sometimes you want an extra exit condition here to bail out early
{
recd = recv(sock, msgp, len, 0);
// test recd for success
len -= recd;
msgp += recd;
}
Insert a canary value so that the protocol handler knows when to stop reading. The null terminator works well here. The protocol reads up until it finds the null and preserves the remainder of what's read for later consumption. No code example here because this can be done many, many different ways.
Not using strings and sending integer code messages instead. Eg:
enum messageID
{
TAKING_SCREENSHOT,
SENDING_FILE,
EATING_COOOOOOKIE_OM_NOM_NOM
};
OK! That moves the strings correctly. Assuming I don't have a bug in there. The idea's right, but the actual code is from memory and may contain brainfarts.
What you want to have is a bunch of functions, one for each type of data you send. Each of these functions can and should be be tested separately so that when you get to integrating them into the program, the program looks something like
sendString(sock, "Attempting to take a screenshot.");
if (getBitmap("screenshot.bmp"))
{
sendString(sock, "Sending file.");
sendBitmap(sock, "screenshot.bmp");
}
or
receiveString(sock);
std::string command = receiveString(sock);
if (command == "Sending file.")
{
receiveBitmap(sock, "screenshot.bmp");
}
else if (command == "Eating coooooookie! Om! Nom! Nom!")
{
OmNomNom(sock);
}
Which is about a close to foolproof as you can get.
Notes:
There is a bug in the server: int nread = fread(buff, 1, 4096, fp); gets the number of bytes read, but send(clientSocket, buff, sizeof(buff), 0); always tries to send a full buffer regardless of how many bytes were read, so garbage will be sent to the client. Also send can fail and this is not being checked. Always check the return codes. People don't put them there unless they're important.
I'm having an issue with winsock on windows 8.1 where recv keeps returning 0 randomly. I'm running both client and server on the same machine (thus all traffic is pointed at the loopback address) and i have breakpoints on any statement on both client and server which would shut down the sockets. But when the issue occurs the server is still operating normally, and hasn't shutdown anything, while the client has hit a breakpoint that only triggers on recv returning 0 or less.
The client keeps returning 0 from recv randomly (although always at the same point in my code) when the server VS 2013 project is set to compile as a windows program (rather than a console, in order to make it produce no window, it's supposed to be silent running). The bug doesn't seem to occur when i compile the server as a console application, as I've been debugging the program in that mode and only come across this issue when i switched compilation settings.
Is there any way to launch a console window when compiling as a windows application so i can see any debug statements?
Does winsock behave differently when compiling for the console vs compiling for a windows application?
Why would the client's recv return 0, when I've not sent a shutdown signal from the server?
My code, ask if there's any more you need:
Client
void recvLoop()
{
int recievedBytes = 1;
while (running && recievedBytes > 0)
{
WORD dataSize;
WORD dataType;
int recievedBytesA = ConnectSock.Recieve(&dataSize, sizeof(WORD));
if (recievedBytesA <= 0)
{
closing = true; //breakpoint set here
attemptKillThreads();
continue;
}
int recievedBytesB = ConnectSock.Recieve(&dataType, sizeof(WORD));
if (recievedBytesB <= 0)
{
closing = true; //breakpoint set here
attemptKillThreads();
continue;
}
unique_ptr<char[]> data(new char[dataSize]);
int recievedBytesC = ConnectSock.Recieve(data.get(), dataSize);
if (recievedBytesC <= 0)
{
closing = true; //breakpoint set here - Always triggers here
attemptKillThreads();
continue;
}
//use the received data.....
}
}
When this breaks recievedBytesA = 2, recievedBytesB = 2, recievedBytesC = 0, dataType = 0, dataSize = 0
ConnectSock is a global of type ConnectSocket. here is its Recieve()
int ConnectSocket::Recieve(void *recvbuf, int recvbuflen)
{
if (sock != INVALID_SOCKET)
{
int i = recv(sock, (char *)recvbuf, recvbuflen, 0);
if ((i == SOCKET_ERROR))
{
int err = 0;
err = WSAGetLastError();
if (err != WSAEINTR)
{
//ignore WSAEINTR as that's likely to be because of a shutdown complating a bit awkwardly
cout << "error: " << err << endl;
}
}
return i;
}
return -2;
}
Server:
void sendLoop()
{
int bytessent = 0;
QueuePack tosend;
while (running)
{
tosend = sendQueue.Dequeue();
if (tosend.packType == QueuePack::EXIT || tosend.packType == 0 || tosend.dSize == 0)
{
attemptKillThreads();
continue;
}
bytessent = Connection->SendData(&tosend.dSize, sizeof(WORD));
//cout used to see what exactly is being sent, even if it is garbage when converted to text
cout << tosend.dSize << endl;
cout << bytessent << endl;
if (bytessent <= 0)
{
attemptKillThreads();
continue;
}
bytessent = Connection->SendData(&tosend.packType, sizeof(WORD));
cout << tosend.dSize << endl;
cout << bytessent << endl;
if (bytessent <= 0)
{
attemptKillThreads();
continue;
}
bytessent = Connection->SendData(tosend.bufferPtr(), tosend.dSize);
cout << tosend.bufferPtr() << endl;
cout << bytessent << endl;
if (bytessent <= 0)
{
attemptKillThreads();
}
}
if (Connection->shutdownSock(SD_SEND) == SOCKET_ERROR)
{
Connection->closeSock();
}
}
SendData is literally a wrapper for send that uses a reinterpret_cast
int SendData(void * writeBuffer, int bufferSize)
{
return send(SocketManager.clientSocket, reinterpret_cast<char *>(writeBuffer), bufferSize, 0);
}
sendQueue is a Bounded blocking queue that holds QueuePacks
QueuePacks are used to transfer data, it's size and what kind of data it is between threads. both Client and server use this as it allows me to make sure data gets to the right thread on the client
Queuepack has 2 public variables packType and dSize of type WORD.
QueuePack::QueuePack() : packType(UND), dSize(0)
{
int h = 0; //debug statement to break on - never gets hit after initial collection construction occurs
}
QueuePack::QueuePack(const WORD type, WORD size, char * data) : packType(type), dSize(size)
{
//debug test and statement to break on
if (size == 0 || type == 0)
{
int h = 0; //breakpoint - never gets hit
}
dSize = (dSize < 1 ? 1 : dSize);
_buffer = make_unique<char[]>(dSize);
memcpy(_buffer.get(), data, dSize);
}
QueuePack::QueuePack(QueuePack &other) : packType(other.packType), dSize(other.dSize)
{
//debug test and statement to break on
if (other.dSize == 0 || other.packType == 0)
{
int h = 0; //breakpoint - never gets hit
}
if (&other == this)
{
return;
}
_buffer = make_unique<char[]>(dSize);
other.buffer(_buffer.get());
}
QueuePack QueuePack::operator= (QueuePack &other)
{
// check for self-assignment
if (&other == this)
{
return *this;
}
// reuse storage when possible
if (dSize != other.dSize)
{
_buffer.reset(new char[other.dSize]);
dSize = other.dSize;
}
packType = other.packType;
other.buffer(_buffer.get());
return *this;
}
QueuePack::~QueuePack()
{
}
HRESULT QueuePack::buffer(void* container)
{
try
{
memcpy(container, _buffer.get(), dSize);
}
catch (...)
{
return E_FAIL;
}
return S_OK;
}
char * QueuePack::bufferPtr()
{
return _buffer.get();
}
When this breaks recievedBytesA = 2, recievedBytesB = 2, recievedBytesC = 0, dataType = 0, dataSize = 0
You are calling ConnectSock.Recieve() when dataSize is 0. There is nothing to read, so Receive() reports that 0 bytes were read.
You need to add a check for that condition:
unique_ptr<char[]> data(new char[dataSize]);
if (dataSize != 0) // <-- add this
{
int recievedBytesC = ConnectSock.Recieve(data.get(), dataSize);
if (recievedBytesC <= 0)
{
closing = true;
attemptKillThreads();
continue;
}
}
Your code is also assuming that Recieve() reads all bytes that are requested, it is not handling the possibility that recv() may return fewer bytes. So you need to make Recieve() call recv() in a loop to guarantee that everything requested is actually read:
int ConnectSocket::Recieve(void *recvbuf, int recvbuflen)
{
if (sock == INVALID_SOCKET)
return -2;
char *buf = static_cast<char *>(recvbuf);
int total = 0;
while (recvbuflen > 0)
{
int i = recv(sock, buf, recvbuflen, 0);
if (i == SOCKET_ERROR)
{
int err = WSAGetLastError();
if (err != WSAEINTR)
{
//ignore WSAEINTR as that's likely to be because of a shutdown complating a bit awkwardly
cout << "error: " << err << endl;
}
return -1;
}
if (i == 0)
{
cout << "disconnected" << endl;
return 0;
}
buf += i;
recvbuflen -= i;
total += i;
}
return total;
}
Same with SendData(), as send() may return fewer bytes than requested:
int SendData(void * writeBuffer, int bufferSize)
{
if (SocketManager.clientSocket == INVALID_SOCKET)
return -2;
char *buf = static_cast<char *>(writeBuffer);
int total = 0;
while (bufferSize > 0)
{
int i = send(SocketManager.clientSocket, buf, bufferSize, 0);
if (i == SOCKET_ERROR)
{
int err = WSAGetLastError();
if (err != WSAEINTR)
{
//ignore WSAEINTR as that's likely to be because of a shutdown complating a bit awkwardly
cout << "error: " << err << endl;
}
return -1;
}
buf += i;
bufferSize -= i;
total += i;
}
return total;
}
I am trying to implement client-server communication via sockets. The main tasks are:
Sending commands from clients to server
Sending data from server to clients
Sending data from clients to server
Commands should come via port1, data via port2.
I got it working without without multi-threading but I have some problems with understanding how do I need to handle sockets.
Current scenario:
1.Server starts (socket, bind, listen for both command and info sockets) and goes for infinite loop with this function:
void FTPServer::waitForConnection()
{
sockaddr_in client;
int clientsize = sizeof(client);
SOCKET clientSocket = accept(_infoSocket, (struct sockaddr*)&client, &clientsize);
if (clientSocket == INVALID_SOCKET)
{
cout << " Accept Info Error" << endl;
}
else
{
cout << " Accept Info OK" << endl;
char* buff = new char[CHUNK_SIZE];
string fullRequest;
int rc = recv(clientSocket, buff, CHUNK_SIZE, 0);
if (rc == SOCKET_ERROR)
{
cout << " Recieve Info Error" << endl;
}
else
{
buff[rc] = NULL;
fullRequest.append(buff);
cout << " Recieve Info OK" <<endl;
if (executeCommand(fullRequest, clientSocket))
{
logOperation(client, fullRequest.c_str());
}
}
delete buff;
}
}
2.Client starts (socket, connect), creates 2 sockets on same ports, waits for user input.
3.User types "LIST", clients checks that it's a valid command and sends it.
bool FTPClient::sendToServer(string data, const bool verbose)
{
int n = 0;
while (data.size() > CHUNK_SIZE)
{
string s = data.substr(CHUNK_SIZE).c_str();
n += send(_infoSocket, data.substr(CHUNK_SIZE).c_str(), CHUNK_SIZE, 0);
data = data.substr(CHUNK_SIZE+1);
}
n+=send(_infoSocket, data.c_str(), data.size(), 0);
cout<<n<<endl;
if(n<0)
{
cout<<"Error: sending"<<endl;
return 0;
}
if (verbose)
cout<<"Send "<<n<<" bytes"<<endl;
return true;
}
4.Servers receives it, accepts on _dataSocket and sends the list of available files.
5.Client receives the list:
string FTPClient::getDataFromServer(const bool verbose)
{
char data[CHUNK_SIZE];
int size = recv(_dataSocket, data, strlen(data), 0);
if (size > 0)
{
int n = 0;
string res;
while (size > CHUNK_SIZE)
{
int buff = recv(_dataSocket, data, CHUNK_SIZE, 0);
res.append(data);
size -= buff;
n += buff;
}
n+= recv(_dataSocket, data, CHUNK_SIZE, 0);
res.append(data);
if (verbose)
cout<<"Recevied "<<n<<" bytes"<<endl;
res.resize(n);
return res;
}
else
{
return "";
}
}
Till this, it works. But if try to execute same command again, I got nothing.
I think, problem is that for each connect we need an accept on server side.
In main loop server gets only one connect from client. Is closing client command socket and reconnecting it on every request only option here? Any other advices (except for "Google it") are highly appreciated.
The following program is written to receive clients' messages and read user inputs from keyboard:
FD_ZERO(&masterfds);
FD_SET(udp_con, &masterfds);
FD_SET(0, &masterfds);
maxfds = udp_con;
while(exit == false)
{
FD_ZERO(&readfds);
readfds = masterfds;
selectFunc = select(maxfds+1, &readfds, NULL, NULL, &tv);
if(selectFunc < 0) {
message("error in select");
exit = true;
} else {
// The server has received something from a client
for(i = 0; i <= maxfds; i++) {
if(FD_ISSET(i, &readfds)) {
if(FD_ISSET(0, &readfds)) {
fgets(userInput, sizeof(userInput), stdin);
int len = strlen(userInput) - 1;
if (userInput[len] == '\n') {
userInput[len] = '\0';
}
cout<<"The user said: "<<userInput<<endl;
} else if(i == udp_con) {
cout<<"Datagram received"<<endl;
// After reading the user input, it never reaches here
}
}
}
}
}
The problem is that when I press the 'enter' key on the keyboard and the '0' file descriptor activates, the program will never activate any other file descriptor and it locks on the '0' file descriptor. How can I fix this bug?
You need to FD_CLR(0, readfds) after you test it with FD_ISSET(0, &readfds), or else it will always take that branch.
But you can simplify the algorithm by rewriting it. It's a good idea to get into the habit of formatting your code to make it easily readable.
FD_ZERO(&masterfds);
FD_SET(udp_con, &masterfds);
FD_SET(0, &masterfds);
maxfds = udp_con;
while (true) {
readfds = masterfds;
selectFunc = select(maxfds + 1, &readfds, NULL, NULL, &tv);
if (selectFunc < 0) {
message("error in select");
break;
}
// Check for input on stdin (fd 0).
if (FD_ISSET(0, &readfds)) {
fgets(userInput, sizeof(userInput), stdin);
int len = strlen(userInput) - 1;
if (userInput[len] == '\n') {
userInput[len] = '\0';
}
cout << "The user said: '" << userInput << "'" << endl;
}
// Check for input on the udp_con fd.
if (FD_ISSET(udp_con, &readfds)) {
cout << "Datagram received" << endl;
}
}