thread-safety issues managing connections objects inside a TCP server - c++

I'm coding a simple client-server application using TCP. I'm using Winsock, but I'm limiting myself to the BSD socket API (since I want the code to run on Linux as well).
The application should run a server and accept incoming client connections, receive and send messages, and gracefully handle client disconnections. The approach I use is this:
(disclaimer: I know it might not be the best one, and that there are a bunch of more efficient techniques, but I'm just starting with network programming)
A connection object (that represents the actual endpoint of the connection and contains a socket object) has a queue of outgoing messages, and a reference to a queue of incoming messages (actual incoming messages queues are inside the clients and the server object, and are passed on construction):
#ifndef CONNECTION_H
#define CONNECTION_H
#include <memory>
#include <thread>
#include "ThreadsafeQueue.h"
#include "Message.h"
#include "debug.h"
template <typename T>
class Connection : public std::enable_shared_from_this<Connection<T>>
{
public:
enum class Owner { CLIENT, SERVER };
private:
using std::enable_shared_from_this<Connection>::shared_from_this;
public:
Connection(Owner owner, uint32_t id, SOCKET socket, ThreadsafeQueue<OwnedMessage<T>> &inMessageQueue);
~Connection();
void Send(const Message<T> &message);
void Close();
bool IsOpen() const { return mIsOpen; }
uint32_t GetId() const { return mId; }
private:
const Owner mOwner;
uint32_t mId;
SOCKET mSocket;
bool mIsOpen;
ThreadsafeQueue<Message<T>> mOutMessageQueue;
ThreadsafeQueue<OwnedMessage<T>> &mInMessageQueue;
std::thread mSendThread;
std::thread mReceiveThread;
void Send_();
void Receive_();
};
template <typename T>
Connection<T>::Connection(Owner owner, uint32_t id, SOCKET socket, ThreadsafeQueue<OwnedMessage<T>> &inMessageQueue) : mOwner(owner), mId(id), mSocket(socket), mInMessageQueue(inMessageQueue)
{
unsigned long socketMode = 1U;
if (ioctlsocket(socket, FIONBIO, &socketMode) != 0) // set non blocking socket
Error("error setting socket i/o mode");
mIsOpen = true;
mSendThread = std::thread(&Connection<T>::Send_, this);
mReceiveThread = std::thread(&Connection<T>::Receive_, this);
}
template <typename T>
Connection<T>::~Connection()
{
Close();
}
template <typename T>
void Connection<T>::Send(const Message<T> &message)
{
mOutMessageQueue.EnQueue(message);
}
template <typename T>
void Connection<T>::Close()
{
if (mIsOpen)
mIsOpen = false;
if (mSendThread.joinable())
mSendThread.join();
if (mReceiveThread.joinable())
mReceiveThread.join();
closesocket(mSocket);
}
template <typename T>
void Connection<T>::Send_()
{
//DbgPrint("send thread start");
while (mIsOpen)
{
// get from outgoing queue and send
if (!mOutMessageQueue.Empty())
{
Message<T> outMessage = mOutMessageQueue.Front();
mOutMessageQueue.DeQueue();
int sendFlags = 0;
int totalBytesSent = 0;
while (totalBytesSent < sizeof(Message<T>::Header))
{
int bytesSent = send(mSocket, reinterpret_cast<char*>(&outMessage.mHeader) + totalBytesSent, sizeof(Message<T>::Header) - totalBytesSent, sendFlags);
totalBytesSent += bytesSent;
if (bytesSent == SOCKET_ERROR)
Error("error sending message");
}
totalBytesSent = 0;
while (totalBytesSent < outMessage.mBody.Size())
{
int bytesSent = send(mSocket, reinterpret_cast<char*>(outMessage.mBody.Data()) + totalBytesSent, outMessage.mBody.Size() - totalBytesSent, sendFlags);
totalBytesSent += bytesSent;
if (bytesSent == SOCKET_ERROR)
Error("error sending message");
}
}
}
//DbgPrint("send thread end");
}
template <typename T>
void Connection<T>::Receive_()
{
//DbgPrint("receive thread start");
static bool skip = false; // used in non blocking mode
while (mIsOpen)
{
skip = false;
Message<T> inMessage;
int recvFlags = 0;
int totalBytesReceived = 0;
while (totalBytesReceived < sizeof(Message<T>::Header)) // receive message header
{
int bytesReceived = recv(mSocket, reinterpret_cast<char*>(&inMessage.mHeader) + totalBytesReceived, sizeof(Message<T>::Header) - totalBytesReceived, recvFlags);
if (bytesReceived == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK)
{
skip = true;
break;
}
if (bytesReceived == SOCKET_ERROR)
Error("error receiving message");
else if (bytesReceived == 0) // other side closed connection
{
mIsOpen = false;
skip = true;
break;
}
totalBytesReceived += bytesReceived;
}
if (skip)
continue;
inMessage.mBody.Resize(inMessage.mHeader.mSize);
totalBytesReceived = 0;
while (totalBytesReceived < inMessage.mBody.Size()) // receive message body
{
int bytesReceived = recv(mSocket, reinterpret_cast<char*>(inMessage.mBody.Data()) + totalBytesReceived, inMessage.mBody.Size() - totalBytesReceived, recvFlags);
if (bytesReceived == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK)
continue;
totalBytesReceived += bytesReceived;
if (bytesReceived == SOCKET_ERROR)
Error("error receiving message");
else if (bytesReceived == 0) // other side closed connection
{
mIsOpen = false;
skip = true;
break;
}
}
if (skip)
continue;
if (mOwner == Owner::SERVER)
mInMessageQueue.EnQueue(OwnedMessage<T>(shared_from_this(), inMessage)); // put received message into incoming queue
else
mInMessageQueue.EnQueue(OwnedMessage<T>(nullptr, inMessage));
}
//DbgPrint("receive thread start");
}
#endif // CONNECTION_H
A client has one Connection objects, the server has many of them in a container. Server runs and accept clients, spawns a Connection and put it into a container. Each connection runs in two threads (one to send and the other to receive, since my sockets are non-blocking it is redundant, I'll address the issue in the future). The connected client stores its own connection as well. The Send_ () and Receive_() threads take care of transferring messages in and out of the message queues (which are brutally thread-safe by synchronizing every operation on a std::mutex, not the best design I know).
The server runs two threads: one to keep listening to incoming clients, the other to "garbage-collect" the closed connections: a server-side Connection can detect if a client closed its connection by reading 0 from recv(), and signals that to the server by setting mIsOpen to false. The CloseSocket thread runs and cleans up the connections (calling the OnClientDisconnect hook):
#ifndef SERVER_H
#define SERVER_H
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <thread>
#include <string>
#include "Vector.h"
#include "Connection.h"
#include "ThreadsafeQueue.h"
#include "Message.h"
#include "debug.h"
template <typename T>
class Server
{
protected:
using ConnectionPtr = std::shared_ptr<Connection<T>>;
public:
Server(uint16_t port);
~Server();
void Start();
void Stop();
void Send(ConnectionPtr connection, const Message<T> &message) const;
void SendAll(const Message<T> &message, ConnectionPtr ignore = nullptr) const;
void Disconnect(ConnectionPtr connection);
bool Available() const { return !mInMessageQueue.Empty(); }
void ProcessMessage();
protected:
virtual void OnClientConnect(ConnectionPtr connection) = 0;
virtual void OnClientDisconnect(ConnectionPtr connection) = 0;
virtual void OnMessage(ConnectionPtr sender, Message<T> &message) = 0;
private:
Vector<ConnectionPtr> mConnections;
ThreadsafeQueue<OwnedMessage<T>> mInMessageQueue;
SOCKET mListenSocket;
std::thread mListenThread;
void Listen();
std::thread mSocketRemoveThread;
void RemoveConnections();
std::string mHost;
uint16_t mPort;
static const uint8_t sMaxNumConnections = 10;
bool mIsRunning;
};
template <typename T>
Server<T>::Server(uint16_t port) :mListenSocket(INVALID_SOCKET), mIsRunning(false)
{
WSAData wsa;
WSAStartup(MAKEWORD(2, 2), &wsa);
addrinfo hints, *address;
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
//hints.ai_flags = AI_PASSIVE;
if (getaddrinfo(nullptr, std::to_string(port).c_str(), &hints, &address) != 0)
Error("cannot get server address");
char stringBuf[INET6_ADDRSTRLEN];
if (address->ai_family == AF_INET)
inet_ntop(AF_INET, &reinterpret_cast<sockaddr_in *>(address->ai_addr)->sin_addr, stringBuf, sizeof stringBuf);
else // address->ai_family == AF_INET6
inet_ntop(AF_INET6, &reinterpret_cast<sockaddr_in6 *>(address->ai_addr)->sin6_addr, stringBuf, sizeof stringBuf);
mHost = stringBuf;
mPort = ntohs(address->ai_family == AF_INET ? reinterpret_cast<sockaddr_in *>(address->ai_addr)->sin_port : reinterpret_cast<sockaddr_in6 *>(address->ai_addr)->sin6_port);
if ((mListenSocket = socket(address->ai_family, address->ai_socktype, address->ai_protocol)) == INVALID_SOCKET)
Error("cannot create server socket");
if (bind(mListenSocket, address->ai_addr, address->ai_addrlen) != 0)
Error("cannot bind server socket");
}
template <typename T>
Server<T>::~Server()
{
if (mIsRunning)
closesocket(mListenSocket);
WSACleanup();
}
template <typename T>
void Server<T>::Start()
{
if (mIsRunning) // TODO: error check
return;
mIsRunning = true;
PRINTLN("server running");
mListenThread = std::thread(&Server::Listen, this); // start thread that accepts new connections
mSocketRemoveThread = std::thread(&Server::RemoveConnections, this);
}
template <typename T>
void Server<T>::Stop()
{
for (std::unique_ptr<Connection<T>> &connection : mConnections)
connection->Close();
}
template <typename T>
void Server<T>::Listen()
{
if (listen(mListenSocket, sMaxNumConnections) != 0)
Error("listen error");
PRINT("server listening # ");
PRINT(mHost);
PRINT(" on port ");
PRINTLN(std::to_string(mPort));
static uint32_t connectionId = 0;
while (true)
{
sockaddr_storage clientAddress;
int clientAddressLength = sizeof(sockaddr_storage);
SOCKET clientSocket = accept(mListenSocket, reinterpret_cast<sockaddr*>(&clientAddress), &clientAddressLength); // accept connections (blocking)
if (clientSocket == INVALID_SOCKET)
Error("cannot create client socket");
mConnections.EmplaceLast(new Connection<T>(Connection<T>::Owner::SERVER, connectionId++, clientSocket, mInMessageQueue)); // create new connection to client (receive/send threads)
OnClientConnect(mConnections.Last()); // callback called on new connections
char stringBuf[INET6_ADDRSTRLEN];
if (clientAddress.ss_family == AF_INET)
inet_ntop(AF_INET, &reinterpret_cast<sockaddr_in *>(&clientAddress)->sin_addr, stringBuf, sizeof stringBuf);
else // clientAddress.ss_family == AF_INET6
inet_ntop(AF_INET6, &reinterpret_cast<sockaddr_in6 *>(&clientAddress)->sin6_addr, stringBuf, sizeof stringBuf);
PRINT("accepted connection from ");
PRINT(stringBuf);
PRINT(" on port ");
PRINTLN(clientAddress.ss_family == AF_INET ? std::to_string(ntohs(reinterpret_cast<sockaddr_in *>(&clientAddress)->sin_port)) : std::to_string(ntohs(reinterpret_cast<sockaddr_in6 *>(&clientAddress)->sin6_port)));
}
}
template <typename T>
void Server<T>::Send(ConnectionPtr connection, const Message<T> &message) const
{
if (connection->IsOpen())
connection->Send(message);
}
template <typename T>
void Server<T>::SendAll(const Message<T> &message, ConnectionPtr ignore) const
{
for (const ConnectionPtr &connection : mConnections)
{
if (connection != ignore && connection->IsOpen())
connection->Send(message);
}
}
template <typename T>
void Server<T>::ProcessMessage()
{
OwnedMessage<T> message(mInMessageQueue.Front());
mInMessageQueue.DeQueue();
OnMessage(message.GetSender(), message);
}
template <typename T>
void Server<T>::RemoveConnections()
{
while (true)
{
for (typename Vector<ConnectionPtr>::Iterator it = mConnections.Begin(), end = mConnections.End(); it != end; ++it)
if (!(*it)->IsOpen())
{
(*it)->Close();
OnClientDisconnect(*it);
it = mConnections.Remove(it);
}
}
}
#endif // SERVER_H
Everything works fine, but I have problems with synchronizing the container of Connections in the server: it is accessed by both the listening thread (which inserts new connections) and the garbage collector thread (which scans the array for dead connection to remove).
How can I address this issue? A vector is obviously the worst choice (dynamic reallocation), being inherently unsafe for concurrent access, but how can I store connections that needs to be manipulated by different threads? Please I need basic/bare-bones/not fancy solutions, since this application is for learning purposes only.
As an aside, how/where can I start learning about how to structure network code more efficiently? How can I improve the structure of my framework?
Thanks

Related

Function Timeout not working as expected

I'm just starting to learn the C++ and I would like to test what I'm learning on a Raspberry. My project, for now, consist to transfert data from a Raspberry to another through WiFi. This works very well but the problem comes when the Raspberry client disconnects from WiFi. At this time, the Server wait indefinitely. So to avoid that, I search on Google to have a Timeout function and here it is:
class Timeout;
static Timeout * global_timeout_instance = 0;
class Timeout {
public:
int m_timeout;
jmp_buf env;
Timeout(int timeout) : m_timeout(timeout) {
if (global_timeout_instance) {
throw "Timeout already in use";
}
global_timeout_instance = this;
}
~Timeout() {
stop();
global_timeout_instance = 0;
}
static void alarm_handler(int signum) {
longjmp(global_timeout_instance->env, 1);
}
void start() {
Timeout * ptr = this;
if (setjmp(env) != 0) {
// Don't do anything except throw here, since the state
// is... funky...
printf("Alarm fired: %p\n", ptr);
throw global_timeout_instance;
}
signal(SIGALRM, alarm_handler);
alarm(2);
}
void stop() {
alarm(0);
}
};
So this function works very well because when the connection is lost, the program stop directly. But I would like to do some action after the loss of connection. So here is my entire code:
#include <exception>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string>
#include <iostream>
#include <cstdio>
#include <setjmp.h>
#include <signal.h>
class Timeout;
static Timeout * global_timeout_instance = 0;
class Timeout {
public:
int m_timeout;
jmp_buf env;
Timeout(int timeout) : m_timeout(timeout) {
if (global_timeout_instance) {
throw "Timeout already in use";
}
global_timeout_instance = this;
}
~Timeout() {
stop();
global_timeout_instance = 0;
}
static void alarm_handler(int signum) {
longjmp(global_timeout_instance->env, 1);
}
void start() {
Timeout * ptr = this;
if (setjmp(env) != 0) {
// Don't do anything except throw here, since the state
// is... funky...
printf("Alarm fired: %p\n", ptr);
throw global_timeout_instance;
}
signal(SIGALRM, alarm_handler);
alarm(2);
}
void stop() {
alarm(0);
}
};
int main(int argc , char *argv[])
{
int socket_desc , client_sock , c , read_size;
struct sockaddr_in server , client;
char client_message[2000];
//Create socket
socket_desc = socket(AF_INET , SOCK_STREAM , 0);
if (socket_desc == -1)
{
printf("Could not create socket");
}
puts("Socket created");
//Prepare the sockaddr_in structure
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons( 8888 );
//Bind
if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0)
{
//print the error message
perror("bind failed. Error");
return 1;
}
puts("bind done");
//Listen
listen(socket_desc , 3);
//Accept and incoming connection
puts("Waiting for incoming connections...");
c = sizeof(struct sockaddr_in);
//accept connection from an incoming client
client_sock = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c);
if (client_sock < 0)
{
perror("accept failed");
return 1;
}
puts("Connection accepted");
//Receive a message from client
while(1)
{
try
{
Timeout timeout(1);
timeout.start();
read_size = recv(client_sock , client_message , 2000 , MSG_WAITALL);
}
catch(Timeout * t)
{
printf("Timeout detected\n");
// Do some action later
}
printf("%d\n", read_size);
if(client_message !="")
{
puts(client_message);
}
else
{
printf("Not Ok");
}
memset(client_message, 0, sizeof(client_message));
}
if(read_size == 0)
{
puts("Client disconnected");
fflush(stdout);
}
else if(read_size == -1)
{
perror("recv failed");
}
return 0;
}
I don't know how I can do some action after the Timeout.
Here is the message I received when the program stop:
Alarm fired: 0x7eff8c40 terminate called after throwing an instance of
'Timeout*' Aborted
Can someone help me on this problem?
Thank you.
Kevin

does this c++ code have memory leaks?

I'm trying to understand this Libevent c++ code I got from this page.
I'm a bit confused - am I correct to think that this code might have memory leaks?
It seems like ConnectionData pointer is created in on_connect() callback, but delete() is only called on bad read or after write is complete.
What if connection was accept()ed - but there were no reads or writes? so is that pointer just stays in daemon memory?
#include <event.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <iostream>
// Read/write buffer max length
static const size_t MAX_BUF = 512;
typedef struct {
struct event ev;
char buf[MAX_BUF];
size_t offset;
size_t size;
} ConnectionData;
void on_connect(int fd, short event, void *arg);
void client_read(int fd, short event, void *arg);
void client_write(int fd, short event, void *arg);
int main(int argc, char **argv)
{
// Check arguments
if (argc < 3) {
std::cout << "Run with options: <ip address> <port>" << std::endl;
return 1;
}
// Create server socket
int server_sock = socket(AF_INET, SOCK_STREAM, 0);
if (server_sock == -1) {
std::cerr << "Failed to create socket" << std::endl;
return 1;
}
sockaddr_in sa;
int on = 1;
char * ip_addr = argv[1];
short port = atoi(argv[2]);
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
sa.sin_addr.s_addr = inet_addr(ip_addr);
// Set option SO_REUSEADDR to reuse same host:port in a short time
if (setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1) {
std::cerr << "Failed to set option SO_REUSEADDR" << std::endl;
return 1;
}
// Bind server socket to ip:port
if (bind(server_sock, reinterpret_cast<const sockaddr*>(&sa), sizeof(sa)) == -1) {
std::cerr << "Failed to bind server socket" << std::endl;
return 1;
}
// Make server to listen
if (listen(server_sock, 10) == -1) {
std::cerr << "Failed to make server listen" << std::endl;
return 1;
}
// Init events
struct event evserver_sock;
// Initialize
event_init();
// Set connection callback (on_connect()) to read event on server socket
event_set(&evserver_sock, server_sock, EV_READ, on_connect, &evserver_sock);
// Add server event without timeout
event_add(&evserver_sock, NULL);
// Dispatch events
event_dispatch();
return 0;
}
// Handle new connection {{{
void on_connect(int fd, short event, void *arg)
{
sockaddr_in client_addr;
socklen_t len = 0;
// Accept incoming connection
int sock = accept(fd, reinterpret_cast<sockaddr*>(&client_addr), &len);
if (sock < 1) {
return;
}
// Set read callback to client socket
ConnectionData * data = new ConnectionData;
event_set(&data->ev, sock, EV_READ, client_read, data);
// Reschedule server event
event_add(reinterpret_cast<struct event*>(arg), NULL);
// Schedule client event
event_add(&data->ev, NULL);
}
//}}}
// Handle client request {{{
void client_read(int fd, short event, void *arg)
{
ConnectionData * data = reinterpret_cast<ConnectionData*>(arg);
if (!data) {
close(fd);
return;
}
int len = read(fd, data->buf, MAX_BUF - 1);
if (len < 1) {
close(fd);
delete data;
return;
}
data->buf[len] = 0;
data->size = len;
data->offset = 0;
// Set write callback to client socket
event_set(&data->ev, fd, EV_WRITE, client_write, data);
// Schedule client event
event_add(&data->ev, NULL);
}
//}}}
// Handle client responce {{{
void client_write(int fd, short event, void *arg)
{
ConnectionData * data = reinterpret_cast<ConnectionData*>(arg);
if (!data) {
close(fd);
return;
}
// Send data to client
int len = write(fd, data->buf + data->offset, data->size - data->offset);
if (len < data->size - data->offset) {
// Failed to send rest data, need to reschedule
data->offset += len;
event_set(&data->ev, fd, EV_WRITE, client_write, data);
// Schedule client event
event_add(&data->ev, NULL);
}
close(fd);
delete data;
}
//}}}
The documentation for event_set says that the only valid event types are EV_READ or EV_WRITE, but the callback will be invoked with EV_TIMEOUT, EV_SIGNAL, EV_READ, or EV_WRITE. The documentation is not clear, but I expect the read callback will be invoked when the socket is closed by the client. I expect the delete in the failure branch in client_read will handle this situation.
Note that that is only the case if the client sends a FIN or RST packet. A client could establish a connection and leave it open forever. For this reason, this code should be modified to have a timeout (perhaps via event_once) and require the client send a message within that timeout.

send data between two client sockets

I have to make an app using C sockets on Mac-OS that sends data from one socket to other socket, like this.
Server waits for connections
Client connect to server(from 1). -> socket1
Server connects to an external server and obtains an socket. -> socket2
From now on the server job is finish. The data exchange should be made only between the client socket (from 2) and socket obtained from 3.
Current implementation:
Server makes the connection and then reads data from one socket and sends to other.
Any ides how after step 3 to pipe line the two sockets socket1 and socket2.
Well your problem can be solved in two ways:
1) You need to code the part related to the connection formation between client and external server. But this puts an extra overload on the client, because it needs to make two connections, to both the servers (and I strongly feel the middle server in this case is useless).
2) Second way of solving it is passing the sockets between the servers:
Client connects to the server, this middle server sends this socket to the external server. Now external server starts communication with the client. This can be done only if both the server processes run on the same machine. And the file-descriptors are usually passed using Unix Domain Sockets.
Here is the code which I have. You can use these two functions to either send or receive the file-descriptors. It works on my Linux machine. I don't know about Mac-OS.
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/uio.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
/* this function passes 'fd_to_send'
file descriptor via
a unix domain socket 'sfd'...
*/
void pass_fd(int sfd, int fd_to_send)
{
struct msghdr msg;
/*allocate memory to 'msg_control' field in msghdr struct */
char buf[CMSG_SPACE(sizeof(int))];
/*the memory to be allocated should include data + header..
this is calculated by the above macro...(it merely adds some
no. of bytes and returs that number..*/
struct cmsghdr *cmsg;
struct iovec ve;
/*must send/receive atleast one byte...
main purpose is to have some error
checking.. but this is completely
irrelevant in the current context..*/
char *st ="I";
/*jst let us allocate 1 byte for formality
and leave it that way...*/
ve.iov_base = st;
ve.iov_len =1;
/*attach this memory to our main msghdr struct...*/
msg.msg_iov = &ve;
msg.msg_iovlen = 1;
/*these are optional fields ..
leave these fields with zeros..
to prevent unnecessary SIGSEGVs..*/
msg.msg_name = NULL;
msg.msg_namelen = 0;
/*here starts the main part..*/
/*attach the 'buf' to msg_control..
and fill in the size field correspondingly..
*/
msg.msg_control = buf;
msg.msg_controllen = sizeof(buf);
/*actually msg_control field must
point to a struct of type 'cmsghdr'
we just allocated the memory, yet we need to
set all the corresponding fields..
It is done as follows:
*/
cmsg = CMSG_FIRSTHDR(&msg);
/* this macro returns the address in the buffer..
from where the first header starts..
*/
/*set all the fields appropriately..*/
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
cmsg->cmsg_len = CMSG_LEN(sizeof(fd_to_send));
/*in the above field we need to store
the size of header + data(in this case 4 bytes(int) for our fd..
this is returned by the 'CMSG_LEN' macro..*/
*(int*)CMSG_DATA(cmsg) = fd_to_send;
/*after the above three fields we keep the actual data..
the macro 'CMSG_DATA' returns pointer to this location
and we set it to the file descriptor to be sent..
*/
msg.msg_controllen = cmsg->cmsg_len;
/*now that we have filled the 'cmsg' struct
we store the size of this struct..*/
/*this one isn't required when you
pass a single fd..
but useful when u pass multiple fds.*/
msg.msg_flags = 0;
/*leave the flags field zeroed..*/
if(sendmsg( sfd, &msg, 0)==-1){ perror("snd:\n"); exit(1); }
/*send this over the UNIX deomain socoket..*/
printf("sent fd:%d\n", fd_to_send);
close(fd_to_send);
/*close the fd which was sent..*/
}
/*returns the received fd over the unix domain socket 'sfd'..*/
int recv_fd(int sfd)
{
struct msghdr msg;
/*do all the unwanted things first...
same as the send_fd function..*/
struct iovec io;
char ptr[1];
io.iov_base = ptr;
io.iov_len = 1;
msg.msg_name = 0;
msg.msg_namelen = 0;
msg.msg_iov = &io;
msg.msg_iovlen = 1;
/*-----------------------*/
char buf[CMSG_SPACE(sizeof(int))];
msg.msg_control = buf;
msg.msg_controllen = sizeof(buf);
/*reasoning is same..as above*/
/*now here comes the main part..*/
if(recvmsg( sfd, &msg, 0)==-1)
{
/*some shit has happened*/
perror("recv\n");
exit(1);
}
struct cmsghdr *cm;
cm = CMSG_FIRSTHDR(&msg);
/*get the first message header..*/
if(cm->cmsg_type != SCM_RIGHTS)
{
/*again some shit has happened..*/
perror("unknown type..\n");
exit(1);
}
/*if control has reached here.. this means
we have got the correct message..and when you
extract the fd out of this message
this need not be same as the one which was sent..
allocating a new fd is all done by the kernel
and our job is jst to use it..*/
printf("received fd:%d\n", *(int*)CMSG_DATA(cm));
return *(int*)CMSG_DATA(cm);
}
In the below example:
ClientOne and ClientTwo connect to the server.
When the server receives both ClientOne and ClientTwo's socket descriptor, it sends ClientOne's information to ClientTwo and vice-versa.
The information it sends is the IP and the client is coming from. Server shuts down.
When each client receives their info, a socket is created and they connect to eachother. The server socket is then shutdown.
Socket Class:
#include <winsock2.h>
#include <Ws2tcpip.h>
#include <windows.h>
#include <cstdint>
#include <string>
#include <stdexcept>
#include <iostream>
#include <thread>
#include <vector>
class Socket
{
private:
SOCKET socket;
std::uint32_t Port;
std::string Address;
bool Listen, Initialized, Asynchronous;
void Swap(Socket &S);
void UnInitialized();
public:
Socket();
Socket(std::uint32_t Port, std::string Address, bool Listen = false, bool Asynchronous = false);
Socket(const Socket &S) = delete;
Socket(Socket && S);
~Socket();
Socket& operator = (const Socket &S) = delete;
Socket& operator = (Socket && S);
int Recv(void* Buffer, std::uint32_t BufferLength);
int Recv(SOCKET S, void* Buffer, std::uint32_t BufferLength);
std::uint32_t RecvEx(void* Buffer, std::uint32_t BufferLength);
std::uint32_t RecvEx(SOCKET S, void* Buffer, std::uint32_t BufferLength);
int Send(void* Buffer, std::size_t BufferSize);
int Send(SOCKET S, void* Buffer, std::size_t BufferSize);
void Connect();
void Connect(std::uint32_t Port, std::string Address, bool Listen, bool Asynchronous);
SOCKET Accept(sockaddr* ClientInfo, int* ClientInfoSize);
void Close();
SOCKET GetSocket() const;
};
Socket::~Socket()
{
Close();
}
void Socket::Close()
{
if (socket)
{
shutdown(socket, SD_BOTH);
closesocket(socket);
socket = 0;
}
if (Initialized)
{
WSACleanup();
}
}
SOCKET Socket::GetSocket() const
{
return this->socket;
}
Socket::Socket(Socket && S) : socket(std::move(S.socket)), Port(std::move(S.Port)), Address(std::move(S.Address)), Listen(std::move(S.Listen)), Initialized(std::move(S.Initialized)), Asynchronous(std::move(S.Asynchronous)) {}
Socket::Socket() : socket(0), Port(0), Address(std::string()), Listen(false), Initialized(false), Asynchronous(false) {}
Socket::Socket(std::uint32_t Port, std::string Address, bool Listen, bool Asynchronous) : socket(0), Port(Port), Address(Address), Listen(Listen), Initialized(true), Asynchronous(Asynchronous)
{
Connect(Port, Address, Listen, Asynchronous);
}
void Socket::Connect()
{
UnInitialized();
Connect(Port, Address, Listen, Asynchronous);
}
void Socket::Connect(std::uint32_t Port, std::string Address, bool Listen, bool Asynchronous)
{
if (!socket)
{
this->Port = Port;
this->Address = Address;
this->Asynchronous = Asynchronous;
this->Initialized = true;
WSADATA wsaData;
struct sockaddr_in* sockaddr_ipv4;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
throw std::runtime_error("Error: WSAStartup Failed");
}
if (Address != "INADDR_ANY")
{
if (Address.find("http://") != std::string::npos)
{
Address = Address.substr(7);
}
std::size_t Position = Address.find("/");
if (Position != std::string::npos)
{
Address = Address.substr(0, Position);
}
struct addrinfo *it = nullptr, *result = nullptr;
getaddrinfo(Address.c_str(), nullptr, nullptr, &result);
for (it = result; it != nullptr; it = it->ai_next)
{
sockaddr_ipv4 = reinterpret_cast<sockaddr_in*>(it->ai_addr);
Address = inet_ntoa(sockaddr_ipv4->sin_addr);
if (Address != "0.0.0.0") break;
}
freeaddrinfo(result);
}
if ((this->socket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
{
this->Close();
throw std::runtime_error("Error: Failed to create socket");
}
struct sockaddr_in SockAddr;
memset(&SockAddr, 0, sizeof(SockAddr));
SockAddr.sin_port = htons(Port);
SockAddr.sin_family = AF_INET;
SockAddr.sin_addr.s_addr = (Address == "INADDR_ANY" ? htonl(INADDR_ANY) : inet_addr(Address.c_str()));
if (Listen && (bind(this->socket, reinterpret_cast<SOCKADDR*>(&SockAddr), sizeof(SockAddr)) == SOCKET_ERROR))
{
this->Close();
throw std::runtime_error("Error: Socket binding failed");
}
if (Listen && (listen(this->socket, SOMAXCONN) == SOCKET_ERROR))
{
this->Close();
throw std::runtime_error("Error: Socket Listening Failed");
}
if(!Listen && (connect(this->socket, reinterpret_cast<SOCKADDR*>(&SockAddr), sizeof(SockAddr)) == SOCKET_ERROR))
{
if(Asynchronous && WSAGetLastError() != WSAEWOULDBLOCK)
{
this->Close();
throw std::runtime_error("Error: Socket Connection failed");
}
else if (!Asynchronous)
{
this->Close();
throw std::runtime_error("Error: Socket Connection failed");
}
}
}
}
SOCKET Socket::Accept(sockaddr* ClientInfo, int* ClientInfoSize)
{
static int Size = sizeof(sockaddr);
return accept(this->socket, ClientInfo, (ClientInfo && ClientInfoSize ? ClientInfoSize : &Size));
}
Socket& Socket::operator = (Socket && S)
{
S.Swap(*this);
return *this;
}
int Socket::Recv(void* Buffer, std::uint32_t BufferLength)
{
return recv(this->socket, reinterpret_cast<char*>(Buffer), BufferLength, 0);
}
int Socket::Recv(SOCKET S, void* Buffer, std::uint32_t BufferLength)
{
return recv(S, reinterpret_cast<char*>(Buffer), BufferLength, 0);
}
std::uint32_t Socket::RecvEx(void* Buffer, std::uint32_t BufferLength)
{
return this->RecvEx(this->socket, Buffer, BufferLength);
}
std::uint32_t Socket::RecvEx(SOCKET S, void* Buffer, std::uint32_t BufferLength)
{
UnInitialized();
char* Pointer = reinterpret_cast<char*>(Buffer);
std::uint32_t TotalRead = 0;
while (BufferLength > 0)
{
int BytesRead = recv(S, Pointer, std::min(1024 * 1024, static_cast<int>(BufferLength)), 0);
if (BytesRead < 0)
{
if ((BytesRead == SOCKET_ERROR) && (WSAGetLastError() == WSAEWOULDBLOCK))
continue;
throw std::runtime_error("Error! RecvEx: Failed To Read Bytes.");
}
if (BytesRead == 0) break;
Pointer += BytesRead;
BufferLength -= BytesRead;
TotalRead += BytesRead;
}
return TotalRead;
}
int Socket::Send(void* Buffer, std::size_t BufferSize)
{
return send(this->socket, reinterpret_cast<char*>(Buffer), BufferSize, 0);
}
int Socket::Send(SOCKET S, void* Buffer, std::size_t BufferSize)
{
return send(S, reinterpret_cast<char*>(Buffer), BufferSize, 0);
}
void Socket::Swap(Socket &S)
{
using std::swap;
swap(socket, S.socket);
swap(Port, S.Port);
swap(Address, S.Address);
swap(Listen, S.Listen);
swap(Initialized, S.Initialized);
swap(Asynchronous, S.Asynchronous);
}
void Socket::UnInitialized()
{
if (!Initialized)
{
throw std::runtime_error("\nError! Socket Not Constructed!");
std::cout << "Socket Not Constructed!\n";
ExitProcess(0);
}
}
Server.cpp:
#include "Sockets.hpp"
#define PORT 27015
#define ADDRESS INADDR_ANY
#define CLIENTCOUNT 2
typedef struct
{
std::string ip;
int port;
SOCKET sock;
} ClientInfo;
template <typename T>
inline T ReadPointer(TCHAR* &Pointer)
{
T Result = *(reinterpret_cast<T*>(Pointer));
Pointer += sizeof(T) / sizeof(TCHAR);
return Result;
}
template <typename T>
inline void WritePointer(TCHAR* &Pointer, const T& Value)
{
*(reinterpret_cast<T*>(Pointer)) = Value;
Pointer += sizeof(T) / sizeof(TCHAR);
}
bool SendClient(ClientInfo* client, ClientInfo* receiver)
{
int datasize = sizeof(client->ip.size()) + client->ip.size() + sizeof(client->port);
std::vector<char> buffer(datasize, 0);
char* ptr = &buffer[0];
WritePointer(ptr, client->ip.size());
for (std::size_t i = 0; i < client->ip.size(); ++i)
WritePointer(ptr, client->ip[i]);
WritePointer(ptr, client->port);
std::cout << "Sending: " << &buffer[0] << "\n";
return send(receiver->sock, &buffer[0], datasize, 0) == datasize;
}
bool ReadClient(SOCKET sock, ClientInfo* client)
{
std::size_t ip_size = 0;
recv(sock, (char*) &ip_size, sizeof(client->ip.size()), 0);
client->ip.resize(ip_size);
recv(sock, &client->ip[0], ip_size, 0);
recv(sock, (char*) &client->port, sizeof(int), 0);
std::cout<<client->ip<<"\n";
return true;
}
int main()
{
Socket s;
s.Connect(PORT, "localhost", true, false);
char buffer[1024] = {0};
std::vector<ClientInfo> clients;
while(true)
{
if (clients.size() < CLIENTCOUNT)
{
sockaddr_in ClientAddressInfo = {0};
SOCKET sock = s.Accept(reinterpret_cast<sockaddr*>(&ClientAddressInfo), nullptr);
char* ip = inet_ntoa(ClientAddressInfo.sin_addr);
int port = (int) ntohs(ClientAddressInfo.sin_port);
ClientInfo info = {ip, port, sock};
clients.push_back(info);
std::cout << "Client Connected From: " << ip << " on port: " << port << "\n";
}
if (ReadAsync(s, buffer))
{
std::cout << "Connected\n";
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
if (clients.size() >= CLIENTCOUNT)
{
SendClient(&clients[0], &clients[1]);
SendClient(&clients[1], &clients[0]);
return 0;
}
}
}
Client.cpp:
#define PORT 27015
#define ADDRESS INADDR_ANY
#define CLIENTCOUNT 2
typedef struct
{
std::string ip;
int port;
SOCKET sock;
} ClientInfo;
template <typename T>
inline T ReadPointer(TCHAR* &Pointer)
{
T Result = *(reinterpret_cast<T*>(Pointer));
Pointer += sizeof(T) / sizeof(TCHAR);
return Result;
}
template <typename T>
inline void WritePointer(TCHAR* &Pointer, const T& Value)
{
*(reinterpret_cast<T*>(Pointer)) = Value;
Pointer += sizeof(T) / sizeof(TCHAR);
}
bool SendClient(ClientInfo* client, ClientInfo* receiver)
{
int datasize = sizeof(client->ip.size()) + client->ip.size() + sizeof(client->port);
std::vector<char> buffer(datasize, 0);
char* ptr = &buffer[0];
WritePointer(ptr, client->ip.size());
for (std::size_t i = 0; i < client->ip.size(); ++i)
WritePointer(ptr, client->ip[i]);
WritePointer(ptr, client->port);
std::cout << "Sending: " << &buffer[0] << "\n";
return send(receiver->sock, &buffer[0], datasize, 0) == datasize;
}
bool ReadClient(SOCKET sock, ClientInfo* client)
{
std::size_t ip_size = 0;
recv(sock, (char*) &ip_size, sizeof(client->ip.size()), 0);
client->ip.resize(ip_size);
recv(sock, &client->ip[0], ip_size, 0);
recv(sock, (char*) &client->port, sizeof(int), 0);
return true;
}
bool ReadAsync(const Socket &sock, ClientInfo* client)
{
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 100000;
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(sock.GetSocket(), &rfds);
for (int i = 0; i < 600; ++i)
{
if (select(sock.GetSocket(), &rfds, &rfds, NULL, &tv))
{
return ReadClient(sock.GetSocket(), client);
}
tv.tv_sec = 0;
tv.tv_usec = 100000;
}
return false;
}
int main()
{
Socket s;
s.Connect(PORT, "localhost", false, false);
std::vector<SOCKET> clients;
ClientInfo client = {};
while(true)
{
if (ReadAsync(s, &client))
{
std::cout<<"IP: "<<client.ip<<" PORT: "<<client.port<<"\n";
s = std::move(Socket(client.port, client.ip, true, false));
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
system("CLS");
std::cout<<"Connecting..\n";
}
}

IOCP Socket, Not sure what to do next

I learned how to create sockets using the Windows Message Proc and switched on FD_CONNECT, FD_ACCEPT, FD_CLOSE, etc.. I used: WSAAsyncSelect(socket, WindowHandle, WM_SOCKET, FD_READ | FD_WRITE | FD_CONNECT | FD_CLOSE | FD_ACCEPT).
This let me know when a socket: accepted, closed, received data all without having to poll for it.
Now I'm trying to learn to do the same for console applications but using IOCP.
I have written the following:
#include <Winsock2.h>
#include <Ws2tcpip.h>
#include <Windows.h>
#include <iostream>
#include <thread>
#include <stdexcept>
#include <cstring>
class Socket
{
private:
typedef struct
{
OVERLAPPED Overlapped;
WSABUF DataBuf;
char Buffer[1024];
unsigned int BytesSent;
unsigned int BytesReceived;
} PER_IO_OPERATION_DATA;
typedef struct
{
SOCKET Sock;
} PER_HANDLE_DATA;
protected:
void Close();
std::function<void(HANDLE)> Worker;
public:
Socket();
bool Start(std::string Address, unsigned int Port, bool Listen);
void Read(char* Buffer, int bufflen); //reads from a socket.
void Write(char* Buffer, int bufflen); //writes to a socket.
bool Accept(); //accepts a socket.
virtual void OnRead(); //Called when Reading.
virtual void OnWrite(); //Called when Writing.
virtual void OnAccept(); //Called when a socket has been accepted.
virtual void OnConnect(); //Called when connected.
virtual void OnDisconnect(); //Called when disconnected.
};
bool Socket::Start(std::string Address, unsigned int Port, bool Listen)
{
WSADATA wsaData;
SOCKET sock = 0;
struct sockaddr_in* sockaddr_ipv4;
//WSA Startup and getaddrinfo, etc.. here..
//Create IOCP Handle and worker threads.
HANDLE IOCPPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr, 0, 0);
if (!IOCPPort)
{
this->Close();
throw std::runtime_error("Error: Creating IOCP Failed With Error: " + std::to_string(GetLastError()));
}
SYSTEM_INFO SystemInfo = {0};
GetSystemInfo(&SystemInfo);
for (std::size_t I = 0; I < SystemInfo.dwNumberOfProcessors * 2; ++I)
{
std::thread(this->Worker, IOCPPort).detach();
}
//Set the socket to overlapped.
if ((sock = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, nullptr, 0, WSA_FLAG_OVERLAPPED)) == INVALID_SOCKET)
{
this->Close();
throw std::runtime_error("Error: ");
}
struct sockaddr_in SockAddr;
std::memset(&SockAddr, 0, sizeof(SockAddr));
SockAddr.sin_port = htons(Port);
SockAddr.sin_family = AF_INET;
SockAddr.sin_addr.s_addr = (Address == "INADDR_ANY" ? htonl(INADDR_ANY) : inet_addr(Address.c_str()));
//If it is a server socket being created, bind it.
if (Listen && (bind(sock, reinterpret_cast<SOCKADDR*>(&SockAddr), sizeof(SockAddr)) == SOCKET_ERROR))
{
this->Close();
throw std::runtime_error("Error: ");
}
//If it is a server socket, start listenening.
if (Listen && (listen(sock, SOMAXCONN) == SOCKET_ERROR))
{
this->Close();
throw std::runtime_error("Error: ");
}
//Otherwise it is a client socket so just connected..
//if(!Listen && (connect(this->socket, reinterpret_cast<SOCKADDR*>(&SockAddr), sizeof(SockAddr)) == SOCKET_ERROR))
//Associate this socket with a completion port.
if (CreateIoCompletionPort(reinterpret_cast<HANDLE>(sock), IOCPPort, 0, 0) != IOCPPort)
{
this->Close();
throw std::runtime_error("Error: ");
}
//setsockopt(sock, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, (char *)sdListen, sizeof(sdListen));
}
However, now I'm stuck. I'm not sure what I need to do after creating the IoCompletionPort. How would I know when the socket has been accepted so I can call OnAccept or how can I tell when there is data to be read so I can call OnRead? I've went through all pages on google and I cannot find anything that is similar to OnRead, OnAccept or OnWrite.
I just want to make it scalable and have callbacks for when something happens without using events or message loop. The only thing I saw on google that interested me was IOCP but I'm completely lost. Any ideas what I need to do next?
I wrote some articles on IOCP server design a long time ago and I also have some code you can download which does the basics for you.
You can get the code and find links to the articles from here. They may help you to understand what happens next and how to structure an IOCP client or server.

Server & client socket connection issue re. send(), accept() and multi-threading

I'm designing a server program in C++ to receive multiple client connections and pass them into threads, however I've reached an impasse.
The socket connections all work fine, as does the multi-threading - almost. Please see my code below (it compiles and runs fine).
I've tried to pare it down to the essentials for you to make it easy to follow and take up the least of your time. I've commented the code to help you see where the problem is, then I describe the problem in detail at the bottom. If you can help me then I would be very grateful!
#include <vector>
#include <boost/thread.hpp>
#include "unix_serverSocket.h"
#include "server.h"
extern const string socketAddress;
void do_stuff(ServerSocket *client)
{
string in;
string out;
try
{
/* Gets input until the client closes the connection, then throws an exception, breaking out of the loop */
while (true)
{
*client >> in; /* Receives data from client socket connection */
/* Assume the input is processed fine and returns the result into 'out' */
sleep(3); /* I've put sleep() here to test it's multithreading properly - it isn't */
*client << out; /* Returns result to client - send() is called here */
/* If I put sleep() here instead it multithreads fine, so the server is waiting for send() before it accepts a new client */
}
}
catch (SocketException &)
{
delete client;
return;
}
}
int main()
{
try
{
ServerSocket server(socketAddress);
while (true)
{
ServerSocket *client = new ServerSocket();
/* See below */
server.accept(*client);
boost::thread newThread(do_stuff, client);
}
}
catch (SocketException &e)
{
cout << "Error: " << e.description() << endl;
}
return 0;
}
After a client socket connection has been passed to a thread, main() gets back to
the line:
server.accept(*client);
but then waits for the previous connection to send its result back to the
client via send() before it will accept a new connection - i.e. the server is waiting
for something to happen in the thread before it will accept a new client! I don't
want it to do this - I want it to send the client connection to a thread then accept
more client connections straight away and pass them into more threads!
In case you're wondering why I created a pointer to the socket here...
ServerSocket *client = new ServerSocket();
... if I don't create a pointer then the recv() function called by the thread fails to receive data from the client, which seems to be due to the thread shallow copying the client socket connection and the garbage collector not understanding threads and thinking the client connection is no longer going to be used after it has been passed to the thread and so destroying it before recv() is called in the thread. Hence using a pointer created on the heap, which worked. Anyway, when I reworked the code using fork() instead of threads (which meant I didn't need to create the socket on the heap), I still had the same problem with the server not being able to accept new clients.
I guess I need to change the server settings somehow so that it doesn't wait for a client to send() before accepting a new one, however despite much Googling I'm still at a loss!
Here's the relevant socket connection code in case it helps (the server and clients are all on the same box and thus connecting via local UNIX sockets):
class Socket
{
private:
int sockfd;
struct sockaddr_un local;
public:
Socket();
virtual ~Socket();
bool create();
bool bind(const string &);
bool listen() const;
bool accept(Socket &) const;
bool send(const string &) const;
int recv(string &) const;
void close();
bool is_valid() const
{
return sockfd != -1;
}
};
bool Socket::create()
{
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
if (!is_valid())
{
return false;
}
int reuseAddress = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (const char*) &reuseAddress, sizeof(reuseAddress)) == -1)
{
return false;
}
return true;
}
bool Socket::bind(const string &socketAddress)
{
if (!is_valid())
{
return false;
}
local.sun_family = AF_UNIX;
strcpy(local.sun_path, socketAddress.c_str());
unlink(local.sun_path);
int len = strlen(local.sun_path) + sizeof(local.sun_family);
int bind_return = ::bind(sockfd, (struct sockaddr *) &local, len);
if (bind_return == -1)
{
return false;
}
return true;
}
bool Socket::listen() const
{
if (!is_valid())
{
return false;
}
int listen_return = ::listen(sockfd, MAXCLIENTCONNECTIONS);
if (listen_return == -1)
{
return false;
}
return true;
}
bool Socket::accept(Socket &socket) const
{
int addr_length = sizeof(local);
socket.sockfd = ::accept(sockfd, (sockaddr *) &local, (socklen_t *) &addr_length);
if (socket.sockfd <= 0)
{
return false;
}
else
{
return true;
}
}
int Socket::recv(string &str) const
{
char buf[MAXRECV + 1];
str = "";
memset(buf, 0, MAXRECV + 1);
int status = ::recv(sockfd, buf, MAXRECV, 0);
if (status == -1)
{
cout << "status == -1 errno == " << errno << " in Socket::recv" << endl;
return 0;
}
else if (status == 0)
{
return 0;
}
else
{
str = buf;
return status;
}
}
bool Socket::send(const string &str) const
{
int status = ::send(sockfd, str.c_str(), str.size(), MSG_NOSIGNAL);
if (status == -1)
{
return false;
}
else
{
return true;
}
}
class ServerSocket : private Socket
{
public:
ServerSocket(const string &);
ServerSocket() {};
virtual ~ServerSocket();
void accept(ServerSocket &);
const ServerSocket & operator << (const string &) const;
const ServerSocket & operator >> (string &) const;
};
ServerSocket::ServerSocket(const string &socketAddress)
{
if (!Socket::create())
{
throw SocketException("Could not create server socket");
}
if (!Socket::bind(socketAddress))
{
throw SocketException("Could not bind to port");
}
if (!Socket::listen())
{
throw SocketException("Could not listen to socket");
}
}
void ServerSocket::accept(ServerSocket &socket)
{
if (!Socket::accept(socket))
{
throw SocketException("Could not accept socket");
}
}
const ServerSocket & ServerSocket::operator << (const string &str) const
{
if (!Socket::send(str))
{
throw SocketException("Could not write to socket");
}
return *this;
}
const ServerSocket & ServerSocket::operator >> (string &str) const
{
if (!Socket::recv(str))
{
throw SocketException("Could not read from socket");
}
return *this;
}
I've figured it out! The reason the clients weren't multithreading was that the program creating the client connections was doing so within a mutex - hence it wouldn't create a new connection until the old one had received a reply from the server, and thus the server appeared to be only single-threading! So in short my server program above was fine and it was a problem at the client end - sorry for wasting your time - I didn't even consider the possibility until I completely reworked the program structure by putting the threading at the client end instead, which then revealed the issue.
Thanks for all your help!
Your sockets are blocking! This means that they will wait for the operation to finish before returning.
This is how you make a socket non-blocking:
bool nonblock(int sock)
{
int flags;
flags = fcntl(sock, F_GETFL, 0);
flags |= O_NONBLOCK;
return (fcntl(sock, F_SETFL, flags) == 0);
}
Now the functions accept, read and write will all return an error if the socket would block, setting the errno variable to EWOULDBLOCK or possibly EAGAIN.
If you want to wait for a socket to be ready for reading or writing, you can use the function select. For listening sockets (the one you do accept on) it will be ready to read when a new connection can be accepted.