TcpNoDelayMod icon indicating copy to clipboard operation
TcpNoDelayMod copied to clipboard

How to enable TCP_NODELAY option in both Server and Client?

Open Zeinab-Rahmani opened this issue 4 years ago • 2 comments

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

Here is a picture of the tx and rx:

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:

  1. Does the delayed sending (or receiving) is related to the TCP_NODELAY option of the TCP?

  2. 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";
    
  3. Am I putting the TCP_NODELAY option in the right place in both server and client codes?

Here are my codes for client:

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:

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!

Zeinab-Rahmani avatar Jan 22 '21 10:01 Zeinab-Rahmani

:)

FelipeAzambuja avatar Jun 06 '22 05:06 FelipeAzambuja

by the way, is it neccessary to add this mod in singal player`s game?

iamsoNewBee avatar Sep 20 '22 01:09 iamsoNewBee