Continuously sending data from server to receiver using c++ - c++

I am coding in C++ using winsock api. I am making a multi- client server chat. The problem that I am encountering in my code is that my server is able to send the message to the client only once. But I want this to happen multiple times. I cannot put accept() function out of infinite loop in server code. I have used select() for multi client. I am doing it without threading.
Server:
#include <iostream>
#include <WS2tcpip.h>
#include <string>
#include <sstream>
#pragma comment (lib, "ws2_32.lib")
using namespace std;
void main()
{
// Initialze winsock
WSADATA wsData;
WORD ver = MAKEWORD(2, 2);
int wsOk = WSAStartup(ver, &wsData);
if (wsOk != 0)
{
cerr << "Can't Initialize winsock! Quitting" << endl;
return;
}
// Create a socket
SOCKET listening = socket(AF_INET, SOCK_STREAM, 0);
if (listening == INVALID_SOCKET)
{
cerr << "Can't create a socket! Quitting" << endl;
return;
}
// Bind the ip address and port to a socket
sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons(54000);
hint.sin_addr.S_un.S_addr = INADDR_ANY; // Could also use inet_pton ....
bind(listening, (sockaddr*)&hint, sizeof(hint));
// Tell Winsock the socket is for listening
listen(listening, SOMAXCONN);
// Create the master file descriptor set and zero it
fd_set master;
FD_ZERO(&master);
// Add our first socket that we're interested in interacting with; the listening socket!
// It's important that this socket is added for our server or else we won't 'hear' incoming
// connections
FD_SET(listening, &master);
// this will be changed by the \quit command (see below, bonus not in video!)
bool running = true;
while (running)
{
// Make a copy of the master file descriptor set, this is SUPER important because
// the call to select() is _DESTRUCTIVE_. The copy only contains the sockets that
// are accepting inbound connection requests OR messages.
// E.g. You have a server and it's master file descriptor set contains 5 items;
// the listening socket and four clients. When you pass this set into select(),
// only the sockets that are interacting with the server are returned. Let's say
// only one client is sending a message at that time. The contents of 'copy' will
// be one socket. You will have LOST all the other sockets.
// SO MAKE A COPY OF THE MASTER LIST TO PASS INTO select() !!!
fd_set copy = master;
// See who's talking to us
int socketCount = select(0, &copy, nullptr, nullptr, nullptr);
for (int i = 0; i < socketCount; i++) {
//Accept a new connection
SOCKET sock = copy.fd_array[i];
if (sock == listening) {
//Accept a new connection
SOCKET client = accept(listening, nullptr, nullptr);
//Add a new connection
FD_SET(client, &master);
string mssg = "Welcome to the awesome chat server\n";
//Send a welcome message to the connected client
send(client, mssg.c_str(), mssg.size() + 1, 0);
}
//Send a new message
string mssg;
getline(cin, mssg);
int bytes = send(sock, mssg.c_str(), mssg.size() + 1, 0);
for (int i = 0; i < master.fd_count; i++) {
SOCKET outsock = master.fd_array[i];
if (outsock != listening && outsock != sock) {
send(outsock, mssg.c_str(), mssg.size() + 1, 0);
}
}
}
}
// Remove the listening socket from the master file descriptor set and close it
// to prevent anyone else trying to connect.
FD_CLR(listening, &master);
closesocket(listening);
// Message to let users know what's happening.
string msg = "Server is shutting down. Goodbye\r\n";
while (master.fd_count > 0)
{
// Get the socket number
SOCKET sock = master.fd_array[0];
// Send the goodbye message
send(sock, msg.c_str(), msg.size() + 1, 0);
// Remove it from the master file list and close the socket
FD_CLR(sock, &master);
closesocket(sock);
}
// Cleanup winsock
WSACleanup();
system("pause");
}
Client code:
#include<iostream>
#include<ws2tcpip.h>
#include<string>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
void main() {
string ipAddress = "127.0.0.1"; //IP Address of the server
int port = 54000; //Listening port on the sever
//Initialize Winsock
WSADATA data;
WORD ver = MAKEWORD(2, 2);
int wsResult = WSAStartup(ver, &data);
if (wsResult != 0) {
cerr << " Can't initialize winsock " << endl;
return;
}
//Create socket
SOCKET sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock == INVALID_SOCKET) {
cerr << "Can't create a socket " << WSAGetLastError() << endl;
closesocket(sock);
WSACleanup();
return;
}
//Fill in a hint structure
sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons(port);
inet_pton(AF_INET, ipAddress.c_str(), &hint.sin_addr);
//Connect to the server
int connResult = connect(sock, (sockaddr*)&hint, sizeof(hint));
if (connResult == SOCKET_ERROR) {
cerr << " Can't connect to the server " << WSAGetLastError() << endl;
closesocket(sock);
WSACleanup();
return;
}
//Do-While loop to send and receive data
//char b[4096];
//int bytes = recv(sock,b,4096, 0);
//cout << string(b, 0, bytes) << endl;
char buff[4096];
string userInput;
do {
//Prompt the user
//cout << ">";
//getline(cin, userInput);
//Send the result
//int sendResult = send(sock, userInput.c_str(), userInput.size() + 1, 0);
//if (sendResult != SOCKET_ERROR) {
//ZeroMemory(buff, 0);
int bytesrecieved = recv(sock, buff, 4096, 0);
if (bytesrecieved > 0) {
//Echo response to console
cout << "SERVER> " << string(buff, 0, bytesrecieved) << endl;
}
//}
} while (true);
//Shut down everything
closesocket(sock);
WSACleanup();
}

EDIT:
You should do some modifications :
Use timeval for select to avoid the blocking select (wait until a
new connection was made or there is something to read).
Move the read/send message section out of the for loop.
Separate key input processing in an other thread.
Use a safe queue to share the input between the input thread and the communciation one(main thread).
Here is an example:
#include <iostream>
#include <WS2tcpip.h>
#include <string>
#include <sstream>
#include <thread>
#include <mutex>
#include <list>
#pragma comment (lib, "ws2_32.lib")
using namespace std;
class safe_queue {
mutex m;
list<string> str_queue;
public:
safe_queue() {};
void add(const string &s) {
const lock_guard<mutex> lock(m);
str_queue.push_back(s);
}
bool pop( string &s ) {
const lock_guard<mutex> lock(m);
if (!str_queue.empty()) {
s = str_queue.front();
str_queue.pop_front();
return true;
}
return false;
}
};
int main()
{
// Initialze winsock
WSADATA wsData;
WORD ver = MAKEWORD(2, 2);
int wsOk = WSAStartup(ver, &wsData);
if (wsOk != 0)
{
cerr << "Can't Initialize winsock! Quitting" << endl;
return 0;
}
// Create a socket
SOCKET listening = socket(AF_INET, SOCK_STREAM, 0);
if (listening == INVALID_SOCKET)
{
cerr << "Can't create a socket! Quitting" << endl;
return 0;
}
// Bind the ip address and port to a socket
sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons(54000);
hint.sin_addr.S_un.S_addr = INADDR_ANY; // Could also use inet_pton ....
bind(listening, (sockaddr*)&hint, sizeof(hint));
// Tell Winsock the socket is for listening
listen(listening, SOMAXCONN);
// Create the master file descriptor set and zero it
fd_set master;
FD_ZERO(&master);
// Add our first socket that we're interested in interacting with; the listening socket!
// It's important that this socket is added for our server or else we won't 'hear' incoming
// connections
FD_SET(listening, &master);
// this will be changed by the \quit command (see below, bonus not in video!)
bool running = true;
safe_queue sq;
auto io_thread = thread([&] {
string s;
while (running && getline(std::cin, s, '\n')){
sq.add(s);
}
});//thread.
while (running)
{
// Make a copy of the master file descriptor set, this is SUPER important because
// the call to select() is _DESTRUCTIVE_. The copy only contains the sockets that
// are accepting inbound connection requests OR messages.
// E.g. You have a server and it's master file descriptor set contains 5 items;
// the listening socket and four clients. When you pass this set into select(),
// only the sockets that are interacting with the server are returned. Let's say
// only one client is sending a message at that time. The contents of 'copy' will
// be one socket. You will have LOST all the other sockets.
// SO MAKE A COPY OF THE MASTER LIST TO PASS INTO select() !!!
fd_set copy = master;
timeval tv = {0,0};
// See who's talking to us
int socketCount = select(0, &copy, nullptr, nullptr, &tv);
for (int i = 0; i < socketCount; i++) {
//Accept a new connection
SOCKET sock = copy.fd_array[i];
if (sock == listening) {
//Accept a new connection
SOCKET client = accept(listening, nullptr, nullptr);
//Add a new connection
FD_SET(client, &master);
string mssg = "Welcome to the awesome chat server\n";
//Send a welcome message to the connected client
send(client, mssg.c_str(), mssg.size() + 1, 0);
}
}//for.
string mssg;
if (sq.pop(mssg) ) {
std::cout << "Send :" << mssg << endl;
for (u_int i = 0; i < master.fd_count; i++) {
SOCKET outsock = master.fd_array[i];
if (outsock != listening) {
send(outsock, mssg.c_str(), mssg.size() + 1, 0);
}
}
}
}//while
// Remove the listening socket from the master file descriptor set and close it
// to prevent anyone else trying to connect.
FD_CLR(listening, &master);
closesocket(listening);
// Message to let users know what's happening.
string msg = "Server is shutting down. Goodbye\r\n";
while (master.fd_count > 0)
{
// Get the socket number
SOCKET sock = master.fd_array[0];
// Send the goodbye message
send(sock, msg.c_str(), msg.size() + 1, 0);
// Remove it from the master file list and close the socket
FD_CLR(sock, &master);
closesocket(sock);
}
// Cleanup winsock
WSACleanup();
system("pause");
return 0;
}

Related

UDP signal from Matlab to C++

I want to send random trigger signals (A and B) from Matlab to a C++ Code. The point where I stuck now is, that whenever I am not sending this trigger signal/message, the C++ Code keeps waiting for it and doesn't continue its process.
How can I make the C++ Code keep running (to collect data) without waiting for the next trigger message. Because now only once it receives the message (UDP transfers trigger signal) it gives me the specific outcome.
----------- BEGIN MATLAB CODE ---------------------
send_trigger_signal = instrfind('Type', 'udp', 'LocalHost', '127.0.0.1','RemoteHost', '192.168.0.100', 'RemotePort', 8888, 'LocalPort', 8844, 'Tag', '');
% Create the udp object if it does not exist otherwise use the object that was found.
if isempty(send_trigger_signal)
send_trigger_signal = udp('127.0.0.1', 'RemotePort', 8888, 'LocalPort', 8844);
else
fclose(send_trigger_signal);
send_trigger_signal = send_trigger_signal(1);
end
send_trigger_signal.DatagramTerminateMode='off';
send_trigger_signal.Timeout=0.0001;
send_trigger_signal.Timerperiod=0.01;
%send_trigger_signal.
% Connect to instrument object, send_trigger_signal.
fopen(send_trigger_signal);
% Communicating with instrument object, send_trigger_signal.
on_trigger_command=typecast(swapbytes(uint16([1 1 0 0])),'uint8'); %trigger on
off_trigger_command=typecast(swapbytes(uint16([0 0 0 0])),'uint8'); %trigger off
while(true)
for i=1:1
fprintf(send_trigger_signal, 'A');
WaitSecs(5);
end
end
fclose(send_trigger_signal);
send_trigger_signal=instrfindall;
delete(send_trigger_signal);
instrfindall;
----------- END MATLAB CODE ---------------------
This is the C++ code which should receive the random trigger signals from Matlab (A and B), while collecting gyro data between those signals.
To test it here the message is send every 5sec. The problem is that I cannot collect the gyro data in within those 5sec. The UDP communication is interrupting the data collection - because it is waiting those 5sec.
----------- START C++ CODE ---------------------
#include <iostream>
#include <winsock2.h>
using namespace std;
#pragma comment(lib,"ws2_32.lib") // Winsock Library
#pragma warning(disable:4996)
#define BUFLEN 512
#define PORT 8888
int receiver(void)
{
int value = 5;
system("title UDP Server");
sockaddr_in server, client;
// initialise winsock
WSADATA wsa;
printf("Initialising Winsock...");
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
{
printf("Failed. Error Code: %d", WSAGetLastError());
exit(0);
}
printf("Initialised.\n");
// create a socket
SOCKET server_socket;
if ((server_socket = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET)
{
printf("Could not create socket: %d", WSAGetLastError());
}
printf("Socket created.\n");
// prepare the sockaddr_in structure
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(PORT);
// bind
if (bind(server_socket, (sockaddr*)&server, sizeof(server)) == SOCKET_ERROR)
{
printf("Bind failed with error code: %d", WSAGetLastError());
exit(EXIT_FAILURE);
}
puts("Bind done.");
while (true)
{
printf("Waiting for data...");
fflush(stdout);
char message[BUFLEN] = {};
// try to receive some data, this is a blocking call
int message_len;
int slen = sizeof(sockaddr_in);
if (message_len = recvfrom(server_socket, message, BUFLEN, 0, (sockaddr*)&client, &slen) == SOCKET_ERROR)
{
printf(message);
printf("recvfrom() failed with error code: %d", WSAGetLastError());
exit(0);
}
if (message[0] == 'A')
{
value = 6;
break;
}
if (message[0] == 'B')
{
value = 7;
break;
}
// print details of the client/peer and the data received
printf("Received packet from %s:%d\n", inet_ntoa(client.sin_addr), ntohs(client.sin_port));
printf("Data: %s\n", message);
return 0;
}
closesocket(server_socket);
WSACleanup();
return value;
}
int main()
{
while (true)
{
// Reading some gyro data here
// Listening UDP
receiver();
}
return 0;
}
----------- END C++ CODE ---------------------
With a few structural tweaks:
Using non-blocking socket.
You don't want to restart winsock and rebind the socket every time you read from it, so that's spun off to different functions (an RAII wrapper class in the case of winsock).
C-style IO replaced with C++ IO.
exit(0) means the program succeeded, but was used in many cases where failure occurred. Consistently using exit(EXIT_FAILURE);. Might be worth throwing an exception, but it's annoying to get the error code into the exception text.
Removed some of the output because it would be spammed out now that the receive function can immediately return .
Your program could look something like this:
#include <iostream>
#include <winsock2.h>
using namespace std;
#pragma comment(lib,"ws2_32.lib") // Winsock Library
#pragma warning(disable:4996)
// using modern C++ constants
constexpr int BUFLEN = 512;
constexpr int PORT = 8888;
//RAII wrapper to make sure winsock is created and disposed of responsibly
struct winsock_RAII
{
winsock_RAII()
{
WSADATA wsa;
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0)
{
std::cerr << "Failed to initialize winsock. Error Code: " << WSAGetLastError() << '\n';
exit(EXIT_FAILURE);
}
}
~winsock_RAII()
{
WSACleanup(); // what are we gonna do if it fails? Not much we can do.
}
};
//socket initialization
SOCKET init_sock()
{
SOCKET server_socket;
if ((server_socket = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET)
{
std::cerr << "Failed to get socket. Error Code: " << WSAGetLastError() << '\n';
exit(EXIT_FAILURE);
}
u_long iMode = 1;
//setr socket non-blocking
if (ioctlsocket(server_socket, FIONBIO, &iMode) != NO_ERROR)
{
std::cerr << "Failed to get socket. Error Code: " << WSAGetLastError() << '\n';
exit(EXIT_FAILURE);
}
// prepare the sockaddr_in structure
sockaddr_in server;
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons(PORT);
// bind
if (bind(server_socket, (sockaddr*) &server, sizeof(server)) == SOCKET_ERROR)
{
std::cerr << "Bind failed. Error Code: " << WSAGetLastError() << '\n';
exit(EXIT_FAILURE);
}
return server_socket;
}
// read from socket
int receiver(SOCKET server_socket)
{
// try to receive some data, this is a non-blocking call
int slen = sizeof(sockaddr_in);
sockaddr_in client;
char message[BUFLEN + 1]; // no need to clear the whole buffer. We'll know
// exactly where to put the null thanks to message_len
// +1 makes sure we have room for terminator
int message_len = recvfrom(server_socket, message,
BUFLEN,
0,
(sockaddr*) &client,
&slen);
int value = 5;
if (message_len != SOCKET_ERROR)
{
message[message_len] = '\0'; // place terrminator
if (message[0] == 'A')
{
value = 6;
}
if (message[0] == 'B')
{
value = 7;
}
// print details of the client/peer and the data received
std::cout << "Received packet from " << inet_ntoa(client.sin_addr) << ':' << ntohs(client.sin_port) << '\n'
<< "Data: " << message << '\n';
}
else if (WSAGetLastError() != WSAEWOULDBLOCK)
{
// printf(message); no point to printing message. There isn't one
std::cerr << "recvfrom() failed . Error Code: " << WSAGetLastError() << '\n';
exit(EXIT_FAILURE);
}
return value;
}
int main()
{
winsock_RAII winsock; // scoped winsock initializer
SOCKET server_socket = init_sock();
while (true)
{
// Reading some gyro data here
receiver(server_socket);
}
closesocket(server_socket);
return 0;
}
You might want to use select with a short timeout to throttle the loop because it can be a serious and unnecessary CPU-eater if the gyro reading code is also quick.

Why socket fails in bind after having a listenfd with same port?

Simple code first:
#include "NetFunc.h"
#include <iostream>
#include <string>
#include <thread>
#include <chrono>
using namespace std;
void test()
{
// SOCKET listenfd = OpenListenFD("127.0.0.1", "19287");
SOCKET connfd = OpenClientFD("127.0.0.1", "12389", "127.0.0.1", "19287");
send(connfd, "test", 5, 0);
close(connfd);
using namespace std::chrono_literals;
this_thread::sleep_for(100ms);
connfd = OpenClientFD("127.0.0.1", "12389", "127.0.0.1", "19287");
send(connfd, "test2", 6, 0);
close(connfd);
// close(listenfd);
cout << "over" << endl;
return;
}
int main()
{
SOCKET listenfd = OpenListenFD("127.0.0.1", "12389");
sockaddr_storage clientaddr;
socklen_t clientlen = sizeof clientaddr;
// SOCKET connfd = 0;
const int maxNameLen = 0x800;
char* buffer = new char[0x800 + 1];
char* hostname = new char[maxNameLen + 1], * port = new char[maxNameLen + 1];
std::thread newThread{&test};
newThread.detach();
SOCKET connfd;
while (true)
{
if ((connfd = accept(listenfd, (sockaddr*)&clientaddr, &clientlen)) < 0)
{
std::cerr << "Not accept correctly at InteractWithClients" << std::endl;
}
else{
getnameinfo((sockaddr*)&clientaddr, clientlen, hostname, maxNameLen, port, maxNameLen, NI_NUMERICHOST);
std::string completeAddr = string(hostname) + ":" + string(port);
std::cout << "Addr : " << completeAddr << std::endl;
recv(connfd, buffer, 0x800, 0);
std::cout << buffer << std::endl;
}
close(connfd);
}
delete[] buffer; delete[] hostname; delete[]port;
return 0;
}
Here, the NetFunc.h has necessary headers and #define SOCKET int. It corresponds with NetFunc.cpp, which has two functions for server&client as below:
(Note that I'm trying to generate a client with a specific port, for I just need two address-port to connect rather than to distinguish a server from a client. Besides, I already use SO_REUSEADDR.)
#include "NetFunc.h"
#include <iostream>
#include <string.h>
SOCKET OpenListenFD(const char* addr, const char* port)
{
struct addrinfo hints, * listp, * p;
SOCKET listenfd;
int optval = 1;
/* Get a list of potential server addresses */
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_socktype = SOCK_STREAM; /* Accept connections */
hints.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; /* ... on any IP address */
hints.ai_flags |= AI_NUMERICSERV; /* ... using port number */
hints.ai_family = AF_INET;
getaddrinfo(addr, port, &hints, &listp);
/* Walk the list for one that we can bind to */
for (p = listp; p; p = p->ai_next) {
/* Create a socket descriptor */
if ((listenfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0)
continue; /* Socket failed, try the next */
/* Eliminates "Address already in use" error from bind */
setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, //line:netp:csapp:setsockopt
(setsockopt_ptr)&optval, sizeof(int));
/* Bind the descriptor to the address */
if (bind(listenfd, p->ai_addr, p->ai_addrlen) == 0)
{
break; /* Success */
}
closesocket(listenfd); /* Bind failed, try the next */
}
/* Clean up */
freeaddrinfo(listp);
if (!p) /* No address worked */
return -1;
/* Make it a listening socket ready to accept connection requests */
if (listen(listenfd, LISTENQ) < 0) {
closesocket(listenfd);
return -1;
}
return listenfd;
}
SOCKET OpenClientFD(const char* serverAddr, const char* port, const char* localAddr, const char* localPort)
{
struct sockaddr_in my_addr, my_addr1;
SOCKET client = socket(AF_INET, SOCK_STREAM, 0);
if (client < 0)
std::cerr<<"Error in client creating\n";
else
std::cout << "Client Created\n";
int optval = 1;
setsockopt(client, SOL_SOCKET, SO_REUSEADDR, //line:netp:csapp:setsockopt
(setsockopt_ptr)&optval, sizeof(int));
my_addr.sin_family = AF_INET;
my_addr.sin_addr.s_addr = INADDR_ANY;
my_addr.sin_port = htons(atoi(port));
// This ip address will change according to the machine
inet_pton(AF_INET, serverAddr, &my_addr.sin_addr.s_addr);
// Explicitly assigning port number 12010 by
// binding client with that port
my_addr1.sin_family = AF_INET;
my_addr1.sin_addr.s_addr = INADDR_ANY;
my_addr1.sin_port = htons(atoi(localPort));
// This ip address will change according to the machine
inet_pton(AF_INET, localAddr, &my_addr1.sin_addr.s_addr);
if (bind(client, (struct sockaddr*)&my_addr1, sizeof(struct sockaddr_in)) == 0)
std::cout << "Binded Correctly\n";
else
{
std::cerr << "Unable to bind\n";
return -1;
}
socklen_t addr_size = sizeof my_addr;
int con = 0;
do { con = connect(client, (struct sockaddr*)&my_addr, sizeof my_addr); } while (con != 0);
if (con == 0)
std::cout << "Client Connected\n";
else
std::cerr << "Error in Connection\n";
return client;
}
My OS is Ubuntu20.04 and such code will work with normal output. However, when I remove comments in test() for listenfd, it will output Unable to bind infinitely.
I'm really a newbie in socket and network. I have no idea why that will happen. Also, is there a way for an address-port to be both server and client at the same time? (Maybe to be exact, can an address-port both connect actively & accept others' connection passively?) Thank you!
my_addr1.sin_family = AF_INET;
my_addr1.sin_addr.s_addr = INADDR_ANY;
my_addr1.sin_port = htons(atoi(localPort));
// This ip address will change according to the machine
inet_pton(AF_INET, localAddr, &my_addr1.sin_addr.s_addr);
if (bind(client, (struct sockaddr*)&my_addr1, sizeof(struct sockaddr_in)) == 0)
std::cout << "Binded Correctly\n";
This code binds a listening socket to port 19287, localPort is 19287.
// SOCKET listenfd = OpenListenFD("127.0.0.1", "19287");
And so does the commented-out code. Un-commenting it out results in two sockets attempting to listen on the same port. Surprise, surprise: this doesn't work.
The fact that the first socket that binds this port uses SO_REUSEADDR is irrelevant, and makes no difference. With or without SO_REUSEADDR, no two sockets can listen on the same port.
SO_REUSEADDR does not allow you to have multiple sockets listening on the same port, at the same time. All that SO_REUSEADDR does is allow a listening socket to be bound on a port that's temporarily "take out of commission", in specific edge cases, until a prescribed timeout expires. It allows a port to be bound as long as nothing else is listening on it, even if it's sitting in a "timeout room".
But it still won't allow you to have two different sockets listening on the same port, at the same time.

select() not getting more than 1 action

I made a server socket in C++ for Unix (TCP), that accepted only one client socket. Today, I attempted to make it accept multiple ones. For some reason, it ends up only accepting 1, and it's not receiving messages sent by the client that IS able to connect. I believe that the select function isn't working properly. FYI: I'm new to socket programming, so please understand if it's a stupid mistake.
Here's the code:
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <cstring>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/select.h>
#define PORT 8080
#define MAXCLIENTS 30
int main()
{
//create a socket
int listening = socket(AF_INET, SOCK_STREAM, 0); //(returns int) - makes socket (returns what socket it is in terms of an int)
if(listening < 0) //check if we were able to make a socket!
{
std::cerr << "Can't create socket!" << std::endl;
return -1;
}
//bind the socket to an IP/Port
sockaddr_in hint{}; // This creates a structure for the ipv4 info of the socket.
hint.sin_family = AF_INET; //set the family to ipv4
hint.sin_port = htons(PORT); //set the port to the macro set above (use host-to-network-short to conver the int to the port)
hint.sin_addr.s_addr = INADDR_ANY; //set the ip to any address
if(bind(listening, (sockaddr*)&hint, sizeof(hint)) < 0) //attempt to bind socket (inticated by what number the `listening` socket is) to ip/port
{
std::cerr << "Can't bind to IP/Port" << std::endl;
return -1;
}
//mark the socket for listening
if(listen(listening, MAXCLIENTS) < 0 /*SOMAXCONN = maximum amount of connections, defined by sys/socket.h*/) //attempt to listen on the socket number indicated by `listening`
{
std::cerr << "Can't listen on the socket!" << std::endl;
return -1;
}
//FD_CLR() = Remove 1 from set
//FD_SET() = Add to set
//FD_ZERO() = Remove everything from set
//FD_ISSET() = Check if something is part of a set
fd_set master; //define the set
int max_sd;
int client_socks[MAXCLIENTS]{};
while (true)
{
FD_ZERO(&master); //make sure it's cleared
FD_SET(listening, &master); //add the listening socket (server) to the set
max_sd = listening; //max socket descriptor set to the listening socket (need this for the select func)
for (int i = 0; i < MAXCLIENTS; i++) {
if (client_socks[i] > 0) //make sure the particular socket exists
{
FD_SET(client_socks[i], &master); //add it to the set
}
if (client_socks[i] > max_sd) //if the socket is greater than our current maximum socket descriptor
{
max_sd = client_socks[i];
}
}
//wait for some action on any socket within the master fd (this will set the master fd_set to be equal to whatever socket had some action on it)
int activity = select(max_sd + 1, &master, nullptr, nullptr, nullptr);
if (activity < 0) //error!
{
std::cerr << "Error while trying to select!" << std::endl;
}
int addrlen = sizeof(hint);
if (FD_ISSET(listening, &master)) //if the select got that there was action on the listening (server) socket - most likely, a client socket is trying to connect!
{
int client_socket;
client_socket = accept(listening, (sockaddr *) &hint, &addrlen); //accept the first client "waiting to get in"
if (client_socket < 0) {
std::cerr << "Something went wrong when trying to accept a client socket!" << std::endl;
break;
}
std::cout << "New connection: " << inet_ntoa(hint.sin_addr) << " on port " << ntohs(hint.sin_port) << std::endl;
if (send(client_socket, "Welcome to the socket party!", strlen("Welcome to the socket party!"), 0) != strlen("Welcome to the socket party!")) { //greetings!
std::cerr << "Error when sending welcome message." << std::endl; //something went wrong ;(
}
for (int i = 0; i < MAXCLIENTS; i++) //for each index, set client_sock to the address of the index of client_socks, so that we can set the value of it!
{
if(client_socks[i] == 0) //if this position is null (0)
{
client_socks[i] = client_socket;
}
}
}
//else, there was action on a client socket (most likely a message is being sent!
char buffer[2048]; //we need somewhere to store clients messages!
for (int &client_sock : client_socks) //loop through the client sockets
{
if (client_sock != 0)
{
if (FD_ISSET(client_sock, &master) == 0) //check if the select got action on the particular index in the client_socks array
{
memset(&buffer, 0, sizeof(buffer)); //make sure the buffer is clear!
if (read(client_sock, &buffer, 2048) == 0) //check if nothing was recieved from the client
{
getpeername(client_sock, (sockaddr*)&hint, (socklen_t*)&addrlen) < 0; //gets networking info, based off of which socket is passed (the if statement checks for errors). In addition, it sets the values of hint to the info from the passed socket
//print that the client disconnected
std::cout << "A client has disconnected! IP: " << inet_ntoa(hint.sin_addr) << " Port: " << ntohs(hint.sin_port) << std::endl;
close(client_sock); //close the socket
client_sock = 0; //set its value in the array to 0, so that we can reuse it!
}
//else, we got message from the client
for (int &socket : client_socks)
//if (socket != client_sock)
send(socket , buffer , strlen(buffer) , 0 );
}
}
}
return 0;
}
}
Your client_socks variable must be initialised, as Igor said - just append braces: int client_socks[MAXCLIENTS]{};.
Then your for (int &client_sock : client_socks) loop doesn't ignore 0 values (which you obviously want to be a sentinel given "if(client_socks[i] == 0) //if this position is null (0)"). Inside that loop, add "if (client_sock[i] == 0) continue;. Because you try to read` from descriptor 0, it will block waiting for keyboard input.
Further, if you wanted your code to be robust, you would make the listening socket non-blocking, as it's possible for that socket to select readable, but by the time your application goes to accept from it, the client connection attempt has already been dropped: then you'd block waiting to accept another client connection attempt that might never come, and not be servicing existing clients.

Winsock local client-server benchmarking

I am tying to evaluate the performance of this simple client-server program setting it up in a way where a test client makes a fixed number of requests, say; 10000 and then see how long it takes the server to deal with these and then trying it with multiple clients in parallel since it runs with threads. Now I am wondering how to program this? (sorry just started with Winsock). I was also wondering if this is a proper implementation of threading and if not what could be improved and why. Thanks in advance
Test server code;
Server:
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#pragma comment(lib,"ws2_32.lib") //header file
#include <WinSock2.h>
#include <string>
#include <iostream>
#include <iomanip>
using namespace std;
SOCKET Connections[100];
int ConnectionCounter = 0;
void ClientHandlerThread(int index) //index = the index in the SOCKET Connections array
{
char buffer[256]; //Buffer to receive and send out messages from/to the clients
while (true)
{
recv(Connections[index], buffer, sizeof(buffer), NULL); //get message from client
for (int i = 0; i < ConnectionCounter; i++) //For each client connection
{
if (i == index) //Don't send the chat message to the same user who sent it
continue; //Skip user
send(Connections[i], buffer, sizeof(buffer), NULL);//send the chat message to this client
}
}
}
int main()
{
std::cout << "\n\n\n\n\n Server sucessfully turned on, awaiting for clients...\n\n\n\n\n";
//Winsock Startup
WSAData wsaData;
WORD DllVersion = MAKEWORD(2, 1);
if (WSAStartup(DllVersion, &wsaData) != 0) //initialise winsock library, if WSAStartup returns anything other than 0, then that means an error has occured in the WinSock Startup.
{
MessageBoxA(NULL, "WinSock startup failed", "Error", MB_OK | MB_ICONERROR);
return 0;
}
SOCKADDR_IN addr; //Address that we will bind our listening socket to
int addrlen = sizeof(addr); //length of the address (required for accept call)
addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //Broadcast locally, using inet_address funtion that converts to correct long format.
addr.sin_port = htons(1111); //Port
addr.sin_family = AF_INET; //IPv4 Socket
SOCKET sListen = socket(AF_INET, SOCK_STREAM, NULL); //Create socket to listen for new connections
bind(sListen, (SOCKADDR*)&addr, sizeof(addr)); //Bind the address to the socket
listen(sListen, SOMAXCONN); //Places sListen socket in a state in which it is listening for an incoming connection. Note:SOMAXCONN = Socket Oustanding Max Connections
SOCKET newConnection; //Socket to hold the client's connection
int ConnectionCounter = 0; //# of client connections
for (int i = 0; i < 100; i++)
{
newConnection = accept(sListen, (SOCKADDR*)&addr, &addrlen); //Accept a new connection
if (newConnection == 0) //If accepting the client connection failed
{
std::cout << "Failed to accept the client's connection." << std::endl;
}
else //If client connection properly accepted
{
std::cout << "\n\n\nClient Connected!\n\n" << std::endl;
/* char MOTD[256] = "Welcome! This is the Message of the Day."; //Create buffer with message of the day
send(newConnection, MOTD, sizeof(MOTD), NULL); //Send MOTD buffer */
Connections[i] = newConnection; //Set socket in array to be the newest connection before creating the thread to handle this client's socket.
ConnectionCounter += 1; //Incremenent total # of clients that have connected
cout << "\nConnected Clients: ";
cout << ConnectionCounter;
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)ClientHandlerThread, (LPVOID)(i), NULL, NULL); //Create Thread to handle this client. The index in the socket array for this thread is the value (i).
}
}
system("pause");
return 0;
}
Client:
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#pragma comment(lib,"ws2_32.lib") //Required for WinSock
#include <WinSock2.h> //For win sockets
#include <string> //For std::string
#include <iostream> //For std::cout, std::endl, std::cin.getline
SOCKET Connection;//This client's connection to the server
void ClientThread()
{
char buffer[256]; //Create buffer to hold messages up to 256 characters
while (true)
{
recv(Connection, buffer, sizeof(buffer), NULL); //receive buffer
std::cout << buffer << std::endl; //print out buffer
}
}
int main()
{
//Winsock Startup
WSAData wsaData;
WORD DllVersion = MAKEWORD(2, 1);
if (WSAStartup(DllVersion, &wsaData) != 0)
{
MessageBoxA(NULL, "Winsock startup failed", "Error", MB_OK | MB_ICONERROR);
return 0;
}
SOCKADDR_IN addr; //Address to be binded to our Connection socket
int sizeofaddr = sizeof(addr); //Need sizeofaddr for the connect function
addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //Address = localhost (this pc)
addr.sin_port = htons(1111); //Port = 1111
addr.sin_family = AF_INET; //IPv4 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;
CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)ClientThread, NULL, NULL, NULL); //Create the client thread that will receive any data that the server sends.
char buffer[256]; //256 char buffer to send message
while (true)
{
std::cin.getline(buffer, sizeof(buffer)); //Get line if user presses enter and fill the buffer
send(Connection, buffer, sizeof(buffer), NULL); //Send buffer
Sleep(10);
}
return 0;
}
As #JerryCoffin said, using thread-per-client model burden the server by too many context switches between threads. Modern servers like Nginx web server use an asynchronous non-blocking model to reach scalability. I have developed such an architecture recently. To get more familiar with this model, I provide some useful links for you.
Start with the following to get more familiar with the concept:
Inside NGINX: How We Designed for Performance & Scale
You need to know re-actor and pro-actor design patterns. You can implement them using select() or similar facilities.
Example: Nonblocking I/O and select()
There are also some nice implementations of them in libraries like boost, POCO, and ACE. Personally, I recommend you take a look at the boost asio library. You can find full documentation, tutorial, and examples here.
Boost.Asio

Client Not Acting Correctly

I've made two C++ files, one for a server and then a client. As you can see below in the code, I wanted to display to the client that they have been connected, and their ID as well, but when I try to connect, it clears the console correctly, but doesn't display their ID. I noticed that when I close the server and the client is still running, the client then displays the ID. Not too sure what the problem is, will be awaiting your replies! Thanks in advance, and here's the code.
Server:
// First, we'll include all the required files
#include <winsock.h>
#include <iostream>
#include <Windows.h>
using namespace std;
#pragma comment(lib, "ws2_32.lib") // Require this lib for winsock
SOCKADDR_IN addr; // This structure saves the address and ports of the server
int addrlen = sizeof(addr); // This saves the length of the address
int Counter; // Counts how many connected clients there are
SOCKET sConnect; // Socket for incoming connections
SOCKET sListen; // Socket for listening
SOCKET *Connections; // Socket for all the connections
// Init the winsock library
int InitWinSock()
{
int Val = 0; // Make a default
WSAData wsaData;
WORD DllVersion = MAKEWORD(2, 1);
Val = WSAStartup(DllVersion, &wsaData); // Initialise winsock
return 0;
}
int main()
{
system("color 0a"); // Change the console color to black-green
cout << "Server Started." << endl;
// Winsock Init
int Val = InitWinSock();
if(Val != 0)
{
// If WinSock Init fails, display an error
MessageBoxA(NULL, "Error while starting WinSock!", "Error", MB_OK | MB_ICONERROR);
exit(1); // Stop the procedure
}
Connections = (SOCKET*) calloc(64, sizeof(SOCKET));
// Init the sockets
sListen = socket(AF_INET, SOCK_STREAM, NULL);
sConnect = socket(AF_INET, SOCK_STREAM, NULL);
addr.sin_addr.s_addr = inet_addr("127.0.0.1"); // Server address, 127.0.0.1 is localhost
addr.sin_port = htons(2222); // Server port
addr.sin_family = AF_INET; // This is the type of connection
bind(sListen, (SOCKADDR*)&addr, sizeof(addr)); // Bind server to address and port
listen(sListen, 64); // Listen for any incoming connections
while(true)
{
if(sConnect = accept(sListen, (SOCKADDR*)&addr, &addrlen))
{
Connections[Counter] = sConnect;
char *Name = new char[64]; // The name of the client
ZeroMemory(Name, 64); // We make the char empty
sprintf(Name, "%i", Counter);
send(Connections[Counter], Name, 64, NULL); // We send the ID to the client
cout << "New Connection!" << endl;
Counter ++; // Increase the amount of clients
} // end if accept the connection
Sleep(50); // Wait 50 milliseconds
} // end while search for connections
}
Client:
#include <iostream>
#include <winsock.h>
#include <Windows.h>
#pragma comment(lib, "ws2_32.lib")
using namespace std;
SOCKET sConnect; // The connection socket
SOCKADDR_IN addr; // The server adress
int Startup_WinSock()
{
WSADATA wsaData;
WORD DllVersion = MAKEWORD(2, 1);
int Val = WSAStartup(DllVersion, &wsaData);
return Val;
}
int main()
{
system("color 0a");
int Val = Startup_WinSock();
if(Val != 0)
{
cout << "Can't Startup WinSock!" << endl; // Display error
exit(1);
}
sConnect = socket(AF_INET, SOCK_STREAM, NULL);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_port = htons(2222);
addr.sin_family = AF_INET;
cout << "Please press [ENTER]" << endl;
cin.get();
Val = connect(sConnect, (SOCKADDR*)&addr, sizeof(addr)); // Connect with the server
if(Val != 0)
{
cout << "Can't reach the server!" << endl;
main (); // Try again
}
else
{
system("cls"); // Clear the screen
int ID;
char *nID = new char[64]; // Client's ID
char *hello = new char[64]; // Message from the server
ZeroMemory(nID, 64);
ZeroMemory(hello, 64);
recv(sConnect, nID, 64, NULL); // Receive ID from server
recv(sConnect, hello, 64, NULL); // Receive message from the server
ID = atoi(nID); // Cast to an int
cout << hello << endl;
cout << "Your ID: " << ID << endl;
cin.get();
}
return 0;
}
recv(sConnect, nID, 64, NULL); // Receive ID from server
recv(sConnect, hello, 64, NULL); // Receive message from the server
First, you have no error checking here. You need to add error checking throughout the program or it will be impossible to troubleshoot.
Second, you have no message handling here. What happens if the first recv gets 3 bytes? You'll wind up reading the rest of the ID into the hello field.
Third, you don't send any messages. So the second recv will wait until the read attempt fails, which is when the server is terminated.
In the server you only send the id, but nothing more, meaning the client will try to receive something which haven't been sent and will block forever until it receives anything.
Oh, and you have a memory leak in the server, you allocate memory for the name (which you only clear, but doesn't actually set to anything) but you never free the memory anywhere. No need for dynamic allocation here anyway.