I'm trying to build a fast server & client which work in localhost. The idea is to send data blobs from another program, and be quick about it. only one client connects to the server at a time.
I first tried implementing this using boost::asio library. everything worked fine, except that the throughput was abysmally slow, 415megabytes/s.
Then I proceeded to create a testcase with winsock, it too had very similar throughput, 434megabytes/s. abysmally slow.
I was expecting more of at range of 40Gigabytes or at least several gigabytes per second.
Id appreaciate any suggestions, as I am way out of my element, with networks programming.
My current client function:
bool sendDataWin(size_t size, const size_t blocksize, size_t port)
{
int result;
struct addrinfo *addressinfo = nullptr, hints;
auto sport = std::to_string(port);
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
// Resolve the local address and port to be used by the server
result = getaddrinfo("localhost", sport.c_str(), &hints, &addressinfo);
if (result != 0) {
printf("Error at client socket(): %ld\n", WSAGetLastError());
return false;
}
SOCKET connection = socket(
addressinfo->ai_family,
addressinfo->ai_socktype,
addressinfo->ai_protocol);
if (connection == INVALID_SOCKET) {
printf("Error at client socket(): %ld\n", WSAGetLastError());
freeaddrinfo(addressinfo);
return false;
}
// Try to put loopback fast path on.
bool value;
DWORD dwBytesRet;
int status =
WSAIoctl(
connection,
SIO_LOOPBACK_FAST_PATH,
&value,
sizeof(bool),
NULL,
0,
&dwBytesRet,
0,
0);
if (status == SOCKET_ERROR) {
DWORD LastError = ::GetLastError();
if (LastError == WSAEOPNOTSUPP) {
printf("client call is not supported :: SIO_LOOPBACK_FAST_PATH\n");
}
}
// Connect to server.
result = connect(connection, addressinfo->ai_addr, (int)addressinfo->ai_addrlen);
if (result == SOCKET_ERROR) {
printf("Error at client socket(): %ld\n", WSAGetLastError());
closesocket(connection);
return false;
}
freeaddrinfo(addressinfo);
std::vector<uint8_t> buffer;
buffer.resize(blocksize);
size_t total = 0;
do {
size_t sendSize = blocksize;
size_t next = total + sendSize;
if (next > size)
{
sendSize -= next - size;
}
// Echo the buffer back to the sender
result = send(connection, (const char*)buffer.data(), (int)sendSize, 0);
if (result == SOCKET_ERROR) {
printf("client send failed: %d\n", WSAGetLastError());
closesocket(connection);
return false;
}
total += sendSize;
} while (total < size);
result = shutdown(connection, SD_SEND);
if (result == SOCKET_ERROR) {
printf("client shutdown failed: %d\n", WSAGetLastError());
closesocket(connection);
return false;
}
// cleanup
closesocket(connection);
return true;
}
server function:
bool serverReceiveDataWin(size_t size, const size_t blocksize, size_t port)
{
int result;
struct addrinfo *addressinfo = nullptr, *ptr = nullptr, hints;
auto sport = std::to_string(port);
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
// Resolve the local address and port to be used by the server
result = getaddrinfo(nullptr, sport.c_str(), &hints, &addressinfo);
if (result != 0) {
printf("Error at server socket(): %ld\n", WSAGetLastError());
return false;
}
SOCKET ListenSocket = socket(addressinfo->ai_family, addressinfo->ai_socktype, addressinfo->ai_protocol);
if (ListenSocket == INVALID_SOCKET) {
printf("Error at server socket(): %ld\n", WSAGetLastError());
freeaddrinfo(addressinfo);
return false;
}
// Try to put loopback fast path on.
bool value;
DWORD dwBytesRet;
int status =
WSAIoctl(
ListenSocket,
SIO_LOOPBACK_FAST_PATH,
&value,
sizeof(bool),
NULL,
0,
&dwBytesRet,
0,
0);
if (status == SOCKET_ERROR) {
DWORD LastError = ::GetLastError();
if (LastError == WSAEOPNOTSUPP) {
printf("server call is not supported :: SIO_LOOPBACK_FAST_PATH\n");
}
}
// Setup the TCP listening socket
result = bind(ListenSocket, addressinfo->ai_addr, (int)addressinfo->ai_addrlen);
if (result == SOCKET_ERROR) {
printf("server bind failed with error: %d\n", WSAGetLastError());
freeaddrinfo(addressinfo);
closesocket(ListenSocket);
return false;
}
if (listen(ListenSocket, SOMAXCONN) == SOCKET_ERROR) {
printf("Listen failed with error: %ld\n", WSAGetLastError());
closesocket(ListenSocket);
return false;
}
// Accept a client socket
SOCKET ClientSocket = accept(ListenSocket, nullptr, nullptr);
if (ClientSocket == INVALID_SOCKET) {
printf("server accept failed: %d\n", WSAGetLastError());
closesocket(ListenSocket);
return false;
}
std::vector<uint8_t> buffer;
buffer.resize(blocksize);
size_t total = 0;
// Receive until the peer shuts down the connection
do {
size_t currentSize = blocksize;
size_t next = total + currentSize;
if (next > size)
{
currentSize -= next - size;
}
result = recv(ClientSocket, (char*)buffer.data(), (int)currentSize, 0);
if (result > 0)
{
total += result;
}
else if (result == 0)
{
printf("server Connection closing...\n");
return false;
}
else {
printf("server recv failed: %d\n", WSAGetLastError());
closesocket(ClientSocket);
return false;
}
} while (total < size);
result = shutdown(ClientSocket, SD_SEND);
if (result == SOCKET_ERROR) {
printf("server shutdown failed: %d\n", WSAGetLastError());
closesocket(ClientSocket);
return false;
}
// cleanup
closesocket(ClientSocket);
return true;
}
and the test program itself is:
int main()
{
int width = 1920;
int height = 1080;
const size_t totalBpp = 3;
const size_t totalSize = width * height * totalBpp;
size_t port = 27140;
size_t times = 1000;
size_t expectedData = totalSize * times;
// Initialize Winsock
int iResult;
WSADATA wsaData;
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
std::cout << "WSAStartup failed: " << iResult << std::endl;
return EXIT_FAILURE;
}
std::atomic_bool serverOk{ false };
std::atomic_bool clientOk{ false };
auto start = std::chrono::high_resolution_clock::now();
std::thread server = std::thread([&] {
serverOk = serverReceiveDataWin(expectedData, totalSize, port);
});
std::thread client = std::thread([&] {
clientOk = sendDataWin(expectedData, totalSize, port);
});
client.join();
server.join();
auto end = std::chrono::high_resolution_clock::now();
WSACleanup();
if (!(clientOk && serverOk))
{
if (!serverOk) std::cout << "Server was not OK." << std::endl;
if (!clientOk) std::cout << "Client was not OK." << std::endl;
return EXIT_FAILURE;
}
std::chrono::duration<double> diff = end - start;
double frameTime = diff.count() / times;
double fps = 1.0 / frameTime;
std::cout << "Sent: " << width << "x" << height << "_" << totalBpp << "(" << totalSize << "). times: " << times << std::endl;
std::cout << "frameTime: " << frameTime << "s." << std::endl;
std::cout << "fps: " << fps << "." << std::endl;
std::cout << "transfer rate : " << ((expectedData / diff.count()) / 1048576) << " mebibytes/s." << std::endl;
std::cout << "transfer rate : " << ((expectedData / diff.count()) / 1000000) << " megabytes/s." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds{ 60 });
return EXIT_SUCCESS;
}
on my machine I get these results:
Sent: 1920x1080_3(6220800).times : 1000
frameTime : 0.0138217s.
fps : 72.35.
transfer rate : 429.225 mebibytes / s.
transfer rate : 450.075 megabytes / s.
It seems that the issue was somewhat my firewall/anti-virus, I first unloaded F-secure, the speeds increased to 500megabytes/s.. after uninstall & reboot, the speeds increased to 4000megabytes/s.
A couple points.
the IOCTL is not called correctly.
bool value;
DWORD dwBytesRet;
int status =
WSAIoctl(
ListenSocket,
SIO_LOOPBACK_FAST_PATH,
&value,
sizeof(bool),
NULL,
0,
&dwBytesRet,
0,
0);
This should pass in a DWORD (32 bit integer) set to 1. The above code passes a C++ bool (probably only 1 byte) that was never initialized to anything.
Since this is only a single socket, this is going to be CPU limited, since it's effectively going to just be going from user/kernel and memcopying on a single thread. It's not likely that this will hit 40 Gbps over a single connection. Particularly since this is only sending 6GB worth of data.
You should turn off Nagle's Algorithm when sending large amounts of data like this. Set TCP_NODELAY on the sending socket.
Related
I have written code in C++ which takes the x and y coordinates of a robot and sends them across a socket using a little endian. The x and y coordinates are then received on the client end of the socket where they are used to control an actor in a game. All of my code works well but I would like to analyse how efficiently the coordinates are sent and received and to do this i would like to measure the delay in the sending of the coordinates.
I looked into this and saw that getting the time that the coordinate was sent and received and then calculating the difference was the way most people spoke about doing this. However, i am just confused how i would do this in my code as i have measured the time for the program to start up the robot by using int Start = Clock() and int End = Clock() and then getting the difference. But i don't know if this would be the most effective or accurate way of measuring the delay across the socket. So, i was wondering if anybody could suggest a way in which i could do this using the code below. The server sends the coordinates, and the client receives the coordinates.
SERVER code:
#undef UNICODE
#include "pch.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <chrono>
// Need to link with Ws2_32.lib
#pragma comment (lib, "Ws2_32.lib")
// #pragma comment (lib, "Mswsock.lib")
//#define DEFAULT_BUFLEN 8
#define DEFAULT_BUFLEN 5000
#define DEFAULT_PORT "27015"
//two structures for converting the x and y back into doubles/floats
union {
float myFloatx;
unsigned char myCharsx[sizeof(float)];
} testx;
union {
float myFloaty;
unsigned char myCharsy[sizeof(float)];
} testy;
double x;
double y;
int __cdecl main(void)
{
int start = clock();
WSADATA wsaData;
int iResult;
double x;
double y;
SOCKET ListenSocket = INVALID_SOCKET;
SOCKET ClientSocket = INVALID_SOCKET;
struct addrinfo* result = NULL;
struct addrinfo hints;
int iSendResult;
char recvbuf[DEFAULT_BUFLEN];
int recvbuflen = DEFAULT_BUFLEN;
//added (to send back bytes to client)----------------
SOCKET ConnectSocket = INVALID_SOCKET;
char sendvbuf[8];
int sendvbuflen = 8;
//---------------------------------------------
//Creating instances of the classes -------------------------------------------------------------------------------------------------------
Articares::Core::ArticaresComm instance;
Articares::Core::HMANData instance2;
Articares::Core::TargetParams instance3;
//Connecting to HMan -------------------------------------------------------------------------------------------------------
instance.EstablishConnection("192.168.102.1", 3000);
int end = clock();
int time = end - start;
std::cout << "time to connect = " << time << "\n";
//Starting the exercise and setting the targets --------------------------------------------------------------------------
instance.StartExercise(3);
instance.SetTransitionTime(8000, 1, 0);
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed with error: %d\n", iResult);
return 1;
}
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
// Resolve the server address and port
iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
if (iResult != 0) {
printf("getaddrinfo failed with error: %d\n", iResult);
WSACleanup();
return 1;
}
// Create a SOCKET for connecting to server
ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (ListenSocket == INVALID_SOCKET) {
printf("socket failed with error: %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}
// Setup the TCP listening socket
iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
if (iResult == SOCKET_ERROR) {
printf("bind failed with error: %d\n", WSAGetLastError());
freeaddrinfo(result);
closesocket(ListenSocket);
WSACleanup();
return 1;
}
freeaddrinfo(result);
printf("listening\n");
iResult = listen(ListenSocket, SOMAXCONN);
if (iResult == SOCKET_ERROR) {
printf("listen failed with error: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
// Accept a client socket
ClientSocket = accept(ListenSocket, NULL, NULL);
if (ClientSocket == INVALID_SOCKET) {
printf("accept failed with error: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
//sleep command added to stop the targets moving before the on game instructions have been read by the user
Sleep(80);
//Set target 1 with targetx = 10mm and targety = 10 mm, ‘StiffnessX’ = 100 N/m, ‘StiffnessY’ = 100 N/m, ‘target gain’ = 1,
//IF WISHING TO INCREASE THE RESISTANCE PLEASE INCREASE THE 4TH AND 5TH VALUES IN THE SetTarget() function:
instance.SetTarget("1", "10", "10", "50", "50", "0", "0", "0", "0", "0", "0", "1", "0");
instance.SetTarget("2", "10", "50", "50", "50", "0", "0", "0", "0", "0", "0", "1", "0");
instance.SetTarget("3", "50", "10", "50", "50", "0", "0", "0", "0", "0", "0", "1", "0");
// No longer need server socket
closesocket(ListenSocket);
// Receive until the peer shuts down the connection
do {
while (recv(ClientSocket, recvbuf, recvbuflen, MSG_PEEK) < 8) { ; }
iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
//std::cout << " hello" << recvbuf << std::endl;
if (iResult > 0) {
printf("Bytes received: %d\n", iResult);
// Echo the buffer back to the sender
iSendResult = send(ClientSocket, recvbuf, iResult, 0);
if (iSendResult == SOCKET_ERROR) {
printf("send failed with error: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
printf("Bytes sent: %d\n", iSendResult);
}
else if (iResult == 0)
printf("Connection closing...\n");
else {
printf("recv failed with error: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
} while (false);
//Start:; allows the continual sending of coordinates to occur meaning that the program will not shutdown unless it is shutdown from the client end.
Start:;
for (int i = 0; i < 50; i++)
{
std::cout << i << "\n";
Sleep(20);
// Send an initial buffer
double x = instance.hman_data.location_X;
double y = instance.hman_data.location_Y;
//following sends the doubles as char to the receiver
testx.myFloatx = x;
sendvbuf[0] = (int)testx.myCharsx[3];
sendvbuf[1] = (int)testx.myCharsx[2];
sendvbuf[2] = (int)testx.myCharsx[1];
sendvbuf[3] = (int)testx.myCharsx[0];
testy.myFloaty = y;
sendvbuf[4] = (int)testy.myCharsy[3];
sendvbuf[5] = (int)testy.myCharsy[2];
sendvbuf[6] = (int)testy.myCharsy[1];
sendvbuf[7] = (int)testy.myCharsy[0];
//changed the length of the buffer sent as it was causing problems with sending coordinates.
iResult = send(ClientSocket, sendvbuf, 8, 0);
std::cout << "X = " << x << " " << "Y = " << y << "\n";
Sleep(200);
if (iResult == SOCKET_ERROR) {
printf("send failed with error: %d\n", WSAGetLastError());
closesocket(ConnectSocket);
WSACleanup();
return 1;
}
}
goto Start;
// shutdown the connection since we are done
iResult = shutdown(ClientSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
printf("shutdown failed with error: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
// cleanup
closesocket(ClientSocket);
WSACleanup();
CLIENT code:
#include "Control.h"
#include "control.h"
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <string>
// Need to link with Ws2_32.lib, Mswsock.lib, and Advapi32.lib
#pragma comment (lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "AdvApi32.lib")
//#define DEFAULT_BUFLEN 512
#define DEFAULT_BUFLEN 5000
#define DEFAULT_PORT "27015"
#include "Components/StaticMeshComponent.h"
//Below the "unions" are called labels and these allow for the conversion of the bytes back to a numerical value i.e. coordinate
union {
float myFloatx;
unsigned char myCharsx[sizeof(float)];
} testx;
union {
float myFloaty;
unsigned char myCharsy[sizeof(float)];
} testy;
//Declaring global variables
SOCKET ClientSocket = INVALID_SOCKET;
WSADATA wsaData;
SOCKET ConnectSocket = INVALID_SOCKET;
struct addrinfo* result = NULL,
* ptr = NULL,
hints;
char sendvbuf[8];
char recvbuf[8];
int iResult;
int iSendResult;
int recvbuflen = 8;
int sendvbuflen = 8;
double x;
double y;
// Sets default values
AControl::AControl()
{
// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
PrimaryActorTick.bCanEverTick = true;
//Creating an instance of the Root component and allows it to be seen in the editor and game for editing e.g. scale
Root = CreateDefaultSubobject<USceneComponent>(TEXT("Root"));
RootComponent = Root;
//Creating and assigning the static mesh component
Mesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));
//want to connect the static mesh with the root component so the pawn component can be edited in the editor
Mesh->AttachTo(Root);
}
// Called when the game starts or when spawned
void AControl::BeginPlay()
{
//////Below has the purpose of starting up the server: -----------------------------------------------
wchar_t command[] = L"C:\\Users\\CCRT\\Documents\\UPDATED\\FINALSERVER\\FINALSERVER\\Debug\\FINALSERVER.exe";
// Start the child process.
STARTUPINFOW si{ sizeof(STARTUPINFOW) };
PROCESS_INFORMATION pi;
if (!CreateProcessW(NULL, // No module name (use command line)
command, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi) // Pointer to PROCESS_INFORMATION structure
)
{
std::cout<< "CreateProcess failed\n";
}
else {
std::cout << "Command success\n";
}
//Below makes the client repeatedly search for the server until it connects with it.
int connected = 0;
while (connected == 0)
{
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
//printf("WSAStartup failed with error: %d\n", iResult);
std::cout << "WSAStartup failed with error: %d\n";
//return 1;
goto finish;
}
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
// Resolve the server address and port
iResult = getaddrinfo("127.0.0.1\0", DEFAULT_PORT, &hints, &result);
if (iResult != 0) {
//printf("getaddrinfo failed with error: %d\n", iResult);
std::cout << "getaddrinfo failed with error\n";
WSACleanup();
//return 1;
goto finish;
}
// Attempt to connect to an address until one succeeds
for (ptr = result; ptr != NULL; ptr = ptr->ai_next) {
// Create a SOCKET for connecting to server
ConnectSocket = socket(ptr->ai_family, ptr->ai_socktype,
ptr->ai_protocol);
if (ConnectSocket == INVALID_SOCKET) {
//printf("socket failed with error: %ld\n", WSAGetLastError());
std::cout << "socket failed with error\n";
WSACleanup();
//return 1;
goto finish;
}
// Connect to server.
iResult = connect(ConnectSocket, ptr->ai_addr, (int)ptr->ai_addrlen);
if (iResult == SOCKET_ERROR) {
closesocket(ConnectSocket);
ConnectSocket = INVALID_SOCKET;
continue;
}
break;
}
freeaddrinfo(result);
if (ConnectSocket == INVALID_SOCKET) {
//printf("Unable to connect to server!\n");
std::cout << "Unable to connect to server!\n";
WSACleanup();
}
else
{
connected = 1;
}
}
iResult = send(ConnectSocket, sendvbuf, (int)sendvbuflen, 0);
if (iResult == SOCKET_ERROR) {
//printf("send failed with error: %d\n", WSAGetLastError());
std::cout << "send failed with error:\n";
closesocket(ConnectSocket);
WSACleanup();
//return 1;
goto finish;
}
std::cout << "Bytes Sent " << iResult << " " << sendvbuf[1] << std::endl;
finish:;
Super::BeginPlay();
}
// Called every frame
void AControl::Tick(float DeltaTime)
{
// Following recieves the coordinates from the server:
while (recv(ConnectSocket, recvbuf, recvbuflen, MSG_PEEK) < 8) { ; }
iResult = recv(ConnectSocket, recvbuf, recvbuflen, 0);
if (iResult > 0) {
printf("Bytes received: %d\n", iResult);
//the following turns the chars recieved back into the double format (uses the labels which were declared before)
testx.myCharsx[0] = recvbuf[3];
testx.myCharsx[1] = recvbuf[2];
testx.myCharsx[2] = recvbuf[1];
testx.myCharsx[3] = recvbuf[0];
x = testx.myFloatx;
testy.myCharsy[0] = recvbuf[7];
testy.myCharsy[1] = recvbuf[6];
testy.myCharsy[2] = recvbuf[5];
testy.myCharsy[3] = recvbuf[4];
y = testy.myFloaty;
std::cout << std::endl;
FVector NewLocation = GetActorLocation();
FRotator NewRotation = GetActorRotation();
// NewLocation.X = -y is corrct way round for the HMan to control the ball.
//NewLocation.Y = -x
//IF THE USER IS NEEDING MORE ASSISTANCE THE X AND Y COORDINATES CAN BE SCALED UP BELOW BY MULTIPLYING THEM BY A HIGHER VALUE:--------------------------------------------------------------------------
NewLocation.X = -y *150000;
NewLocation.Y = -x *150000;
NewLocation.Z = 0;
NewRotation.Roll = 0;
NewRotation.Pitch = 0;
NewRotation.Yaw = 0;
SetActorLocationAndRotation(NewLocation, NewRotation, true);
//the true is added to stop the actor moving through walls.
printf("NEW Y: %f\n", NewLocation.Y);
printf("NEW X: %f\n", NewLocation.X);
}
else if (iResult == 0)
//printf("Connection closing...\n");
std::cout << "Connection closing...\n";
else {
//printf("recv failed with error: %d\n", WSAGetLastError());
std::cout << "recv failed with error:\n";
closesocket(ClientSocket);
WSACleanup();
//return 1;
goto finish;
}
finish:;
Super::Tick(DeltaTime);
}
Thanks for any help.
So I've been creating a winsock server/client in UE4. I can get the client to connect to the server however once the client sends the first message it seems to close the socket, preventing any further messages to be sent to the server. It also seems like the server is doing the same thing when sending data.
client
// Convert IP & port to standard lib
const std::string IP = std::string(TCHAR_TO_UTF8((*GameInstance->GetIPAddress())));
const std::string PORT = std::string(TCHAR_TO_UTF8(*GameInstance->GetPort()));
// Set the version of WSA we are using
auto Version = MAKEWORD(2, 2);
WSAData WSData;
struct addrinfo* Result = nullptr, * ptr = nullptr, hints;
int iResult; // Store Initializing results
std::string message; // Define a message to send to the server
UE_LOG(LogTemp, Log, TEXT("Starting Client"));
// Initialize WinSock
iResult = WSAStartup(Version, &WSData); // Start winsock
if(iResult != 0)
{
UE_LOG(LogTemp, Error, TEXT("Failed to initialize winsock"));
return ECreateConnectionFlag::WINSOCK_FAILED;
}
UE_LOG(LogTemp, Log, TEXT("Initialized WinSock"));
// Setup hints
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
UE_LOG(LogTemp, Log, TEXT("Connecting"));
// Get the address details
iResult = getaddrinfo(IP.c_str(), PORT.c_str(), &hints, &Result);
if(iResult != 0)
{
UE_LOG(LogTemp, Error, TEXT("Error getting address info from the server"));
WSACleanup();
return 0;
}
// Connect the player
for(ptr = Result; ptr != nullptr; ptr->ai_next)
{
GameInstance->SetPlayerSocket(socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol));
if(GameInstance->GetLoggedInPlayer().PlayerSocket == INVALID_SOCKET)
{
UE_LOG(LogTemp, Error, TEXT("Failed to create socket"));
WSACleanup();
return 0;
}
iResult = connect(GameInstance->GetLoggedInPlayer().PlayerSocket, ptr->ai_addr, ptr->ai_addrlen);
if(iResult == SOCKET_ERROR)
{
closesocket(GameInstance->GetLoggedInPlayer().PlayerSocket);
GameInstance->SetPlayerSocket(INVALID_SOCKET);
continue;
}
break;
}
freeaddrinfo(Result); // Release Address information as it's no longer required
// Ensure the socket is valid
if(GameInstance->GetLoggedInPlayer().PlayerSocket == INVALID_SOCKET)
{
UE_LOG(LogTemp, Error, TEXT("Unable to connect to server..."));
WSACleanup();
return 0;
}
// WE ARE CONNECTED
/* CONNECT AND SEND USERNAME */
FString SignInMessage = FString("Username-" + GameInstance->GetLoggedInPlayer().Username);
std::string ConnectionMessage = std::string(TCHAR_TO_UTF8(*SignInMessage));
iResult = send(GameInstance->GetLoggedInPlayer().PlayerSocket, ConnectionMessage.c_str(), (int)strlen(ConnectionMessage.c_str()), 0);
if(iResult <= 0)
{
int error = WSAGetLastError();
UE_LOG(LogTemp, Error, TEXT("Failed to send message: %d"), error);
return 0;
}
while(bRunThread)
{
UE_LOG(LogTemp, Log, TEXT("Receiving Data"));
/* DISCONNECT FROM SERVER */
const std::string msg = "Hello World";
iResult = send(GameInstance->GetLoggedInPlayer().PlayerSocket, msg.c_str(), (int)strlen(msg.c_str()), 0);
if(iResult <= 0)
{
int error = WSAGetLastError();
UE_LOG(LogTemp, Error, TEXT("Failed to send message: %d"), error);
return 0;
}
FPlatformProcess::Sleep(1.0f);
}
/* DISCONNECT FROM SERVER */
const std::string DisconnectMsg = "Disconnect";
iResult = send(GameInstance->GetLoggedInPlayer().PlayerSocket, DisconnectMsg.c_str(), (int)strlen(DisconnectMsg.c_str()), 0);
if(iResult <= 0)
{
int error = WSAGetLastError();
UE_LOG(LogTemp, Error, TEXT("Failed to send message: %d"), error);
return 0;
}
UE_LOG(LogTemp, Warning, TEXT("Client Disconnected"));
closesocket(GameInstance->GetLoggedInPlayer().PlayerSocket);
WSACleanup();
return 0;
}
server
WSAData wsa;
struct addrinfo hints; // Server Hint details
struct addrinfo* server = NULL; // Address info of the server
SOCKET serverSocket = INVALID_SOCKET; // Server Listening Socket
PlayerArray* Players = new PlayerArray(); // Reference to all the players in the server
LobbyArray* Lobbies = new LobbyArray(); // Reference to all the lobbies in the server
// Initialize the winsock library
std::cout << "Initializing WinSock..." << std::endl;
int WSA_Init = WSAStartup(MAKEWORD(2, 2), &wsa);
if (WSA_Init != 0)
{
std::cerr << "Error Initializing Winsock";
WSACleanup();
return;
}
else
{
std::cout << "Winsock Initialized" << std::endl;
}
// Setup Hints
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
// Setup the server
std::cout << "Setting up Server" << std::endl;
getaddrinfo(IP_ADDRESS, PORT, &hints, &server);
// Create the listening socket
std::cout << "Creating Listening socket" << std::endl;
serverSocket = socket(server->ai_family, server->ai_socktype, server->ai_protocol);
if (serverSocket == INVALID_SOCKET)
{
std::cerr << "Failed creating listening socket" << std::endl;
WSACleanup();
return;
}
else
{
std::cout << "Created listen socket" << std::endl;
}
// Set the socket to be TCP
setsockopt(serverSocket, IPPROTO_TCP, TCP_NODELAY, &OPTION_VALUE, sizeof(int));
// Bind the socket
std::cout << "Binding Socket..." << std::endl;
bind(serverSocket, server->ai_addr, (int)server->ai_addrlen);
// Start the server
std::cout << "Server has started & is listening..." << std::endl;
listen(serverSocket, SOMAXCONN);
while (true)
{
std::cout << "Players Connected: " << Players->Count() << std::endl;
SOCKET Incoming = INVALID_SOCKET; // Define a socket for anything incoming
Incoming = accept(serverSocket, NULL, NULL); // Accept the incoming message from the socket
// If the socket is not valid than continue through the loop
if (Incoming == INVALID_SOCKET)
{
std::cout << "Invalid Socket" << std::endl;
continue;
}
else
{
std::cout << "Valid Socket" << std::endl;
}
char tempmsg[DFT_BUFLEN] = ""; // Define a temp msg to store the message from the client
int received = recv(Incoming, tempmsg, DFT_BUFLEN, 0); // Receive a message from the client
std::string convertedMessage = tempmsg;
// Check that the received message is from a valid socket
if (received != SOCKET_ERROR)
{
std::string message = tempmsg; // Assing the temp message to a string to split
if (convertedMessage == "Disconnect")
{
Players->RemovePlayer(Incoming);
std::cout << "Player Disconnected..." << std::endl;
continue;
}
else
{
std::cout << tempmsg << std::endl; // === DEBUG ===
// Split the string
char* next_split;
char* split_string = strtok_s(tempmsg, "-", &next_split);
std::string FirstMsg = split_string;
if (FirstMsg == "Username")
{
std::cout << next_split << " Has joined the server" << std::endl; // Server message
// Get the player that we want to set the username to
// Create the player and add it to the server list
Player* NewPlayer = new Player();
Players->AddPlayer(NewPlayer);
NewPlayer->SetUsername(next_split); // Set the usernames
continue;
}
else if (split_string == "Lobby")
{
if (next_split == "Create")
{
Lobby* NewLobby = Lobbies->CreateLobby(); // Create a new lobby
Player* SocketPlayer = Players->GetPlayerBySocket(Incoming); // Get the player creating it by socket
// ensure that the player is valid, if so add the player to the lobby
// Otherwise send an error message to the console.
if (SocketPlayer != nullptr)
{
NewLobby->AddPlayerToLobby(SocketPlayer);
}
else
{
std::cerr << "Failed Locate player to add to lobby" << std::endl;
}
}
else if (next_split == "Destroy")
{
// TODO: Destroy Specific lobby
}
continue;
}
else
{
std::cout << "Error Reading Message" << std::endl;
}
}
}
else
{
std::cerr << "Socket Error when recieving message" << std::endl;
}
}
// Clean up the server
delete Players;
delete Lobbies;
closesocket(serverSocket);
WSACleanup();
return;
Console output after disconnecting
You are making a fundamental TCP mistake. TCP is a stream protocol., its only gurantees are
the bytes you send will be received in the same order they were sent
they will be received only once
BUT there are no 'messages' or 'records' in TCP. You can send a 100 byte message and the other end can receive
one 100 byte message
25 4 byte messages
100 1 byte messages
one 25, one 12, one 3, and one 60 (hope my math is correct)
So in the receive logic you must do this
char buffer[1000]; // or whatever
int length = ????;
char* bptr = buffer;
while(length > 0){
int recvLen = recv(sock, bptr, length,0);
if (recvLen < 1){
// error - disconnect or other failure
break;
}
bptr += recvLen;
length -= recvLen;
}
Ie keep pulling data till you have the whole message
BUT this means you need to know the messages length in advance. So either
send a well known sized length first
send fixed length messages
Or you can have a recognizable termination sequence- ie 10 byte of FF means end of message (see crlfcrlf at end of HTTP get for example)
The first option is the most robust (send length then data)
I code a small client for IPv4 / IPv6 with a hostname resolver.
For IPv4 and resolver it's fine but not with IPv6 when connect() I have a problem WSAGetLastError() say WSAEAFNOSUPPORT.
I have a switch all structures (AF_INET -> AF_INET6, SOCKADDR_IN -> SOCKADDR_IN6) to IPv6 versions.
#include <winsock2.h>
#include <ws2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
int main()
{
printf("Simple_Client IPv4 & IPv6\n\n");
// Initiates Winsock
WSADATA WSAData;
WSAStartup(MAKEWORD(2, 0), &WSAData);
// Get Parameters IP/PORT and request
std::string str_HOSTNAME = "mirror.neostrada.nl";
int PORT = 21;
// RESOLVE IP
BOOL is_IPv6 = FALSE;
std::string str_dest_ip = "";
addrinfo hints = { 0 };
hints.ai_flags = AI_ALL;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
addrinfo * pResult;
getaddrinfo(str_HOSTNAME.c_str(), NULL, &hints, &pResult);
if (pResult == NULL)
{
printf("pResult error\n");
return -1;
}
if (pResult->ai_family == AF_INET)
{
printf("getaddrinfo = AF_INET (IPv4)\n");
is_IPv6 = FALSE;
}
if (pResult->ai_family == AF_INET6)
{
printf("getaddrinfo = AF_INET6 (IPv6)\n");
is_IPv6 = TRUE;
}
char str[128];
memset(str, 0, sizeof(str));
if (is_IPv6 == FALSE) // IPv4
{
if (inet_ntop(AF_INET, &(*((ULONG*)&(((sockaddr_in*)pResult->ai_addr)->sin_addr))), str, INET_ADDRSTRLEN))
str_dest_ip = char_to_string(str, strlen(str)); // Copy char in std::string
else
printf("inet_ntop error\n");
}
if (is_IPv6 == TRUE) // IPv6
{
if (inet_ntop(AF_INET6, &(*((ULONG*)&(((sockaddr_in6 *)pResult->ai_addr)->sin6_addr))), str, INET6_ADDRSTRLEN))
str_dest_ip = char_to_string(str, strlen(str)); // Copy char in std::string
}
printf("%s : %s | Port : %i\n", is_IPv6 ? "IPv6" : "IPv4", str_dest_ip.c_str(), PORT);
// Connect to the HOSTNAME
SOCKET sock;
if (is_IPv6 == TRUE)
{
SOCKADDR_IN6 sin;
sin.sin6_family = AF_INET6;
if(inet_pton(sin.sin6_family, str_dest_ip.c_str(), &sin) != 1)
printf("ERROR inet_pton %i\n", WSAGetLastError());
sin.sin6_port = htons(PORT);
sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
if (sock == INVALID_SOCKET)
return -2;
if (connect(sock, (struct sockaddr *)&sin, sizeof(sin)) != SOCKET_ERROR)
{
printf("Connect Success to %s | PORT : %i\n", str_dest_ip.c_str(), PORT);
}
else
{
printf("ERROR connect to %s | PORT : %i : %i\n", str_dest_ip.c_str(), PORT, WSAGetLastError());
Sleep(10000);
return -2;
}
}
char buf[1024] = { 0 };
int size_recv = recv(sock, buf, sizeof(buf), 0);
printf("SIZE RECV = %i | DATA RECV = %s\n", size_recv, char_to_string(buf, size_recv).c_str());
WSACleanup();
getchar();
return 0;
}
If somebody have a idea, thanks for reading.
The problem is here:
inet_pton(sin.sin6_family, str_dest_ip.c_str(), &sin)
This writes the IPv6 address on top of the sin6_family field, damaging the whole structure.
It should be:
inet_pton(sin.sin6_family, str_dest_ip.c_str(), &sin.sin6_addr)
It's also a good idea to zero-initialize the whole sin structure in the beginning because it has more fields than you're filling in.
You are not using getaddrinfo() correctly.
For one thing, getaddrinfo() returns an error code that you are ignoring.
For another thing, getaddrinfo() returns a linked list that potentially contains multiple addresses in a mix of IPv4 and/or IPv6, due to your use of AF_UNSPEC. If you are only interested in IPv6, set hints.ai_family to AF_INET6 rather than AF_UNSPEC.
But either way, a given hostname may have multiple IPs associated with it, and they might not all be reachable from your location, so you should be connect()'ing to each address in the list, either one at a time or in parallel, until one of them succeeds.
Also, there is no need to use inet_pton() in this situation at all (which you are not using correctly, as explained by #rustyx's answer). getaddrinfo() returns fully populated sockaddr_in(6) structs that you can pass as-is to connect().
Try something more like this instead:
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iostream>
#include <string>
#pragma comment(lib, "ws2_32.lib")
std::string addr_to_str(addrinfo *addr)
{
char str[128];
switch (addr->ai_family)
{
case AF_INET: // IPv4
{
if (inet_ntop(AF_INET, &(((sockaddr_in*)(addr->ai_addr))->sin_addr), str, INET_ADDRSTRLEN))
return str;
ret = WSAGetLastError();
break;
}
case AF_INET6: // IPv6
{
if (inet_ntop(AF_INET6, &(((sockaddr_in6*)(addr->ai_addr))->sin6_addr), str, INET6_ADDRSTRLEN))
return str;
ret = WSAGetLastError();
break;
}
default:
ret = WSAEAFNOSUPPORT;
break;
}
std::cerr << "inet_ntop error: " << ret << std::endl;
return "";
}
int main()
{
std::cout << "Simple_Client IPv4 & IPv6" << std::endl << std::endl;
// Initiates Winsock
WSADATA WSAData;
int ret = WSAStartup(MAKEWORD(2, 0), &WSAData);
if (ret != 0)
{
std::cerr << "WSAStartup error: " << ret << std::endl;
return -1;
}
// Get Parameters IP/PORT and request
std::string str_HOSTNAME = "mirror.neostrada.nl";
int PORT = 21;
// RESOLVE IP
addrinfo hints = { 0 };
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
addrinfo *pResult = NULL;
ret = getaddrinfo(str_HOSTNAME.c_str(), std::to_string(PORT).c_str(), &hints, &pResult);
if (ret != 0)
{
std::cerr << "getaddrinfo error: " << ret << std::endl;
WSACleanup();
return -1;
}
// Log the IPs
bool has_IPv4 = false;
bool has_IPv6 = false;
for (addrinfo *addr = pResult; addr != NULL; addr = addr->ai_next)
{
switch (addr->ai_family)
{
case AF_INET: // IPv4
{
has_IPv4 = true;
std::cout << "IPv4 : " << addr_to_str(addr);
break;
}
case AF_INET6: // IPv6
{
has_IPv6 = true;
std::cout << "IPv6 : " << addr_to_str(addr);
break;
}
}
}
// Connect to the HOSTNAME
SOCKET sock = INVALID_SOCKET;
if (has_IPv6)
{
// try IPv6 first...
for (addrinfo *addr = pResult; addr != NULL; addr = addr->ai_next)
{
if (addr->ai_family != AF_INET6)
continue;
std::cout << "Connecting to IPv6 : " << addr_to_str(addr) << " | Port : " << PORT << std::endl;
sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (sock == INVALID_SOCKET)
{
ret = WSAGetLastError();
std::cerr << "socket error: " << ret << std::endl;
continue;
}
if (connect(sock, addr->ai_addr, addr->ai_addrlen) == SOCKET_ERROR)
{
ret = WSAGetLastError();
std::cerr << "connect error: " << ret << std::endl;
closesocket(sock);
sock = INVALID_SOCKET;
continue;
}
break;
}
}
if ((sock == INVALID_SOCKET) && (has_IPv4))
{
// try IPv4 next...
for (addrinfo *addr = pResult; addr != NULL; addr = addr->ai_next)
{
if (addr->ai_family != AF_INET)
continue;
std::cout << "Connecting to IPv4 : " << addr_to_str(addr) << " | Port : " << PORT << std::endl;
sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if (sock == INVALID_SOCKET)
{
ret = WSAGetLastError();
std::cerr << "socket error: " << ret << std::endl;
continue;
}
if (connect(sock, addr->ai_addr, addr->ai_addrlen) == SOCKET_ERROR)
{
ret = WSAGetLastError();
std::cerr << "connect error: " << ret << std::endl;
closesocket(sock);
sock = INVALID_SOCKET;
continue;
}
break;
}
}
freeaddrinfo(pResult);
if (sock == INVALID_SOCKET)
{
WSACleanup();
return -2;
}
std::cout << "Connect Successful" << std::endl;
char buf[1024];
int size_recv = recv(sock, buf, sizeof(buf), 0);
if (size_recv == SOCKET_ERROR)
{
ret = WSAGetLastError();
std::cerr << "recv error: " << ret << std::endl;
}
else
{
std::cout << "SIZE RECV = " << size_recv;
if (size_recv > 0)
{
std::cout << " | DATA RECV = ";
std::cout.write(buf, size_recv);
}
std::cout << std::endl;
}
closesocket(sock);
WSACleanup();
std::cin.get();
return 0;
}
I'm creating a simple chat client, where clients join the server and send messages to it, which then in turn sends those messages to all the other connected clients.
The issue I have is with figuring out how to have the clients be able to send messages to the server while also being able to simultaneously receive inbound messages from other clients via the server. The problem I run into is that any method I find of taking an input in, always makes the thread hang (as it's waiting for an input from the user).
This is true also for when listening for incoming data, but I've found a way to timeout the wait so I can loop it, and do things before seeing if there is any data needing to be processed again.
My client code:
int main()
{
WSADATA wsaData;
SOCKET ConnectionSocket = INVALID_SOCKET;
addrinfo *result = NULL;
addrinfo hints;
addrinfo *ptr = NULL;
fd_set set;
HANDLE hThread;
string username;
unsigned threadID;
int iResult;
int sent_msg;
int fileD;
char send_buffer[DEFAULT_BUFLEN];
char recieve_buffer[DEFAULT_BUFLEN];
bool connected = true;
int error_code;
timeval timeout_period;
/*
* Initalize WinSock.
*/
if (init_WinSock(wsaData)) { return 1; };
/*
* Zeroize structure.
*/
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
getaddrinfo("192.168.1.77", DEFAULT_PORT, &hints, &result);
ptr = result;
ConnectionSocket = socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol);
iResult = connect(ConnectionSocket, ptr->ai_addr, ptr->ai_addrlen);
freeaddrinfo(result);
if (iResult == SOCKET_ERROR) {
error_code = WSAGetLastError();
closesocket(ConnectionSocket);
cout << "Unable to connect!";
WSACleanup();
system("Pause >> null");
return 1;
}
cout << "Connected." << endl;
cout << "Please choose a username: ";
do {
cin.getline(send_buffer, (int)strlen(send_buffer));
if ((int)strlen(send_buffer) > 10) {
cout << "Name too long! Choose again: ";
memset(send_buffer, 0, DEFAULT_BUFLEN);
}
} while ((int)strlen(send_buffer) > 10);
username = send_buffer;
/*
* Initial connection. Send ID/username.
*/
send(ConnectionSocket, send_buffer, (int)strlen(send_buffer), 0);
memset(recieve_buffer, 0, DEFAULT_BUFLEN);
while (iResult != SOCKET_ERROR) {
FD_ZERO(&set); /* clear the set */
FD_SET(ConnectionSocket, &set);
FD_SET(0, &set);
error_code = select(ConnectionSocket + 1, &set, NULL, NULL, NULL);
error_code = WSAGetLastError();
if (FD_ISSET(0, &set)) {
cin.getline(send_buffer, DEFAULT_BUFLEN);
cout << username << ": " << send_buffer << endl;
send(ConnectionSocket, send_buffer, DEFAULT_BUFLEN, 0);
memset(send_buffer, 0, DEFAULT_BUFLEN);
} else if (FD_ISSET(ConnectionSocket, &set)) {
iResult = recv(ConnectionSocket, recieve_buffer, DEFAULT_BUFLEN, 0);
recieve_buffer[iResult] = '\0';
cout << "Placeholder: " << recieve_buffer << endl;
}
}
closesocket(ConnectionSocket);
cout << "\n\nDisconnected...";
system("pause >> null");
return 0;
}
Server code:
unsigned __stdcall handle_client(void *data)
{
client_socket_data_type client;
char recvbuf[DEFAULT_BUFLEN];
timeval timeout_period;
fd_set set;
int iResult;
// mutex mtx;
client = *((client_socket_data_type *)data);
memset(recvbuf, 0, DEFAULT_BUFLEN);
timeout_period.tv_sec = MAX_TIMEOUT;
timeout_period.tv_usec = 0;
FD_ZERO(&set); /* clear the set */
FD_SET(client.client_socket, &set);
iResult = select(client.client_socket, &set, NULL, NULL, &timeout_period);
while (iResult != SOCKET_ERROR) {
if (iResult > 0) {
recv(client.client_socket, recvbuf, DEFAULT_BUFLEN, 0);
if (recvbuf != " ") {
for each (SOCKET connected_clients in client.connected_clients)
{
if (client.client_socket != connected_clients) {
send(connected_clients, recvbuf, DEFAULT_BUFLEN, 0);
memset(recvbuf, 0, DEFAULT_BUFLEN);
}
}
}
}
iResult = select(client.client_socket, &set, NULL, NULL, &timeout_period);
}
clients--;
return 0;
}
int main()
{
WSADATA wsaData;
SOCKET listen_socket = INVALID_SOCKET;
SOCKET client_socket = INVALID_SOCKET;
struct addrinfo *result = NULL;
struct addrinfo hints;
client_socket_data_type client;
HANDLE hThread;
unsigned threadID;
char recv_buffer[DEFAULT_BUFLEN];
void *ctx;
int error_code;
int iResult;
/*
* Initalize WinSock.
*/
iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed with error: %d\n", iResult);
return 1;
}
/*
* Zeroize structure.
*/
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
if (iResult != 0) {
printf("getaddrinfo failed with error: %d\n", iResult);
WSACleanup();
return 1;
}
listen_socket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (listen_socket == INVALID_SOCKET) {
printf("socket failed with error: %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}
iResult = bind(listen_socket, result->ai_addr, (int)result->ai_addrlen);
if (iResult == SOCKET_ERROR) {
printf("bind failed with error: %d\n", WSAGetLastError());
freeaddrinfo(result);
closesocket(listen_socket);
WSACleanup();
return 1;
}
/*
* Finished setup. Clear structure.
*/
freeaddrinfo(result);
/*
* Listen on port for inbound connections.
*/
iResult = listen(listen_socket, SOMAXCONN);
if (iResult == SOCKET_ERROR) {
printf("listen failed with error: %d\n", WSAGetLastError());
closesocket(listen_socket);
WSACleanup();
return 1;
}
cout << "Server initalized successfully. Waiting on client connections. 0/" << MAX_CLIENTS << endl;
memset(recv_buffer, 0, DEFAULT_BUFLEN);
while (true) {
while (clients < MAX_CLIENTS) {
client_socket = SOCKET_ERROR;
while (client_socket == SOCKET_ERROR) {
client_socket = accept(listen_socket, NULL, NULL);
}
client.client_socket = client_socket;
clients++;
cout << "Client connected. Number of connected clients: " << clients << '/' << MAX_CLIENTS << endl;
/*
* Initial connection. Asking for name.
*/
recv(client.client_socket, recv_buffer, DEFAULT_BUFLEN, 0);
cout << "Debug, name recieved. Name is: " << recv_buffer << endl;
/*
* Name put inside structure.
*/
client.client_name = recv_buffer;
ctx = &client;
hThread = (HANDLE)_beginthreadex(NULL, 0, &handle_client, ctx, 0, &threadID);
client.connected_clients.push_back(client.client_socket);
memset(recv_buffer, 0, DEFAULT_BUFLEN);
}
}
return 0;
}
I'm trying to create a server that talks with 2 clients, 1 in each time. After the talking with one client, the server sends a message to both clients.
I found a basic code of a server, and I tried to upgrade it to accept multiple number of connections, and I saw 2 ways of it : threads, or doing array of sockets, but I couldn't understand it.
Can someone explain me how to use threads and give examples please?
This is the code :
int main()
{
WSADATA WsaDat;
if (WSAStartup(MAKEWORD(2, 2), &WsaDat) != 0)
{
std::cout << "WSA Initialization failed!\r\n";
WSACleanup();
system("PAUSE");
return 0;
}
SOCKET Socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (Socket == INVALID_SOCKET)
{
std::cout << "Socket creation failed.\r\n";
WSACleanup();
system("PAUSE");
return 0;
}
SOCKADDR_IN serverInf;
serverInf.sin_family = AF_INET;
serverInf.sin_addr.s_addr = INADDR_ANY;
serverInf.sin_port = htons(8888);
if (bind(Socket, (SOCKADDR*)(&serverInf), sizeof(serverInf)) == SOCKET_ERROR)
{
std::cout << "Unable to bind socket!\r\n";
WSACleanup();
system("PAUSE");
return 0;
}
listen(Socket, 1);
SOCKET TempSock = SOCKET_ERROR;
while (TempSock == SOCKET_ERROR)
{
std::cout << "Waiting for incoming connections...\r\n";
TempSock = accept(Socket, NULL, NULL);
}
// If iMode!=0, non-blocking mode is enabled.
u_long iMode = 1;
ioctlsocket(Socket, FIONBIO, &iMode);
Socket = TempSock;
std::cout << "Client connected!\r\n\r\n";
// Main loop
for (;;)
{
char *szMessage = "Welcome to the server!\r\n";
send(Socket, szMessage, strlen(szMessage), 0);
int nError = WSAGetLastError();
if (nError != WSAEWOULDBLOCK&&nError != 0)
{
std::cout << "Winsock error code: " << nError << "\r\n";
std::cout << "Client disconnected!\r\n";
// Shutdown our socket
shutdown(Socket, SD_SEND);
// Close our socket entirely
closesocket(Socket);
break;
}
Sleep(1000);
}
WSACleanup();
system("PAUSE");
return 0;
}
To do so you need one server socket and a clientsocket array like this:
SERVER:
ACCEPT:
int clientsock[2];
minsocks = 0;
numsocks = 2;
while(minsock < numsocks)
{
clientsock[minsock] = accept(serversock,
(struct sockaddr *) &clientaddr,
(socklen_t *)&clientaddrlen);
minsock++;
}
RECIEVE:
char message[6];
int data;
int limit = 6;
for(int i = 0; i < NUMSOCK; i++)
{
int in = recv(clientsock[i], &message[index], limit, 0);
if(in > 0)
{
index += in;
limit -= in;
}
else if ( in == 0 )
printf("Connection closed\n");
else
printf("recv failed: %d\n", WSAGetLastError());
}
This should be a good beginning for you to start with.
Threads - C version
pthread_t sniffer_thread;
if( pthread_create( &sniffer_thread , NULL , connection_handler , (void*) new_sock) < 0)
{
perror("could not create thread");
return 1;
}
//Now join the thread , so that we dont terminate before the thread
//pthread_join( sniffer_thread , NULL);
puts("Handler assigned");
}
/*
* This will handle connection for each client
* */
void *connection_handler(void *socket_desc)
{
//Get the socket descriptor
int sock = *(int*)socket_desc;
int read_size;
char *message , client_message[2000];
while(in != 0)
{
int in = recv(socket_desc, &client_message[index], limit, 0);
if(in > 0)
{
index += in;
limit -= in;
}
else
printf("recv failed: %d\n", WSAGetLastError());
}
//Free the socket pointer
free(socket_desc);
return 0;
}