I'm trying to build a multithread HTTP server using C++ and winsock2.h and sys/socket.h.However, after sending response successfully to first request, accept() function captures other requests and sends through thread pool but those threads hangs up in recv() function so function can never capture data from socket.Here is my code.
void HTTPServer::init()
{
#ifdef _WIN32
WSADATA wsaData;
WSAStartup(0x202,&wsaData);
if((mSock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP)) == INVALID_SOCKET)
#else
if((mSock = socket(AF_INET,SOCK_STREAM,0)) < 0)
#endif
{
std::cout << "Error while initilazing socket\n";
}
mServerAddr.sin_family = AF_INET;
mServerAddr.sin_port = htons(mPort);
#ifdef _WIN32
mServerAddr.sin_addr.S_un.S_addr = INADDR_ANY;
#else
mServerAddr.sin_addr.S_addr = INADDR_ANY;
#endif
if(bind(mSock, (struct sockaddr*) &mServerAddr, sizeof(mServerAddr)) < 0)
{
std::cout << "Error while binding socket\n";
}
if(listen(mSock, SOMAXCONN))
{
std::cout << "Error while listening socket\n";
}
}
void HTTPServer::fRun()
{
ThreadPool* tPool = new ThreadPool(10);
while(1)
{
int size = sizeof(mClientAddr);
#ifdef _WIN32
if((mAcceptSocket = accept(mSock,(struct sockaddr*) &mClientAddr,&size)) == INVALID_SOCKET)
#else
if((mAcceptSocket = accept(mSock,(struct sockaddr*) &mClientAddr,&size))) < 0)
#endif
{
std::cout << "Error while initilazing accept socket\n";
}
std::cout << "Socket Sent " << mAcceptSocket << std::endl;
tPool->enqueue([&] { this->fOnRequest(mAcceptSocket); });
}
delete tPool;
}
void HTTPServer::fOnRequest(uint64_t socket)
{
const string reqData = fRecieveNext(mAcceptSocket);
const HTTPResponse res = fProcessRequest(reqData);
fSendResponse(res,socket);
std::cout << "Done";
}
const string HTTPServer::fRecieveNext(uint64_t socket)
{
int64_t recieveLength = 0,totalRecieved = 0;
int64_t recieveLengthBeforeBody = -1;
int contentLength = 0;
string rawData;
while(1)
{
memset(mBuffer,'\0',8192);
if((recieveLength = recv(socket,mBuffer,8192, 0)) == SOCKET_ERROR)
{
std::cout << "Error while receiving data from socket\n";
#ifdef _WIN32
std::cout << WSAGetLastError() << std::endl;
#endif
break;
}
if(recieveLength == 0)
break;
totalRecieved += recieveLength;
rawData += string(mBuffer,recieveLength);
if(totalRecieved > 32)
{
if(rawData.find("Content-Length") == string::npos && contentLength == 0)
break;
else
{
if(contentLength == 0)
{
auto pos = rawData.find("Content-Length:");
auto lengthStr = rawData.substr(pos + 16, rawData.find_first_of("\r",pos) - pos - 16);
contentLength = std::stoi(lengthStr);
}
if(rawData.find("\r\n\r\n") != string::npos && recieveLengthBeforeBody == -1)
{
recieveLengthBeforeBody = rawData.find("\r\n\r\n") + 4;
}
if(totalRecieved >= contentLength + recieveLengthBeforeBody)
break;
}
}
}
return rawData;
}
void HTTPServer::fSendResponse(const HTTPResponse& response,const uint64_t socket)
{
string resStr = response.fSerializeResponse();
size_t sent = 0,totalSent = 0;
char* buffer = &(resStr[0]);
while(totalSent < resStr.length())
{
if((sent = send(socket, buffer, resStr.length() - totalSent,0)) < 0)
{
std::cout << "Error while sending response" << std::endl;
}
buffer += sent;
totalSent += sent;
}
delete &response;
#ifdef _WIN32
if(shutdown(socket, SD_BOTH) == SOCKET_ERROR)
{
std::cout << "Error while closing socket" << std::endl;
std::cout << WSAGetLastError() << std::endl;
};
if(closesocket(socket) == SOCKET_ERROR)
{
std::cout << "Error while closing socket" << std::endl;
std::cout << WSAGetLastError() << std::endl;
};
#else
if(shutdown(socket, SHUT_RDWR) < 0)
{
std::cout << "Error while closing socket" << std::endl;
};
if(close(socket) < 0)
{
std::cout << "Error while closing socket" << std::endl;
};
#endif
}
It looks like you are expecting to receive a TCP message all in one recv. TCP doesnt work like that. Tcp is a stream protocol, a message sent to you can be broken into multiple chunks. TCP only guarantees that the bytes will arrive once and in the correct order.
That would make you get out of sync with the input
Related
I am implementing a communication system (tx, rx) using TCP, in windows 10. My problem is when tx sends a message to rx, the message is received with delay. When tx sends several messages, the rx starts receiving only after several messages are sent. My guess is that tx waits until its buffer gets full and then starts sending messages altogether (in my platform buffer length is 512).
As shown in the bellow picture, before receiving is started, this error appears:
ERROR receiving TCP, error #: 10014
I tried to solve this problem by enabling the TCP_NODELAY option so that the messages are being sent immediately (which was not successful). However, my knowledge of TCP is shallow and I am not sure if I am doing it correctly.
Here are my questions:
Does the delayed sending (or receiving) is related to the TCP_NODELAY option of the TCP?
If yes, am I enabling the TCP_NODELAY correctly as follows:
int yes = 1;
int resultt = setsockopt(serverSocket[0], IPPROTO_TCP, TCP_NODELAY, (char*)&yes, sizeof(int)); // 1 - on, 0 - off
if (resultt < 0)
std::cerr << "Error: TCP_NODELAY";
Am I putting the TCP_NODELAY option in the right place in both server and client codes?
Here are my codes for client:
size_t IPTunnel::ipTunnelSend_tcp(size_t process) {
size_t remaining = process;
size_t result{ 0 };
size_t sent{ 0 };
while (remaining > 0) {
result = send(clientSocket[0], &(ip_tunnel_buffer[0]) + sent, (int) remaining, 0);
if (result >= 0) {
remaining -= result;
sent += result;
}
else {
if (getLogValue()) {
if ( result == 0) std::cerr << "No data send through TCP" << std::endl;
else std::cerr << "ERROR sending TCP, error #: " << WSAGetLastError() << std::endl;
}
break;
}
}
return sent;
}
and:
bool IPTunnel::client_tcp() {
// STEP 1 - Initialize Winsock
WSADATA wsData;
WORD ver = MAKEWORD(2, 2);
int wsResult = WSAStartup(ver, &wsData);
if (wsResult != 0)
{
std::cerr << "Can't start Winsock. Error code #" << wsResult << std::endl;
return false;
}
// STEP 2 - Create a socket
SOCKET s = socket(AF_INET, SOCK_STREAM, 0);
int yes = 1;
int resultt = setsockopt(clientSocket[0], IPPROTO_TCP, TCP_NODELAY, (char*)&yes,
sizeof(int));
if (resultt < 0)
std::cerr << "Error: TCP_NODELAY";
clientSocket.push_back(s);
if (clientSocket[0] == INVALID_SOCKET)
{
std::cerr << "Can't create socket, Error Code #" << WSAGetLastError() << std::endl;
WSACleanup();
return false;
}
// STEP 3 - Connect to the server
sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons((u_short)localMachinePort);
inet_pton(AF_INET, remoteMachineIpAddress.c_str(), &hint.sin_addr);
int connResult{ -2 };
while (connResult != 0 || numberOfTrials == 0) {
connResult = connect(clientSocket[0], (sockaddr*)& hint, sizeof(hint));
if (connResult == SOCKET_ERROR)
{
std::cerr << "Can't connect to server, Err #" << WSAGetLastError() << std::endl;
std::cerr << "Waiting " << timeIntervalSeconds << " seconds." << std::endl;
Sleep(timeIntervalSeconds * 1000);
}
if (--numberOfTrials == 0) {
std::cerr << "Reached maximum number of attempts." << std::endl;
}
}
std::cerr << "Connected!\n";
return true;
}
Here are my codes for server:
size_t IPTunnel::ipTunnelRecv_tcp(size_t space) {
char* recv_buffer = &ip_tunnel_buffer[0];
size_t remaining{ 0 };
if (outputSignals[0]->getValueType() == (signal_value_type::t_message))
{
remaining = ip_tunnel_buffer.size();
}
else
{
remaining = space * outputSignals[0]->valueTypeSizeOf();
}
size_t received{ 0 };
while (remaining > 0) {
auto aux{ 0 };
aux = recv(serverSocket[1], recv_buffer + received, (int) remaining, 0);
if (aux > 0) {
received += aux;
remaining -= received;
}
else {
if (getLogValue()) {
if (aux == 0) std::cerr << "No data received through TCP" << std::endl;
else std::cerr << "ERROR receiving TCP, error #: " << WSAGetLastError() << std::endl;
}
break;
}
}
return received;
}
and:
bool IPTunnel::server_tcp() {
// STEP 1 - Initialize Winsock
WSADATA wsData;
WORD ver = MAKEWORD(2, 2);
int wsResult = WSAStartup(ver, &wsData);
if (wsResult != 0)
{
std::cerr << "Can't start Winsock. Error code #" << wsResult << std::endl;
return false;
}
// STEP 2 - Create a socket
SOCKET s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
serverSocket.push_back(s);
if (serverSocket[0] == INVALID_SOCKET)
{
std::cerr << "Can't create socket, Error Code #" << WSAGetLastError() << std::endl;
WSACleanup();
return false;
}
// STEP 3 - Bind the socket
sockaddr_in hint;
hint.sin_family = AF_INET; // AF_INET=2, IPv4
inet_pton(AF_INET, localMachineIpAddress.data(), &hint.sin_addr.S_un.S_addr);
hint.sin_port = ntohs((u_short)localMachinePort);
char ipAddress[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &hint.sin_addr.S_un.S_addr, ipAddress, INET_ADDRSTRLEN);
std::cerr << "The TCP server is running on IP address: " << ipAddress;
std::cerr << " Port: " << htons(hint.sin_port) << std::endl;
if (bind(serverSocket[0], (sockaddr*)& hint, sizeof(hint)) < 0) {
std::cerr << "Bind failed with error code #" << WSAGetLastError() << std::endl;
return false;
}
// STEP 4 - Listen on the socket for a client
if (listen(serverSocket[0], SOMAXCONN) == -1) {
std::cerr << "Listen error!" << std::endl;
return false;
}
// STEP 5 - Accept a connection from a client
sockaddr_in client;
int clientSize = sizeof(client);
s = accept(serverSocket[0], (sockaddr*) &client, &clientSize);
serverSocket.push_back(s);
// Provides information
char host[NI_MAXHOST];
char service[NI_MAXSERV];
ZeroMemory(host, NI_MAXHOST);
ZeroMemory(service, NI_MAXSERV);
if (getnameinfo((sockaddr*)& client, sizeof(client), host, NI_MAXHOST, service, NI_MAXSERV, 0) == 0)
{
std::cerr << host << " connected on port " << service << std::endl;
}
else
{
inet_ntop(AF_INET, &client.sin_addr, host, NI_MAXHOST);
std::cerr << host << " connected on port " << ntohs(client.sin_port) << std::endl;
}
int yes = 1;
int resultt = setsockopt(serverSocket[0], IPPROTO_TCP, TCP_NODELAY, (char*)&yes, sizeof(int)); // 1 - on, 0 - off
if (resultt < 0)
std::cerr << "Error: TCP_NODELAY_Server";
return true;
}
Here are the codes for sending the message (tx):
bool LoadFromCommandWindow::runBlock(void) {
int space = outputSignals[0]->space();
if (!space) return false;
if (flag && flag1)
{
std::getline(std::cin, plainData);
}
if (plainData.length() == 0)
{
flag = false;
return false;
}
else
{
data = plainData.substr(k, paddedDataLength);
if (data.length() != paddedDataLength) paddedData = padTo(data, paddedDataLength, '\0');
else paddedData = data;
outputSignals[0]->bufferPut((std::byte*) paddedData.c_str(), paddedDataLength); \\ This line puts the message into buffer
k += data.length();
if (k != plainData.length()) flag1 = false;
else
{
flag1 = true;
k = 0;
}
}
return flag;
}
std::string LoadFromCommandWindow::padTo(std::string str, const size_t num, const char paddingChar = '\0')
{
if (num > str.size())
str.insert(str.size(), num - str.size(), paddingChar);
return str;
}
Here are the codes for receiving the message (rx):
bool PrintData::runBlock(void) {
int ready = inputSignals[0]->ready();
int space = outputSignals[0]->space();
int process = std::min(ready, space);
if (process == 0) return false;
inputSignals[0]->bufferGet((std::byte*)decryptedData, decryptedDataLength);
decryptedDataLength = unpad(decryptedDataLength, decryptedData, '\0');
for (size_t i = 0; i < decryptedDataLength; i++)
{
std::cout << decryptedData[i];
}
std::cout << std::endl;
outputSignals[0]->bufferPut((std::byte*)decryptedData, decryptedDataLength);
decryptedDataLength = 496;
return true;
}
Thank you!
socket should be
int data_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
it should be enough , but it depends
bulletproof stuff is to use whole buffer transfers with buffer size on system maximum
https://stackoverflow.com/a/64726689/7172363
I am making a socket programming code
which send a socket and receive from the server. (www.dict.org)
I could connect the server using connect and send()
(The server requires one send to connect)
After the connection, I sent a char array as a command, but the server does not send anything.
I cited part 2.3 and 8 of RFC 2229 (https://www.rfc-editor.org/rfc/rfc2229)
and I sent a char array as "HELP" or "DEFINE ! ONE".
A few times the server sent me a message as your order is wrong.
But now I cannot get a message like that.
#include <stdlib.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
using namespace std;
#pragma comment (lib, "Ws2_32.lib")
void print_array(char x[]);
int count_array(char x[]);
int main()
{
int iResult;
WSADATA wsa;
SOCKET Client_sock;
struct sockaddr_in Sock_info;
char message[512]; // recv function's result will be saved.
//Command lines MUST NOT exceed 1024 characters in length (Part 2.3 RFC 2229)
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
{
cout << "WSAStartup failed with error" << endl;
system("pause");
return FALSE;
}
Client_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (Client_sock == INVALID_SOCKET)
{
cout << "Failed to create a socket" << endl;
closesocket(Client_sock);
return FALSE;
}
int nTime = 10000; //10 Sec stopwatch
if (setsockopt(Client_sock, SOL_SOCKET, SO_RCVTIMEO, (char*)&nTime, sizeof(nTime)) == SOCKET_ERROR)
{
shutdown(Client_sock, SD_RECEIVE | SD_SEND);
closesocket(Client_sock);
cout << "Waiting" << endl;
cout << "10 Secs over.";
system("pause");
return FALSE;
}
memset(&Sock_info, 0, sizeof(Sock_info));// Address structure clear
Sock_info.sin_family = AF_INET;
Sock_info.sin_port = htons(2628);// Get port
InetPton(AF_INET, "216.18.20.172", &Sock_info.sin_addr.s_addr); //IP of dict.org
if (connect(Client_sock, (SOCKADDR*)&Sock_info, sizeof(Sock_info)) == SOCKET_ERROR) // The fail of request to connect the server
{
closesocket(Client_sock);
return FALSE;
}
//Connect using text capabilities msg-id for code 220 in part 3.1 RFC 2229
if (send(Client_sock, "[ client initiates connection ]", 512, 0) < 0)
{
closesocket(Client_sock);
return FALSE;
}
if (recv(Client_sock, message, 512, 0) < 0)
{
closesocket(Client_sock);
return FALSE;
}
cout << "Send Help" << endl;
char request[] = "<HELP>";
iResult = send(Client_sock, request, sizeof(request), 0);
if (iResult < 0)
{
cout << "Failed to send a request" << endl;
system("pause");
closesocket(Client_sock);
return FALSE;
}
cout << "SENT" << endl;
while (iResult > 0)
{
iResult = recv(Client_sock, message, 512, 0);
cout << "RECEIVed" << endl;
if (iResult < 0)
{
cout << "Failed to receive a message" << endl;
system("pause");
closesocket(Client_sock);
return FALSE;
}
print_array(message);
}
print_array(message);
// Print the information
int count = 0;
//4. Close socket
closesocket(Client_sock);
//5. Quit winsock
WSACleanup();
return 0;
}
int count_array(char x[])
{
int cnt;
cnt = 0;
for (int i = 0; x[i] != '\n'; i++)
{
cnt++;
}
return cnt;
}
void print_array(char x[])
{
int i;
for (i = 0; i < count_array(x); i++)
{
cout << x[i];
}
cout << endl;
}
```
I'm trying to build a client and a server in the same program. For example, user 1 sends a packet of data to user 2, user 2 after receiving the packet sends back a different packet to user 1. The problem is, after running the program neither user receives the packets.If I build the client in a separate program from the server it works
#pragma comment(lib,"ws2_32.lib")
#include <WinSock2.h>
#include <iostream>
#include <thread>
static const int num_threads = 2;
static char buffer[8096 + 1];
char a[256] = {};
char MOTD[256];
void call_from_thread(int tid) {
// ---------- Server code: ---------- //
if( tid == 0 ){
WSAData wsaData;
WORD DllVersion = MAKEWORD(2, 1);
if (WSAStartup(DllVersion, &wsaData) != 0){MessageBoxA(NULL, "WinSock startup failed", "Error", MB_OK | MB_ICONERROR);}
SOCKADDR_IN addr;
int addrlen = sizeof(addr);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(1112);
addr.sin_family = AF_INET;
SOCKET sListen = socket(AF_INET, SOCK_STREAM, NULL);
bind(sListen, (SOCKADDR*)&addr, sizeof(addr));
listen(sListen, SOMAXCONN);
SOCKET newConnection;
newConnection = accept(sListen, (SOCKADDR*)&addr, &addrlen);
if (newConnection == 0){
std::cout << "Failed to accept the client's connection." << std::endl;
}else{
std::cout << "Client Connected!" << std::endl;
}
while (true){
std::cin >> a;
send(newConnection, a, sizeof(a), NULL);
}
}else if( tid == 1 ){
// ---------- Client code: ---------- //
WSAData wsaData;
WORD DllVersion = MAKEWORD(2, 1);
if (WSAStartup(DllVersion, &wsaData) != 0){MessageBoxA(NULL, "Winsock startup failed", "Error", MB_OK | MB_ICONERROR);}
SOCKADDR_IN addr;
int sizeofaddr = sizeof(addr);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(1111);
addr.sin_family = AF_INET;
SOCKET Connection = socket(AF_INET, SOCK_STREAM, NULL); //Set Connection socket
if (connect(Connection, (SOCKADDR*)&addr, sizeofaddr) != 0) //If we are unable to connect...
{
MessageBoxA(NULL, "Failed to Connect", "Error", MB_OK | MB_ICONERROR);
//return 0; //Failed to Connect
}
std::cout << "Connected!" << std::endl;
char MOTD[256];
while (true){
recv(Connection, MOTD, sizeof(MOTD), NULL); //Receive Message of the Day buffer into MOTD array
std::cout << "MOTD:" << MOTD << std::endl;
}
}
// ---------- Thread selection: ---------- //
int main() {
std::thread t[num_threads];
for (int i = 0; i < num_threads; ++i) {
t[i] = std::thread(call_from_thread, i);
}
for (int i = 0; i < num_threads; ++i) {
t[i].join();
}
}
The server is listening on port 1112, but the client is connecting to port 1111. This means the client and server cannot possibly be connected to each other at all, whether they are in the same application or not.
I also see a number of other problems with your code, including:
WSAStartup() should be called once at app startup, not per thread.
Neither thread quits cleanly if any WinSock function fails.
The client thread does not wait for the server thread to open its listening port before the client can then connect to it.
Potential buffer overflows.
Resource leaks.
A lack of decent error handling.
On the server side, you are completely ignoring errors reported by socket(), bind(), listen(), and send(), and you are not checking the return value of accept() correctly.
On the client side, you are completely ignoring errors reported by socket() and recv(). And, you are not ensuring the MOTD buffer is null-terminated before printing it to std::cout.
Try something more like this:
#include <winsock2.h>
#include <windows.h>
#pragma comment(lib,"ws2_32.lib")
#include <iostream>
#include <thread>
#include <string>
#include <cstdint>
#include <stdexcept>
#include <limits>
#include <algorithm>
// The below variables are used to let the client wait for the server
// port to be opened. If you don't want to use these, especially if the
// client and server are ever run on different machines, you can omit
// these and instead just have the client call connect() in a loop
// until successful...
//
#include <mutex>
#include <condition_variable>
#include <chrono>
static std::condition_variable server_cv;
static std::mutex server_mtx;
static bool server_is_listening = false;
//
static const int num_threads = 2;
static const u_short server_port = 1111;
class winsock_error : public std::runtime_error
{
public:
int errCode;
std::string funcName;
winsock_error(int errCode, const char *funcName) : std::runtime_error("WinSock error"), errCode(errCode), funcName(funcName) {}
};
void WinSockError(const char *funcName, int errCode = WSAGetLastError())
{
throw winsock_error(errCode, funcName);
}
class connection_closed : public std::runtime_error
{
public:
connection_closed() : std::runtime_error("Connection closed") {}
};
class socket_ptr
{
public:
socket_ptr(SOCKET s) : m_sckt(s) {}
socket_ptr(const socket_ptr &) = delete;
socket_ptr(socket_ptr &&src) : m_sckt(src.m_sckt) { src.m_sckt = INVALID_SOCKET; }
~socket_ptr() { if (m_sckt != INVALID_SOCKET) closesocket(m_sckt); }
socket_ptr& operator=(const socket_ptr &) = delete;
socket_ptr& operator=(socket_ptr &&rhs) { m_sckt = rhs.m_sckt; rhs.m_sckt = INVALID_SOCKET; return *this; }
operator SOCKET() { return m_sckt; }
bool operator!() const { return (m_sckt == INVALID_SOCKET); }
private:
SOCKET m_sckt;
}
template <typename T>
T LimitBufferSize(size_t size)
{
return (T) std::min(size, (size_t) std::numeric_limits<T>::max());
}
void sendRaw(SOCKET sckt, const void *buffer, size_t buflen)
{
const char *ptr = static_cast<const char*>(buffer);
while (buflen > 0)
{
int numToSend = LimitBufferSize<int>(buflen);
int numSent = ::send(sckt, ptr, numToSend, 0);
if (numSent == SOCKET_ERROR)
WinSockError("send");
ptr += numSent;
buflen -= numSent;
}
}
void recvRaw(SOCKET sckt, void *buffer, size_t buflen)
{
char *ptr = static_cast<char*>(buffer);
while (buflen > 0)
{
int numToRecv = LimitBufferSize<int>(buflen);
int numRecvd = ::recv(sckt, ptr, numToRecv, 0);
if (numRecvd == SOCKET_ERROR)
WinSockError("recv");
if (numRecvd == 0)
throw connection_closed();
ptr += numRecvd;
buflen -= numRecvd;
}
}
void sendStr(SOCKET sckt, const std::string &str)
{
uint32_t len = LimitBufferSize<uint32_t>(str.size());
uint32_t tmp = htonl(len);
sendRaw(sckt, &tmp, sizeof(tmp));
if (len > 0)
sendRaw(sckt, str.c_str(), len);
}
std::string recvStr(SOCKET sckt)
{
std::string str;
uint32_t len;
recvRaw(sckt, &len, sizeof(len));
len = ntohl(len);
if (len > 0)
{
str.resize(len);
recvRaw(sckt, &str[0], len);
}
return str;
}
void server_thread()
{
std::cout << "[Server] Starting ..." << std::endl;
try
{
socket_ptr sListen(::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
if (!sListen)
WinSockError("socket");
SOCKADDR_IN addr = {};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = ::inet_addr("127.0.0.1");
addr.sin_port = ::htons(server_port);
int addrlen = sizeof(addr);
if (::bind(sListen, (SOCKADDR*)&addr, sizeof(addr)) == SOCKET_ERROR)
WinSockError("bind");
if (::listen(sListen, 1) == SOCKET_ERROR)
WinSockError("listen");
// this is optional...
{
std::unique_lock<std::mutex> lk(server_mtx);
server_is_listening = true;
}
server_cv.notify_all();
//
std::cout << "[Server] Listening for Client ..." << std::endl;
socket_ptr newConnection(::accept(sListen, (SOCKADDR*)&addr, &addrlen));
if (!newConnection)
WinSockError("accept");
std::cout << "[Server] Client Connected!" << std::endl;
try
{
std::string a;
while (std::cin >> a)
sendStr(newConnection, a);
}
catch (const connection_closed &)
{
}
catch (const winsock_error &e)
{
std::cerr << "[Server] Client error: " << e.errCode << " on WinSock function: " << e.funcName << std::endl;
}
std::cout << "[Server] Client Disconnected!" << std::endl;
}
catch (const winsock_error &e)
{
std::cerr << "[Server] Error: " << e.errCode << " on WinSock function: " << e.funcName << std::endl;
}
catch (const std::exception &e)
{
std::cerr << "[Server] Unexpected Error! " << e.what() << std::endl;
}
std::cout << "[Server] Stopped!" << std::endl;
}
void client_thread()
{
std::cout << "[Client] Starting ..." << std::endl;
try
{
// this is optional, could call connect() below in a loop instead...
std::cout << "[Client] Waiting for Server ..." << std::endl;
{
std::unique_lock<std::mutex> lk(server_mtx);
if (!server_cv.wait_for(lk, std::chrono::seconds(5), []{ return server_is_listening; }))
throw std::runtime_error("Server not listening after 5 seconds!");
}
//
socket_ptr sConnection(::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP));
if (!sConnection)
WinSockError("socket");
SOCKADDR_IN addr = {};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = ::inet_addr("127.0.0.1");
addr.sin_port = ::htons(server_port);
if (::connect(sConnection, (SOCKADDR*)&addr, sizeof(addr)) == SOCKET_ERROR)
WinSockError("connect");
std::cout << "[Client] Connected!" << std::endl;
try
{
std::string MOTD;
while (true)
{
MOTD = recvStr(sConnection);
std::cout << "[Client] MOTD: " << MOTD << std::endl;
}
}
catch (const connection_closed &)
{
}
catch (const winsock_error &e)
{
std::cerr << "[Client] Server error: " << e.errCode << " on WinSock function: " << e.funcName << std::endl;
}
std::cout << "[Client] Disconnected!" << std::endl;
}
catch (const winsock_error &e)
{
std::cerr << "[Client] Error: " << e.errCode << " on WinSock function: " << e.funcName << std::endl;
}
catch (const std::exception &e)
{
std::cerr << "[Client] Unexpected Error! " << e.what() << std::endl;
}
std::cout << "[Client] Stopped!" << std::endl;
}
int main()
{
WSADATA wsaData;
int ret = ::WSAStartup(MAKEWORD(2, 1), &wsaData);
if (ret != 0)
{
std::cerr << "WinSock Startup failed with error: " << ret << std::endl;
return -1;
}
std::cout << "WinSock Startup successful" << std::endl;
std::thread t[num_threads];
t[0] = std::thread(server_thread);
t[1] = std::thread(client_thread);
for (int i = 0; i < num_threads; ++i) {
t[i].join();
}
::WSACleanup();
return 0;
}
I'm writing a server on Windows in C++ and I'm facing a strange behavior using recv().
I wrote this function:
bool readN(SOCKET s, int size, char* buffer){
fd_set readset;
struct timeval tv;
int left, res;
FD_ZERO(&readset);
FD_SET(s, &readset);
left = size;
std::cout << "-----called readN to read " << size << " byte" << std::endl;
while (left > 0) {
tv.tv_sec = MAXWAIT;
tv.tv_usec = 0;
res = select(0, &readset, NULL, NULL, &tv);
if (res > 0) {
res = recv(s, buffer, left, 0);
if (res == 0) {//connection closed by client
return false;
}
left -= res;
std::cout << "\treceived " << res << " left " << left << std::endl;
if (left != 0) {
buffer += res;
}
}
else if (res == 0) { //timer expired
return false;
}
else { //socket error
return false;
}
}
std::cout << "\t" << buffer << std::endl;
return true;
}
And I call it like this:
std::unique_ptr<char[]> buffer = std::make_unique<char[]>(size_);
if (readN(sck, size_, buffer.get())) {
std::cout << "----read message----" << std::endl;
std::cout <<"\t"<< buffer.get()<< std::endl;
}
The problem is that even if recv() returns a positive number, the buffer is still empty. What am I missing?
I see a few problems in your code.
you are not resetting the readset variable each time you call select(). select() modifies the variable. For a single-socket case, this is not too bad, but you should get in the habit of resetting the variable each time.
you are not checking for errors returned by recv(). You assume any non-graceful-disconnect is success, but that is not always true.
at the end of readN() before returning true, you are outputting the buffer parameter to std::cout, however buffer will be pointing at the END of the data, not the BEGINNING, since it was advanced by the reading loop. This is likely where your confusion about an "empty buffer" is coming from. readN() itself should not even be outputting the data at all, since you do that after readN() exits, otherwise you end up with redundant output messages.
if readN() returns true, you are passing the final buffer to std::cout using an operator<< that expects a null terminated char string, but your buffer is not guaranteed to be null-terminated.
Try something more like this instead:
bool readN(SOCKET s, int size, char* buffer){
fd_set readset;
struct timeval tv;
int res;
std::cout << "-----called readN to read " << size << " byte(s)" << std::endl;
while (size > 0) {
FD_ZERO(&readset);
FD_SET(s, &readset);
tv.tv_sec = MAXWAIT;
tv.tv_usec = 0;
res = select(0, &readset, NULL, NULL, &tv);
if (res > 0) {
res = recv(s, buffer, size, 0);
if (res == SOCKET_ERROR) {
res = WSAGetLastError();
if (res == WSAEWOULDBLOCK) {
continue; //call select() again
}
return false; //socket error
}
if (res == 0) {
return false; //connection closed by client
}
buffer += res;
size -= res;
std::cout << "\treceived " << res << " byte(s), " << size << " left" << std::endl;
}
/*
else if (res == 0) {
return false; //timer expired
}
else {
return false; //socket error
}
*/
else {
return false; //timer expired or socket error
}
}
return true;
}
std::unique_ptr<char[]> buffer = std::make_unique<char[]>(size_);
if (readN(sck, size_, buffer.get())) {
std::cout << "----read message----" << std::endl;
std::cout << "\t";
std::cout.write(buffer.get(), size_);
std::cout << std::endl;
}
With that said, I would suggest an alternative implementation of readN(), depending on whether you are using a blocking or non-blocking socket.
If blocking, use setsockopt(SO_RCVTIMEO) instead of select(). If recv() fails with a timeout, WSAGetLastError() will report WSAETIMEDOUT:
sck = socket(...);
DWORD timeout = MAXWAIT * 1000;
setsockopt(sck, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
bool readN(SOCKET s, int size, char* buffer){
int res;
std::cout << "-----called readN to read " << size << " byte(s)" << std::endl;
while (size > 0) {
res = recv(s, buffer, size, 0);
if (res == SOCKET_ERROR) {
/*
res = WSAGetLastError();
if (res == WSAETIMEDOUT) {
return false; //timer expired
}
else {
return false; //socket error
}
*/
return false; //timer expired or socket error
}
if (res == 0) {
return false; //connection closed by client
}
buffer += res;
size -= res;
std::cout << "\treceived " << res << " byte(s), " << size << " left" << std::endl;
}
return true;
}
If non-blocking, don't call select() unless recv() asks you to call it:
bool readN(SOCKET s, int size, char* buffer){
fd_set readset;
struct timeval tv;
int res;
std::cout << "-----called readN to read " << size << " byte(s)" << std::endl;
while (size > 0) {
res = recv(s, buffer, size, 0);
if (res == SOCKET_ERROR) {
res = WSAGetLastError();
if (res != WSAEWOULDBLOCK) {
return false; //socket error
}
FD_ZERO(&readset);
FD_SET(s, &readset);
tv.tv_sec = MAXWAIT;
tv.tv_usec = 0;
res = select(0, &readset, NULL, NULL, &tv);
if (res > 0) {
continue; //call recv() again
}
/*
else if (res == 0) {
return false; //timer expired
}
else {
return false; //socket error
}
*/
return false; //timer expired or socket error
}
if (res == 0) {
return false; //connection closed by client
}
buffer += res;
size -= res;
std::cout << "\treceived " << res << " byte(s), " << size << " left" << std::endl;
}
return true;
}
At the end of the readN() there is
std::cout << "\t" << buffer << std::endl;
The problem is that the buffer points now to buffer + size in respect to the original value of buffer. The value has been modified by
buffer += res;
This should output the buffer,
std::cout << "\t" << (buffer - size) << std::endl;
After experimenting readN() with the following main(), it seems that readN() works if the socket is not invalid handle (text/binary data sent by ncat). If the socket is a invalid handle, the function returns quickly.
#include <iostream>
#include <memory>
#include <string.h>
#ifdef _WIN64
#include <ws2tcpip.h>
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#endif
#include <errno.h>
#define MAXWAIT 5000
bool readN(SOCKET fd, int size, char *buffer)
{
fd_set readset;
struct timeval tv;
int left, res;
FD_ZERO(&readset);
FD_SET(fd, &readset);
left = size;
std::cout << "-----called readN to read " << size << " byte" << std::endl;
while (left > 0) {
tv.tv_sec = MAXWAIT;
tv.tv_usec = 0;
res = select(fd + 1, &readset, NULL, NULL, &tv);
if (res > 0) {
res = recv(fd, buffer, left, 0);
if (res == 0) { //connection closed by client
return false;
}
left -= res;
std::cout << "\treceived " << res << " left " << left << std::endl;
buffer += res;
} else if (res == 0) { //timer expired
std::cout << "\ttimer expired" << std::endl;
return false;
} else { //socket error
std::cout << "\tsocket error " << WSAGetLastError() << std::endl;
return false;
}
}
std::cout << "Print the buffer now\n" << (buffer - size) << std::endl;
return true;
}
int main(void)
{
int err;
SOCKET cfd = 0;
SOCKET afd = 0;
struct sockaddr_in addr;
socklen_t clen;
struct sockaddr_in caddr;
#ifdef _WIN64
WORD ver = 0x202;
WSADATA wsa_data;
memset(&wsa_data, 0, sizeof(wsa_data));
std::cout << "WSAStartup" << std::endl;
err = WSAStartup(ver, &wsa_data);
if (err < 0) goto error_exit;
#endif
memset(&addr, 0, sizeof(addr));
memset(&caddr, 0, sizeof(caddr));
std::cout << "socket" << std::endl;
afd = socket(AF_INET, SOCK_STREAM, 0);
if (afd < 0) goto error_exit;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(1234);
std::cout << "bind" << std::endl;
err = bind(afd, (struct sockaddr *)&addr, sizeof(addr));
if (err < 0) goto error_exit;
std::cout << "listen" << std::endl;
listen(afd, 5);
clen = sizeof(caddr);
std::cout << "accept" << std::endl;
cfd = accept(afd, (struct sockaddr *) &caddr, &clen);
if (cfd == INVALID_SOCKET) goto error_exit;
{
int size_ = 1024;
std::unique_ptr<char[]> buffer2 = std::make_unique<char[]>(size_);
std::cout << "readN" << std::endl;
if (readN(cfd, 1024, buffer2.get())) {
std::cout << "----read message----" << std::endl;
std::cout <<"\t"<< buffer2.get() << std::endl;
}
}
return 0;
error_exit:
std::cout << "Error!" << std::endl;
std::cout << "\tsocket error " << WSAGetLastError() << std::endl;
return 1;
}
Here is the code I ended up doing for an asynchronous client:
struct addrinfo *currentAddress = NULL;
void GetAddresses(std::string hostname, int port)
{
struct addrinfo hints;
std::cout << "Get adresses for hostname " << hostname << " port " << port << std::endl;
std::memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC; /* Allow IPV4 or IPV6 */
hints.ai_socktype = SOCK_STREAM; /* Datagram socket */
hints.ai_flags = 0;
hints.ai_protocol = 0; /* Any protocol */
std::string portStr;
portStr = std::to_string(port);
int status = getaddrinfo(hostname.c_str(), portStr.c_str(), &hints, ¤tAddress);
if (status != 0)
{
std::stringstream ss;
ss << "Cannot resolve hostname " << hostname << ". Error: " << gai_strerror(status);
throw std::runtime_error(ss.str());
}
}
void StepNextAddress()
{
currentAddress = currentAddress->ai_next;
}
void Connect(const std::string& address, int port)
{
/*
* Get all addresses for connection
*/
GetAddresses(address, port);
bool success = false;
while (currentAddress != NULL)
{
int s = socket(currentAddress->ai_family, currentAddress->ai_socktype, currentAddress->ai_protocol);
if (s == -1)
{
freeaddrinfo(currentAddress);
std::stringstream ss;
ss << "Error creating socket. Out of resources.";
throw std::runtime_error(ss.str());
}
std::cout << "Socket created: " << s << std::endl;
#ifdef _WIN32
unsigned long on = 1;
ioctlsocket(s, FIONBIO, &on); // Make it non blocking
#else
fcntl(s, F_SETFL, O_NONBLOCK); Make if non blocking
#endif
/*
* Connect to socket
*/
int status = connect(s, currentAddress->ai_addr, currentAddress->ai_addrlen);
if (status < 0 && errno != EINPROGRESS)
{
freeaddrinfo(currentAddress);
std::stringstream ss;
ss << "Error connecting socket to " << address << " port " << port << ".";
throw std::runtime_error(ss.str());
}
/*
* Wait socket to get ready to select
*/
fd_set readSet, writeSet, exceptSet;
FD_ZERO(&readSet);
FD_ZERO(&writeSet);
FD_ZERO(&exceptSet);
timeval tout;
tout.tv_sec = CONNECT_TIMEOUT_SECONDS;
int retval = select (s + 1, &readSet, &writeSet, &exceptSet, &tout);
if (retval == -1)
{
freeaddrinfo(currentAddress);
std::stringstream ss;
ss << "Error selecting socket.";
throw std::runtime_error(ss.str());
}
/*
* Timeout. Try next resources
*/
if (retval == 0)
{
std::cout << "Connection timedout. Trying next resource." << std::endl;
#ifdef _WIN32
_close(s);
#else
close(s);
#endif
StepNextAddress();
continue;
}
/*
* Wait socket to be ready to work
*/
int result = 0;
socklen_t result_len = sizeof(result);
int sts = getsockopt(s, SOL_SOCKET, SO_ERROR, (char *) &result, &result_len);
if (sts < 0)
{
freeaddrinfo(currentAddress);
std::stringstream ss;
ss << "Error getting socket option.";
throw std::runtime_error(ss.str());
}
if (result != 0)
{
freeaddrinfo(currentAddress);
std::stringstream ss;
ss << "Error getting socket resources.";
throw std::runtime_error(ss.str());
}
}
freeaddrinfo(currentAddress);
std::cout << "CONNECTED!!!!!!!!!!!!" << std::endl;
}
int main()
{
Connect("test.test.com", 21);
}
I don´t understand yet the selectusage together with the getsockopt. Are they necessary or in my case select would be enough ?
In that case, the select is thowing an error... What is wrong with the select code ?
Also, what about the error treatments here... The only case I´m trying to connect the next address in list is on timeout... Does it make sense ?
To finish, comments in the code are well appreciated.