Cannot get sigaction to work - c++

I'm trying to create two programs: A basic socket server, and a client, both of which will run on Linux machines. The instructions for the server are to set up a socket, accept an incoming client request, set up a handler (for reading a buffer of data) using signal, and enter an infinite sleep loop. The instructions for the client are to set up a socket, connect to the server, and send a buffer of data. I'd like to get this working as described for a single client connection before worrying about closing the connection and starting a new one (not sure where these things should be looped yet, and I'm trying to keep this simple.) I've also learned that signal is deprecated, so I'm attempting to use sigaction as per the example here:
http://www.linuxprogrammingblog.com/code-examples/sigaction
Unfortunately, what happens when I run my code is this:
Server launches
Server sets up socket
Server begins listening and blocks on accept (waiting for client)
Client launches
Client sets up socket
Client connects to server
Server unblocks
Server sets up sigaction
Server begins sleeping
Client calls write
Client appears to write successfully (lord knows where to)
Client blocks waiting for bytes read acknowledgement from server
Server is still sleeping (sigaction never triggered)
Here is my current code for the server:
#include <sys/types.h> // socket, bind
#include <sys/socket.h> // socket, bind, listen, inet_ntoa
#include <netinet/in.h> // htonl, htons, inet_ntoa
#include <arpa/inet.h> // inet_ntoa
#include <netdb.h> // gethostbyname
#include <unistd.h> // read, write, close
#include <string.h> // bzero
#include <netinet/tcp.h> // SO_REUSEADDR
#include <sys/uio.h> // writev
#include <signal.h> // sigaction
#include <sys/time.h> // gettimeofday
#include <unistd.h> // write
#include <fcntl.h> // fcntl
#include <iostream> // cout
using namespace std;
#define BUFSIZE 1500
// Globals
int nreps;
int nbufs;
int newSd;
// Read all the data from the client and output how long it took
void readFromClient(int sig, siginfo_t *siginfo, void *context)
{
cout << "readFromClient triggered!" << endl;
/*
// Set up asynchronous communication
int fd = siginfo->si_fd;
fcntl(fd, F_SETOWN, getpid());
fcntl(fd, F_SETFL, FASYNC);
*/
// Declare data buffer
char databuf[BUFSIZE];
// Record start time
struct timeval theTime;
gettimeofday(&theTime, NULL);
int startTime = theTime.tv_usec + theTime.tv_sec * 1000000;
// Keep reading until the buffer is full
int nRead = 0;
/*
while((nRead += read(newSd, databuf, BUFSIZE - nRead)) < BUFSIZE)
{
cout << "nRead now: " << nRead << endl;
}
*/
// For testing single byte read
cout << "Reading a byte... " << endl;
char bytebuf[1];
read(newSd, bytebuf, 1);
cout << "SUCCESS" << endl;
// Record finish time
gettimeofday(&theTime, NULL);
int finishTime = theTime.tv_usec + theTime.tv_sec * 1000000;
// Calculate the receiving time
int receiveTime = finishTime - startTime;
// Display the receiving time
cout << "data-receiving time = " << receiveTime << " usec" << endl;
// Tell the client how much data was read
cout << "Writing amount read... " << endl;
write(newSd, (void*)nRead, 4);
cout << "SUCCESS" << endl;
// Close the socket
cout << "Closing socket... " << endl;
close(newSd);
cout << "SUCCESS" << endl;
// Exit the program
cout << "Exiting!" << endl;
exit(0);
cout << "Why are you still here?" << endl;
}
int main(int argc, char *argv[])
{
cout << "Server is running!" << endl;
// Store command line arguments
int port = atoi(argv[1]);
int nreps = atoi(argv[2]);
int nbufs = atoi(argv[3]);
cout << "port: " << port << endl;
cout << "nreps: " << nreps << endl;
cout << "nbufs: " << nbufs << endl;
// Declare a socket
sockaddr_in acceptSockAddr;
memset((char*)&acceptSockAddr, '\0', sizeof(acceptSockAddr));
acceptSockAddr.sin_family = AF_INET; // Address Family Internet
acceptSockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
acceptSockAddr.sin_port = htons(port); // convert host byte-order
// Open a stream-oriented socket
int serverSd = socket(AF_INET, SOCK_STREAM, 0);
// Signal OS to reuse this port once server closes
const int on = 1;
setsockopt(serverSd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(int));
// Bind socket to local address
bind(serverSd, (sockaddr*)&acceptSockAddr, sizeof(acceptSockAddr));
// Instruct OS to listen for up to 5 clients
listen(serverSd, 5);
// Declare a new socket
sockaddr_in newSockAddr;
socklen_t newSockAddrSize = sizeof(newSockAddr);
int newSd;
// Set up signal handler for IO from client
struct sigaction action;
memset(&action, '\0', sizeof(action));
action.sa_sigaction = &readFromClient;
action.sa_flags = SA_SIGINFO;
//fcntl(newSd, F_SETSIG, SIGIO); // Fixes problem with si_fd
if(sigaction(SIGIO, &action, NULL) < 0)
{
perror("sigaction");
return 1;
}
// sleep forever
cout << "Sleeping..." << endl;
while(1)
{
cout << "Waiting for client... " << endl;
newSd = accept(serverSd, (sockaddr*)&newSockAddr, &newSockAddrSize);
cout << "SUCCESS" << endl;
cout << "Switching to asynchronous communication... " << endl;
fcntl(newSd, F_SETOWN, getpid());
fcntl(newSd, F_SETFL, FASYNC);
cout << "SUCCESS" << endl;
cout << "Resuming sleep... " << endl;
sleep(10);
}
return 0;
}
And here is my current code for the client:
#include <sys/types.h> // socket, bind
#include <sys/socket.h> // socket, bind, listen, inet_ntoa
#include <netinet/in.h> // htonl, htons, inet_ntoa
#include <arpa/inet.h> // inet_ntoa
#include <netdb.h> // gethostbyname
#include <unistd.h> // read, write, close
#include <string.h> // bzero
#include <netinet/tcp.h> // SO_REUSEADDR
#include <sys/uio.h> // writev
#include <signal.h> // sigaction
#include <sys/time.h> // gettimeofday
#include <unistd.h> // write
#include <fcntl.h> // fcntl
#include <iostream> // cout
using namespace std;
#define BUFSIZE 1500
#define SIZEOFINT 4
int main(int argc, char *argv[])
{
cout << "Client is running!" << endl;
// Store commmand line arguments
int server_port = atoi(argv[1]);
int nreps = atoi(argv[2]);
int nbufs = atoi(argv[3]);
int bufsize = atoi(argv[4]);
const char* server_name = argv[5];
int testType = atoi(argv[6]);
cout << "server_port: " << server_port << endl;
cout << "nreps: " << nreps << endl;
cout << "nbufs: " << nbufs << endl;
cout << "bufsize: " << bufsize << endl;
cout << "server_name: " << server_name << endl;
cout << "testType: " << testType << endl;
// Check to ensure proper buffer count/sizes
if(nbufs * bufsize != BUFSIZE)
{
cout << "nbufs times bufsize must equal " << BUFSIZE << endl;
exit(0);
}
if(testType < 1 || testType > 3)
{
cout << "test type must be 1, 2, or 3" << endl;
exit(0);
}
// Create buffers
char databuf[nbufs][bufsize];
// Retrieve hostent structure
struct hostent* host = gethostbyname(server_name);
// Declare socket structure
sockaddr_in sendSockAddr;
memset((char*)&sendSockAddr, '\0', sizeof(sendSockAddr));
sendSockAddr.sin_family = AF_INET; // Address Family Internet
sendSockAddr.sin_addr.s_addr = inet_addr(inet_ntoa(*(struct in_addr*)*host->h_addr_list));
sendSockAddr.sin_port = htons(server_port); // convert host byte-order
// Open stream-oriented socket
int clientSd = socket(AF_INET, SOCK_STREAM, 0);
// Connect socket to server
cout << "Connecting socket to server... " << endl;
int code = connect(clientSd, (sockaddr*)&sendSockAddr, sizeof(sendSockAddr));
cout << "Connection result: " << code << endl;
// Record start time
struct timeval theTime;
gettimeofday(&theTime, NULL);
int startTime = theTime.tv_usec + theTime.tv_sec * 1000000;
// Conduct tests
for(int i = 0; i < nreps; i++)
{
switch(testType)
{
case 1:
{
// Multiple write test
cout << "Running multiple write test" << endl;
for(int j = 0; j < nbufs; j++)
{
cout << "Writing buffer " << j << "... " << endl;
write(clientSd, databuf[j], bufsize);
cout << "SUCCESS" << endl;
}
cout << "Finished multiple write test" << endl;
}
case 2:
{
// Vector write test
cout << "Running vector write test" << endl;
struct iovec vector[nbufs];
for(int j = 0; j < nbufs; j++)
{
vector[j].iov_base = databuf[j];
vector[j].iov_len = bufsize;
}
cout << "Writing vector... " << endl;
writev(clientSd, vector, nbufs);
cout << "SUCCESS" << endl;
cout << "Finished vector write test" << endl;
}
case 3:
{
// Single write test
cout << "Running single write test" << endl;
/*
cout << "Writing... ";
write(clientSd, databuf, nbufs * bufsize);
cout << "SUCCESS" << endl;
*/
// For testing single byte write
cout << "writing a byte..." << endl;
char singleByte[1];
write(clientSd, singleByte, 1);
cout << "wrote a byte!" << endl;
cout << "Finished single write test" << endl;
}
}
}
// Record finish time
gettimeofday(&theTime, NULL);
int finishTime = theTime.tv_usec + theTime.tv_sec * 1000000;
// Calculate the sending time
int sendTime = finishTime - startTime;
// Receive number of bytes read from server
int nReads;
cout << "reading nReads from server... " << endl;
read(clientSd, (void*)nReads, SIZEOFINT);
cout << "SUCCESS" << endl;
// Record read time
gettimeofday(&theTime, NULL);
int readTime = theTime.tv_usec + theTime.tv_sec * 1000000;
// Calculate the round-trip time
int roundTime = readTime - startTime;
// Display data sending statistics
cout << "Test " << testType << ": data-sending time = " << sendTime;
cout << " usec, round-trip time = " << roundTime << " usec, # reads = ";
cout << nReads << endl;
// Close the socket
cout << "Closing the socket... " << endl;
close(clientSd);
cout << "SUCCESS" << endl;
cout << "Exiting!" << endl;
return 0;
}
I've spent around 14 hours troubleshooting this already, and tried a number of things before coming here:
Using SIGTERM instead of SIGIO
Re-arranging the order of operations so the sigaction is set up prior to accepting an incoming connection
Using fcntl inside the triggered function instead of inside the sleep loop
Using the field descriptor from the siginfo_t structure passed into the triggered function
Using the sa_handler instead of setting the flags for sa_siginfo (so siginfo_t is not passed)
Not calling fcntl at all
Switching the servers that these programs are running on
Switching the ports that these programs are using
Calling everything before the sleep loop
At this point my instructor is telling me to use the deprecated signal method instead, but that seems like a poor solution. Surely siginfo is common practice these days, and using it should not have to be this difficult? Any suggestions on things to try would be appreciated!

You don't seem to be fcntl'ing the socket to F_SETOWN yourself as the controlling process and to SETFL the O_ASYNC flag, which causes the socket to actually send a signal to the SETOWN'd process group. If you don't do those things, no signals will be sent, regardless of whether you use signal(2) or sigaction(2)

Solved by replacing references to newSockAddr with acceptSockAddr. Here is the current code, now malfunctioning in new and terrific ways!:
server.cpp:
#include <sys/types.h> // socket, bind
#include <sys/socket.h> // socket, bind, listen, inet_ntoa
#include <netinet/in.h> // htonl, htons, inet_ntoa
#include <arpa/inet.h> // inet_ntoa
#include <netdb.h> // gethostbyname
#include <unistd.h> // read, write, close
#include <string.h> // bzero
#include <netinet/tcp.h> // SO_REUSEADDR
#include <sys/uio.h> // writev
#include <signal.h> // sigaction
#include <sys/time.h> // gettimeofday
#include <unistd.h> // write
#include <fcntl.h> // fcntl
#include <iostream> // cout
using namespace std;
#define BUFSIZE 1500
#define MAX_PENDING 5
#define SIZEOFINT 4
// Globals
int nreps;
int nbufs;
int newSd;
// Read all the data from the client and output how long it took
void readFromClient(int sig, siginfo_t *siginfo, void *context)
{
cout << "readFromClient triggered!" << endl;
// Declare data buffer
char databuf[BUFSIZE];
// Record start time
struct timeval theTime;
gettimeofday(&theTime, NULL);
int startTime = theTime.tv_usec + theTime.tv_sec * 1000000;
// Keep reading until the buffer is full
int nRead = 0;
while((nRead += read(newSd, databuf, BUFSIZE - nRead)) < BUFSIZE)
{
cout << "nRead now: " << nRead << endl;
}
// For testing single byte read
/*
cout << "Reading a byte... " << endl;
char bytebuf[1];
read(newSd, bytebuf, 1);
cout << "SUCCESS" << endl;
*/
// Record finish time
gettimeofday(&theTime, NULL);
int finishTime = theTime.tv_usec + theTime.tv_sec * 1000000;
// Calculate the receiving time
int receiveTime = finishTime - startTime;
// Display the receiving time
cout << "data-receiving time = " << receiveTime << " usec" << endl;
// Tell the client how much data was read
cout << "Writing amount read... " << endl;
write(newSd, (void*)nRead, SIZEOFINT);
cout << "SUCCESS" << endl;
// Close the socket
cout << "Closing socket... " << endl;
close(newSd);
cout << "SUCCESS" << endl;
}
int main(int argc, char *argv[])
{
// Store command line arguments
int port = atoi(argv[1]);
int nreps = atoi(argv[2]);
int nbufs = atoi(argv[3]);
// Declare a socket
struct sockaddr_in acceptSockAddr;
socklen_t len = sizeof(acceptSockAddr);
memset((char*)&acceptSockAddr, '\0', sizeof(acceptSockAddr));
acceptSockAddr.sin_family = AF_INET; // Address Family Internet
acceptSockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
acceptSockAddr.sin_port = htons(port); // convert host byte-order
// Open a stream-oriented socket
int serverSd;
if((serverSd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket failure");
exit(1);
}
// Signal OS to reuse this port once server closes
const int on = 1;
setsockopt(serverSd, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(int));
// Bind socket to local address
if(bind(serverSd, (sockaddr*)&acceptSockAddr, sizeof(acceptSockAddr)) < 0)
{
perror("bind failure");
exit(1);
}
// Instruct OS to listen for up to 5 clients
listen(serverSd, MAX_PENDING);
// Set up signal handler for IO from client
struct sigaction action;
memset(&action, '\0', sizeof(action));
action.sa_sigaction = &readFromClient;
action.sa_flags = SA_SIGINFO;
//fcntl(newSd, F_SETSIG, SIGIO); // Fixes problem with si_fd
if(sigaction(SIGIO, &action, NULL) < 0)
{
perror("sigaction");
exit(1);
}
while(1) // sleep forever
{
cout << "Waiting for client... " << endl;
if((newSd = accept(serverSd, (struct sockaddr*)&acceptSockAddr, &len)) < 0)
{
perror("accept failure");
//exit(1);
}
cout << "SUCCESS" << endl;
fcntl(newSd, F_SETOWN, getpid());
fcntl(newSd, F_SETFL, FASYNC);
}
return 0;
}
client.cpp:
#include <sys/types.h> // socket, bind
#include <sys/socket.h> // socket, bind, listen, inet_ntoa
#include <netinet/in.h> // htonl, htons, inet_ntoa
#include <arpa/inet.h> // inet_ntoa
#include <netdb.h> // gethostbyname
#include <unistd.h> // read, write, close
#include <string.h> // bzero
#include <netinet/tcp.h> // SO_REUSEADDR
#include <sys/uio.h> // writev
#include <signal.h> // sigaction
#include <sys/time.h> // gettimeofday
#include <fcntl.h> // fcntl
#include <iostream> // cout
using namespace std;
#define BUFSIZE 1500
#define SIZEOFINT 4
int main(int argc, char *argv[])
{
// Store commmand line arguments
int server_port = atoi(argv[1]);
int nreps = atoi(argv[2]);
int nbufs = atoi(argv[3]);
int bufsize = atoi(argv[4]);
const char* server_name = argv[5];
int testType = atoi(argv[6]);
// Check to ensure proper buffer count/sizes
if(nbufs * bufsize != BUFSIZE)
{
perror("nbufs times bufsize must equal BUFSIZE");
exit(1);
}
if(testType < 1 || testType > 3)
{
perror("test type must be 1, 2, or 3");
exit(1);
}
// Create buffers
char databuf[nbufs][bufsize];
// Retrieve hostent structure
struct hostent* host = gethostbyname(server_name);
if(!host)
{
perror("unknown hostname");
exit(1);
}
// Declare socket structure
sockaddr_in sendSockAddr;
memset((char*)&sendSockAddr, '\0', sizeof(sendSockAddr));
sendSockAddr.sin_family = AF_INET; // Address Family Internet
sendSockAddr.sin_addr.s_addr = inet_addr(inet_ntoa(*(struct in_addr*)*host->h_addr_list));
sendSockAddr.sin_port = htons(server_port); // convert host byte-order
// Open stream-oriented socket
int clientSd;
if((clientSd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
{
perror("socket failure");
exit(1);
};
// Connect socket to server
if(connect(clientSd, (struct sockaddr*)&sendSockAddr, sizeof(sendSockAddr)) < 0)
{
perror("connect failure");
exit(1);
};
// Record start time
struct timeval theTime;
gettimeofday(&theTime, NULL);
int startTime = theTime.tv_usec + theTime.tv_sec * 1000000;
// Conduct tests
for(int i = 0; i < nreps; i++)
{
switch(testType)
{
case 1:
{
// Multiple write test
cout << "Running multiple write test" << endl;
for(int j = 0; j < nbufs; j++)
{
cout << "Writing buffer " << j << "... " << endl;
write(clientSd, databuf[j], bufsize);
cout << "SUCCESS" << endl;
}
cout << "Finished multiple write test" << endl;
}
case 2:
{
// Vector write test
cout << "Running vector write test" << endl;
struct iovec vector[nbufs];
for(int j = 0; j < nbufs; j++)
{
vector[j].iov_base = databuf[j];
vector[j].iov_len = bufsize;
}
cout << "Writing vector... " << endl;
writev(clientSd, vector, nbufs);
cout << "SUCCESS" << endl;
cout << "Finished vector write test" << endl;
}
case 3:
{
// Single write test
cout << "Running single write test" << endl;
cout << "Writing... ";
write(clientSd, databuf, nbufs * bufsize);
cout << "SUCCESS" << endl;
// For testing single byte write
/*
cout << "writing a byte..." << endl;
char singleByte[1];
write(clientSd, singleByte, 1);
cout << "wrote a byte!" << endl;
*/
cout << "Finished single write test" << endl;
}
}
}
// Record finish time
gettimeofday(&theTime, NULL);
int finishTime = theTime.tv_usec + theTime.tv_sec * 1000000;
// Calculate the sending time
int sendTime = finishTime - startTime;
// Receive number of bytes read from server
int nReads = 0;
cout << "reading nReads from server... " << endl;
read(clientSd, (void*)nReads, SIZEOFINT);
cout << "SUCCESS" << endl;
// Record read time
gettimeofday(&theTime, NULL);
int readTime = theTime.tv_usec + theTime.tv_sec * 1000000;
// Calculate the round-trip time
int roundTime = readTime - startTime;
// Display data sending statistics
cout << "Test " << testType << ": data-sending time = " << sendTime;
cout << " usec, round-trip time = " << roundTime << " usec, # reads = ";
cout << nReads << endl;
// Close the socket
cout << "Closing the socket... " << endl;
close(clientSd);
cout << "SUCCESS" << endl;
cout << "Exiting!" << endl;
return 0;
}
There are still severe problems when attempting to establish a second client connection to the server after closing the first one.

Related

Why does server output for separate clients go to the same terminal?

I'm working on a client-server application for my Operating Systems class that is supposed to simulate sales of airplane tickets. We are directed to make it so that the main thread on the TCP server is listening for incoming connections and then, as soon as we receive a client connection, creates a new thread to handle that connection. After a lot of initial confusion, I believe I have the program in a mostly-functioning state.
The problem I'm having now is that when I run all the clients from separate terminals (whether it be 2 or 5 or any other number), all the output from the server comes into the most recent terminal that I have launched it on. This isn't a huge deal in and of itself but it also means that when I use Ctrl+C to close the process running on that last terminal, it exits all clients from the server (which is a problem).
So my questions are:
1. Why is all the output from the server being directed to a single terminal rather than sending the responses to the terminal that each client process was launched from?
2. Why do all clients quit as soon as I end the process in terminal 5?
Picture of the terminals for all the clients and the server (may have to open in new tab to see everything).
Server.cpp (Needs my other class Plane.cpp to compile which I can provide if needed, but I don't think any code in there is relevant to the issue I'm facing):
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <string>
#include <sstream>
#include <stdlib.h>
#include <pthread.h>
#include "Plane.h"
using namespace std;
// default plane sizing
const int DEFAULT_ROWS = 26;
const int DEFAULT_COLUMNS = 6;
// Set up global variables for threads to access (yikes)
int rows, cols;
Plane* plane;
pthread_mutex_t mutexL = PTHREAD_MUTEX_INITIALIZER;
static int clientSocket;
int connections = 0;
void *connection_handler(void*);
struct argList {
string arg;
int row, col;
};
bool argParser(string input, argList &argL) {
stringstream ss;
ss << input;
try {
ss >> argL.arg >> argL.row >> argL.col;
} catch (exception e) {
cout << "Invalid arguments\n";
return false;
}
return true;
}
string purchaseTicket(int row, int col) {
string output;
// lock this section before we use shared resource
pthread_mutex_lock(&mutexL);
cout << "Mutex locked\n";
if (plane->isAvailable(row, col)) {
plane->buyTicket(row, col);
output = "Successfully purchased ticket for row: " + to_string(row) + ", column: " + to_string(col) + "\n";
} else {
if (row > plane->getNumRows() || row < 0 || col > plane->getNumCols() || col < 0) {
output = "Invalid seat location!\n";
} else {
output = "Seat unavailable!\n";
}
}
pthread_mutex_unlock(&mutexL);
cout << "Mutex unlocked\n";
// unlock when we're done
return output;
}
string convertMatrix(Plane plane) {
char** tempMatrix = plane.getSeatMatrix();
string seats = "";
for (int i = 0; i < plane.getNumRows(); i++) {
seats += tempMatrix[i];
seats += "\n";
}
return seats;
}
// arguments to run: column row
int main(int argc, char* argv[]) {
// array of threads (thread pool)
pthread_t threads[5];
if (argc < 3) {
rows = DEFAULT_ROWS;
cols = DEFAULT_COLUMNS;
plane = new Plane(rows, cols);
} else if (argc == 3) {
rows = atoi(argv[1]);
cols = atoi(argv[2]);
plane = new Plane(rows, cols);
} else {
cout << "Only 2 arguments allowed. You entered [" << argc << "]\n";
return -1;
}
// Create socket
int listen_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (listen_sock == -1) {
cerr << "Failed to create socket\n";
return -1;
}
// Socket hint stuff
sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons(54000);
inet_pton(AF_INET, "0.0.0.0", &hint.sin_addr);
// Bind socket to IP and port
if (bind(listen_sock, (sockaddr*)&hint, sizeof(hint)) < 0) {
cerr << "Binding to IP/Port failed\n";
return -2;
}
// Mark the socket for listening
if (listen(listen_sock, SOMAXCONN) == -1) {
cerr << "Can't listen";
return -3;
}
char host[NI_MAXHOST];
char service[NI_MAXSERV];
int numThread = 0;
while (numThread < 5) {
cout << "Listening for connections...\n";
sockaddr_in client;
socklen_t clientSize = sizeof(client);
// accept connections
clientSocket = accept(listen_sock, (sockaddr*)&client, &clientSize);
// if connection failed
if (clientSocket == -1) {
cerr << "Failed to connect with client";
return -4;
} else {
cout << "Connection successful\n";
connections++;
}
pthread_create(&threads[numThread], NULL, connection_handler, (void*) &clientSocket);
// 0 out used memory
memset(host, 0, NI_MAXHOST);
memset(service, 0, NI_MAXSERV);
int result = getnameinfo((sockaddr*)&client,
sizeof(client),
host,
NI_MAXHOST,
service,
NI_MAXSERV,
0);
if (result) {
cout << host << " connected on " << service << endl;
} else {
inet_ntop(AF_INET, &client.sin_addr, host, NI_MAXHOST);
cout << host << " connected on " << ntohs(client.sin_port) << endl;
}
numThread++;
}
// join threads together
for (int i = 0; i < numThread; i++) {
pthread_join(threads[i], NULL);
}
return 0;
}
void *connection_handler(void* listen_sock) {
cout << "Thread No: " << pthread_self() << "\n-----\n";
const int clientID = connections;
// necessary variables for processing
char buff[4096];
string custMsg;
custMsg += to_string(rows) + " " + to_string(cols) + "\n";
int msgSize = strlen(custMsg.c_str())*sizeof(char);
send(clientSocket, custMsg.c_str(), msgSize+1, 0);
// Determine what we do when we receieve messages
bool firstMsg = true;
while (true) {
memset(buff, 0, 4096);
custMsg = "";
int bytesRecv = recv(clientSocket, buff, 4096, 0);
if (bytesRecv == -1) {
pthread_mutex_lock(&mutexL);
cerr << "There was a connection issue (client " << clientID << ")\n";
pthread_mutex_unlock(&mutexL);
break;
} else if (bytesRecv == 0) {
pthread_mutex_lock(&mutexL);
cout << "Client " << clientID << " disconnected" << endl;
pthread_mutex_unlock(&mutexL);
}
if (bytesRecv > 0)
cout << "Received: " << string(buff, 0, bytesRecv) << " (client " << clientID << ")\n";
// do things based on user input
string inputStr(buff);
argList args;
if (argParser(inputStr, args)) {
if (args.arg == "buy") {
string purchResult = purchaseTicket(args.row, args.col);
custMsg += purchResult;
cout << purchResult << "------\n";
} else {
custMsg = "To buy a ticket, enter: 'buy <row> <col>'\n";
}
} else {
custMsg = "Invalid argument list";
}
//custMsg += convertMatrix(*plane);
int msgSize = strlen(custMsg.c_str())*sizeof(char);
//cout << custMsg << "\n";
cout << "Responding to client: " << clientID << "\n";
send(clientSocket, custMsg.c_str(), msgSize+1, 0);
}
// Close socket
close(clientSocket);
return 0;
}
Client.cpp:
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <fstream>
#include <sstream>
#include <time.h>
using namespace std;
struct serverInfo {
string ipAddr;
int portNum;
int timeout;
};
int getRand(int max) {
return rand() % max;
}
bool getPlaneInfo(string line, int& rows, int& cols) {
stringstream ss;
ss << line;
try {
ss >> rows >> cols;
return true;
} catch (exception e) {
cout << "Critical error\n";
return false;
}
}
void getServerInfo(ifstream &serverCfg, serverInfo &conn_serv) {
// variables that we'll read into
string label, val, eq;
int i = 0;
try { // for conversion errors
while (serverCfg >> label >> eq >> val) {
if (i == 0)
conn_serv.ipAddr = val;
else if (i == 1)
conn_serv.portNum = stoi(val);
else if (i == 2)
conn_serv.timeout = stoi(val);
else
break;
i++;
}
} catch (exception e) {
e.what();
}
}
// arguments being sent in should be 'automatic' or 'manual' for method of purchasing
// followed by the .ini file containing the server connection info.
int main(int argc, char* argv[]) {
srand(time(NULL));
// we get these int variables from the first server response
int rows, cols;
bool AUTOMATIC = false;
// make sure arguments are present and valid
if (argc != 3) {
cout << "Invalid number of arguments. Exiting...\n";
}
if (strncmp(argv[1],"automatic", 9) != 0 && strncmp(argv[1],"manual", 6) != 0) {
cout << "Invlaid arguments! Please use 'manual' or 'automatic'. Exiting...\n";
return -1;
}
// check to see if they want automatic ticket purchasing
if (strncmp(argv[1], "automatic", 9) == 0) {
AUTOMATIC = true;
}
// Handle file processing in getServerInfo function
string fileName = argv[2];
ifstream SERVER_CFG;
SERVER_CFG.open(fileName);
// store values from file in conn_info
serverInfo conn_info;
if(SERVER_CFG) {
getServerInfo(SERVER_CFG, conn_info);
} else {
cout << "Invalid filename. Exiting...\n";
return -2;
}
SERVER_CFG.close();
// create socket
int conn_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (conn_sock < 0) {
cout << "\nFailed to Create Socket. Exiting...\n";
return -3;
}
// get port and ipAddr information that we read from file
int port = conn_info.portNum;
string ipAddr = conn_info.ipAddr;
// make hint
sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons(port);
inet_pton(AF_INET, ipAddr.c_str(), &hint.sin_addr);
// try to connect to server socket i times where i is conn_info.timeout
for (int i = 0; i < conn_info.timeout; i++) {
int connectVal = connect(conn_sock, (sockaddr*) &hint, sizeof(sockaddr_in));
if (connectVal < 0 && i >= conn_info.timeout-1) {
cout << "Failed to connect (" << (i+1) << ")\n";
cout << "Failed to connect after " << (i+1) << " attempts. Exiting.\n";
return -4;
} else if (connectVal == 0) {
break;
}
cout << "Failed to connect (" << (i+1) << ")\n";
sleep(1);
}
char buff[4096];
string userInput;
bool firstMsg = true;
bool needGreet = true;
do {
userInput = "";
int sendResult;
// Send a greeting message to the server to get plane info
if (needGreet) {
userInput = "Greeting the server";
send(conn_sock, userInput.c_str(), userInput.size() + 1, 0);
needGreet = false;
continue;
}
if (AUTOMATIC && !firstMsg) {
int row = getRand(20);
int col = getRand(6);
userInput = string("buy ") + to_string(row) + " " + to_string(col);
cout << "Sending request to buy seat " << row << " " << col << "\n";
sleep(1);
} else { // get input if its manual
if (!firstMsg) {
cout << "> ";
getline(cin, userInput);
}
}
// send to server
sendResult = send(conn_sock, userInput.c_str(), userInput.size() + 1, 0);
// check if sent successfully
if (sendResult < 0) { // connection error
cout << "Failed to send to server\r\n";
continue;
}
// wait for response
memset(buff, 0, 4096);
int bytesReceived = recv(conn_sock, buff, 4096, 0);
// print response
cout << "Server> " << string(buff, bytesReceived) << "\r\n";
if (firstMsg) {
string planeInf(string(buff,bytesReceived));
if (getPlaneInfo(planeInf, rows, cols)) {
cout << "Rows: " << rows << ", Columns: " << cols << "\n";
} else {
return -5;
}
firstMsg = false;
}
} while (true);
// closing socket
close(conn_sock);
return 0;
}
Any help is greatly appreciated.
The problem is your use of global variables.
Your connection thread writes a response to clientSocket, which your main changes with every connection. Every thread will write to the same socket.
You need to create a class to hold data specific to each connection, and pass a new one of those to each thread. Do not use shared global data to hold thread-specific values.

Multiple connections sockets c++

I'm trying to implement my own server and client side which uses sockets to send and receive data. But i got some problem with realization of multi-threading.
My server.cpp:
#include <iostream>
#include <netinet/in.h>
#include <cstring>
#include <arpa/inet.h>
#include <unistd.h>
#include <thread>
using namespace std;
void connection_handler(int socket) {
char client_message[256];
memset(&client_message, 0, 256);
size_t message_size = 0;
while ((message_size = recv(socket, client_message, sizeof(client_message) - 1, 0)) > 0) {
client_message[message_size] = '\0';
cout << "[Server] Client message accepted" << endl;
cout << "[Server] Client message: " << client_message << endl;
if (write(socket, client_message, message_size) == -1) {
cout << "[Client] Message sending failed" << endl;
return;
}
cout << "[Server] Message sent to client" << endl << endl;
cout << "============================" << endl << endl;
cout.flush();
memset(&client_message, 0, 256);
}
}
int main() {
unsigned short int PORT = 8080;
int listener, client_socket;
socklen_t client_len;
struct sockaddr_in server_address{};
memset(&server_address, 0, sizeof(server_address));
listener = socket(AF_INET, SOCK_STREAM, 0);
server_address.sin_family = AF_INET;
server_address.sin_port = htons(PORT);
if (inet_aton("127.0.0.1", &server_address.sin_addr) == 0) {
cout << "[Server] Invalid IP address" << endl;
return -1;
}
if (bind(listener, (struct sockaddr*) &server_address, sizeof(server_address)) == -1) {
cout << "[Server] Binding failed" << endl;
return -1;
}
cout << "[Server] All setting are done" << endl;
cout << "[Server] Server enabled" << endl;
if (listen(listener, 100) == -1) {
cout << "[Server] Listening failed" << endl;
return -1;
}
cout << "[Server] Waiting for connection..." << endl;
for (; ;) {
client_socket = accept(listener, (struct sockaddr*) &server_address, &client_len);
cout << "[Server] Connection accepted" << endl << endl;
cout << "----------------------------" << endl << endl;
int new_socket = client_socket;
thread handling_thread(connection_handler, new_socket);
handling_thread.detach();
}
}
My client.cpp:
#include <iostream>
#include <netinet/in.h>
#include <cstring>
#include <arpa/inet.h>
#include <unistd.h>
using namespace std;
int main() {
unsigned short int PORT = 8080;
int sockfd;
char buffer[256] = {0};
struct sockaddr_in server_address{};
sockfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&server_address, '0', sizeof(server_address));
server_address.sin_family = AF_INET;
server_address.sin_port = htons(PORT);
server_address.sin_addr.s_addr = INADDR_ANY;
if (connect(sockfd, (struct sockaddr*) &server_address, sizeof(server_address)) < 0) {
cout << "[Client] Connection failed" << endl;
return -1;
}
cout << "[Client] All setting are done" << endl;
cout << "[Client] Succefully connected to server" << endl << endl;
cout << "----------------------------" << endl << endl;
while (true) {
string client_request;
cout << "[Client] Enter a message: ";
getline(cin, client_request);
if (client_request == "-1") {
write(sockfd, client_request.c_str(), client_request.size());
close(sockfd);
cout << endl << "[Client] Client exited" << endl;
return 0;
}
if (write(sockfd, client_request.c_str(), client_request.size()) == -1) {
cout << "[Client] Message sending failed" << endl;
}
cout << "[Client] Message sent to server" << endl;
memset(&buffer, 0, 256);
read(sockfd, buffer, 256);
cout << "[Client] Server message: " << buffer << endl << endl;
cout << "============================" << endl << endl;
cout.flush();
}
}
It's perfectly working until i create one more connection to server and after that second client cans send and receive data, but first one at this time becomes not working.
I compiled my program like this: g++ server.cpp -lpthread -o server -std=c++11 And then in other console tab run my compiled client.cpp: ./client.
To check multi-threading working i run client one more time (in other tab again) and trying send requests in two tabs at the same time.
I want to realize multi-threading in my program. How can i do this?
UPD: I'm using Linux
UPD2: Problem solved. Fixed code there.
int new_socket = client_socket;
thread handling_thread(connection_handler, &new_socket);
handling_thread.detach();
}
This initializes new_socket, which gets declared in local scope inside this for loop, then passes the pointer to this new_socket to a new thread that gets started, and detached. Immediately after that, this for loop iteration ends, which destroys the new_socket object, before starting the next iteration of this loop.
Meanwhile, the execution thread repeatedly attempts to dereference the int * it receives, which now points to a destroyed object. This results in undefined behavior, and the likely reason your program is "not working".
The most simple solution is to create the int socket value in dynamic scope, using new, and then pass the pointer to this newed socket value to the execution thread. The execution thread will, of course, be responsible for retrieving the socket value, then properly deleteing it, to avoid leaking memory.
This should be sufficient for this simple program. More complicated programs will likely require slightly more sophisticated socket and dynamic scoping handling logic, for reliability.

C++ WebSocket in macOS App

I'm trying to build a little Chat App just between the Server and the Client. I stumbled across this YouTube video (GitHub source).
If I run both scripts everything works fine but if I try to put the Client-Side into an macOS Application the Client says that it has connected even though the Server isn't running.
Client.cpp
#include <iostream>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include "Client.hpp"
using namespace std;
void connectToServer()
{
int client;
int portNum = 1600; // NOTE that the port number is same for both client and server
bool isExit = false;
int bufsize = 1024;
char buffer[bufsize];
char* ip = "127.0.0.1";
struct sockaddr_in server_addr;
client = socket(AF_INET, SOCK_STREAM, 0);
if (client < 0)
{
cout << "\nError establishing socket..." << endl;
exit(1);
}
cout << "\n=> Socket client has been created..." << endl;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(portNum);
if (connect(client,(struct sockaddr *)&server_addr, sizeof(server_addr)) == 0)
cout << "=> Connection to the server port number: " << portNum << endl;
cout << "=> Awaiting confirmation from the server..." << endl; //line 40
recv(client, buffer, bufsize, 0);
std::cout << client << std::endl;
std::cout << buffer << std::endl;
cout << "=> Connection confirmed, you are good to go...";
cout << "\n\n=> Enter # to end the connection\n" << endl;
// Once it reaches here, the client can send a message first.
do {
cout << "Client: ";
do {
cin >> buffer;
send(client, buffer, bufsize, 0);
if (*buffer == '#') {
send(client, buffer, bufsize, 0);
*buffer = '*';
isExit = true;
}
} while (*buffer != 42);
cout << "Server: ";
do {
recv(client, buffer, bufsize, 0);
cout << buffer << " ";
if (*buffer == '#') {
*buffer = '*';
isExit = true;
}
} while (*buffer != 42);
cout << endl;
} while (!isExit);
cout << "\n=> Connection terminated.\nGoodbye...\n";
close(client);
}
ViewController.mm
- (void)viewDidLoad {
[super viewDidLoad];
connectToServer();
}
Console Output:
=> Socket client has been created...
=> Awaiting confirmation from the server...
=> Connection confirmed, you are good to go...
=> Enter # to end the connection
Client: test *
Server: *
Client:

C++ Sockets - Server doesn't accept multiple clients (linux)

I have working server and client code. The server and client can connect and chat with each other correctly. But when I open another client terminal, the client is says Awaiting confirmation from the server and nothing else. Although server and client #1 can still chat.
I searched on multi-threading but the examples or code snippets they show is advanced. Maybe a little explanation or an example will help a lot!
The code below is working. I have a working server but it only accepts one connection. How do I make the server to allow multiple connection? So that I can make the program look like a group chat.
client.cpp (when client #2 connects, the code freezes at line 40)
#include <iostream>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdlib.h>
#include <unistd.h>
using namespace std;
int main()
{
char a;
int client;
int portNum = 1500;
int bufsize = 1024;
char* buffer = new char[bufsize];
bool isExit = false;
char* ip = "127.0.0.1";
struct sockaddr_in direc;
if ((client = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
cout << "\nError creating socket..." << endl;
exit(0);
}
cout << "\nSocket created successfully..." << endl;
direc.sin_family = AF_INET;
direc.sin_port = htons(portNum);
inet_pton(AF_INET, ip, &direc.sin_addr);
if (connect(client,(struct sockaddr *)&direc, sizeof(direc)) == 0)
cout << "Connection to the server " << inet_ntoa(direc.sin_addr) << endl;
cout << "Awaiting confirmation from the server..." << endl; //line 40
recv(client, buffer, bufsize, 0);
cout << "\n=> Enter # to terminate the connection\n" << endl;
do {
cout << "Client: ";
do {
cin >> buffer;
send(client, buffer, bufsize, 0);
if (*buffer == '#') {
send(client, buffer, bufsize, 0);
*buffer = '*';
isExit = true;
}
} while (*buffer != 42);
cout << "Server: ";
do {
recv(client, buffer, bufsize, 0);
cout << buffer << " ";
if (*buffer == '#') {
*buffer = '*';
isExit = true;
}
} while (*buffer != 42);
cout << endl;
} while (!isExit);
cout << "=> Connection terminated.\nGoodbye";
close(client);
return 0;
}
server.cpp
#include <iostream>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <unistd.h>
using namespace std;
int main()
{
int client, server;
int bufsize = 1024;
int portNum = 1500;
bool isExit = false;
char* buffer = new char[bufsize];
struct sockaddr_in direc;
socklen_t tamano;
pid_t pid;
if ((client = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
cout << "\nError establishing socket..." << endl;
exit(1);
}
cout << "\nSocket server has been created..." << endl;
direc.sin_family = AF_INET;
direc.sin_addr.s_addr = htons(INADDR_ANY);
direc.sin_port = htons(portNum);
if ((bind(client, (struct sockaddr*)&direc,sizeof(direc))) < 0) {
cout << "\nError binding connection..." << endl;
return -1;
}
tamano = sizeof(direc);
cout << "Looking for clients..." << endl;
listen(client, 1);
while ((server = accept(client,(struct sockaddr *)&direc,&tamano)) > 0) {
strcpy(buffer, "Server connected...\n");
send(server, buffer, bufsize, 0);
cout << "Connected with the client, you are good to go..." << endl;
cout << "Enter # to end the connection\n" << endl;
cout << "Client: ";
do {
recv(server, buffer, bufsize, 0);
cout << buffer << " ";
if (*buffer == '#') {
*buffer = '*';
isExit = true;
}
} while (*buffer != '*');
do {
cout << "\nServer: ";
do {
cin >> buffer;
send(server, buffer, bufsize, 0);
if (*buffer == '#') {
send(server, buffer, bufsize, 0);
*buffer = '*';
isExit = true;
}
} while (*buffer != '*');
cout << "Client: ";
do {
recv(server, buffer, bufsize, 0);
cout << buffer << " ";
if (*buffer == '#') {
*buffer == '*';
isExit = true;
}
} while (*buffer != '*');
} while (!isExit);
cout << "\n=> Connection terminated... " << inet_ntoa(direc.sin_addr);
close(server);
cout << "\nGoodbye..." << endl;
isExit = false;
}
close(client);
return 0;
}
How do I make the server accept multiple connection?
Thanks!
In order to properly support multiple connections you should fire up a new thread for each incoming connection. Each new connection is identified by its own unique socket descriptor returned by accept(). A simple example:
while ((accepted = accept(client,(struct sockaddr *)&direc,&tamano)) > 0) {
/*Create the thread and pass the socket descriptor*/
if( pthread_create(new_thread, &thread_attributes, &handle_tcp_connection, (void *)accepted) != 0){
perror("create thread");
exit(EXIT_FAILURE);
}
}
You will need to use select or poll and a state machine pattern to do what you want to do. This means that you will need to process the data as it comes in from whichever client is sending it. Take a look here for a working example.

Passing Object over Server to client

I am trying to create a client server application in linux. Server is supposed to send one object to all connected clients.
Here is the code for it.
In this When server send object, everything remains ok at server side but Segmentation fault occurs on client server immediately it receive it.
Server:
#include "Question.h"
#include <iostream>
#include <string.h>
using namespace std;
#include<sys/socket.h>
#include<sys/types.h>
#include<stdio.h>
#include<arpa/inet.h>
#include<time.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <netinet/in.h>
int main() {
Question q1("Capital Of Pakistan?", "Lahore", "Karachi", "Quetaa", "Islamabad");
int socketID = 0, clientID[10] = {0}, totalClients = 3;
char sendBuffer[1024];
memset(sendBuffer, '0', sizeof(sendBuffer));
time_t time;
struct sockaddr_in servAddr;
cout << "Question is: \n" << q1.getQuestion()<<endl;
cout << q1.getOpt1() << endl << q1.getOpt2() << endl << q1.getOpt3() << endl << q1.getCorrect()<<endl;
cout << "\n\n --- Server starting up --- \n\n";
/*
* Creating Socket
*/
socketID = socket(AF_INET, SOCK_STREAM, 0);
if (socketID == -1) {
cerr << " Can't create Socket";
}
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(5000);
servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
/*
* Binding IP
*/
int bindID;
bindID = bind(socketID, (struct sockaddr *) &servAddr, sizeof(servAddr)); // Casting sockaddr_in on sockaddr and binding it with socket id
if (bindID != -1) {
cout << " Bind SucessFull";
listen(socketID, 5);
cout << " Server Waiting for connections" << endl;
int i = 0;
while (1) {
clientID[i] = accept(socketID, (struct sockaddr *) NULL, NULL);
cout << "Got Client: " << i+1 << ","<<totalClients-(i+1)<<" to go" << endl;
cout << "ID: " << clientID[i]<<endl;
cout.flush();
snprintf(sendBuffer, sizeof(sendBuffer), "%.24s\n", ctime(&time));
write(clientID[i], sendBuffer, strlen(sendBuffer));
i++;
if (i >= totalClients)
break;
sleep(1);
}
cout << "Sending Question to All Clients...." << endl;
for(int j=0; j<totalClients; j++) {
cout << "Sending to ID " << clientID[j]<<endl;
write(clientID[j], &q1 , sizeof(q1));
cout << "Sent " << j << "...." << endl;
}
/*
* Closing all clients
*/
for (int k = 0; k < totalClients; k++) {
close(clientID[k]);
}
} else {
cerr << " Unable to Bind";
}
return 0;
}
Client:
#include "Question.h"
#include <iostream>
#include <string.h>
using namespace std;
#include<sys/socket.h>
#include<sys/types.h>
#include<stdio.h>
#include<arpa/inet.h>
#include<time.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
int main(int argc, char *argv[])
{
int socketID = 0 /*Socket Descriptor*/, n = 0;
char recvBuffer[1024];
memset(recvBuffer, '0',sizeof(recvBuffer));
struct sockaddr_in servAddr;
if(argc!=2){
cout << "\n Usage: %s <ip of server> \n",argv[0];
return 1;
}
socketID = socket(AF_INET, SOCK_STREAM, 0);
if(socketID == -1){
cerr << "\n Can't create socket \n";
return 1;
}
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(5000);
if(inet_pton(AF_INET, argv[1], &servAddr.sin_addr)==-1){
cerr << "\n Unable to convert given IP to Network Form \n inet_pton Error";
return 1;
}
int connectFlag;
connectFlag = connect(socketID, (struct sockaddr *)&servAddr, sizeof(servAddr));
if(connectFlag == -1){
cout << " Connection Failed" << endl;
return 1;
}
n = read(socketID, recvBuffer, sizeof(recvBuffer)-1);
recvBuffer[n] = 0;
cout << recvBuffer << endl;
if(n < 0){
cerr << "Buffer Read error\n";
}
Question q1;
cout << "Gonna Receive Connections"<<endl;
q1.setAll("0","0","0","0","0");
cout.flush();
n = read(socketID, &q1, sizeof(q1));
cout << n << endl;
cout << "Received Question " << endl;
cout << "Question is: \n" << q1.getQuestion()<<endl;
cout << q1.getOpt1() << endl << q1.getOpt2() << endl << q1.getOpt3() << endl << q1.getCorrect()<<endl;
cout.flush();
return 0;
}
Question.h
#ifndef QUESTION_H_
#define QUESTION_H_
#include <iostream>
using namespace std;
class Question {
private:
string question;
string opt1;
string opt2;
string opt3;
string correct;
public:
/*
* Constructors
*/
Question();
Question(string, string, string, string, string);
void setAll(string, string, string, string, string);
string getCorrect() const;
void setCorrect(string correct);
string getOpt1() const;
void setOpt1(string opt1);
string getOpt2() const;
void setOpt2(string opt2);
string getOpt3() const;
void setOpt3(string opt3);
void setQuestion(string question);
string getQuestion() const;
};
#endif /* QUESTION_H_ */
You will need to "serialize" your object. This usually involves making it into a string that can be read at the other "side" of whatever thing you are sending the object through.
It is exactly the same issue as if you were to write the data to a file, you don't want to store the OBJECT, you want to store the "payload" or "content" of that is inside the class.
You can use stringstream to form a long string of your data, and pass the string formed from that.
Something like:
class A
{
int x;
string s;
};
...
class A a;
stringstream ss;
ss << a.x << ", " << a.s << endl;
....
write(clientID[j], ss.str.c_str(), ss.str.length());
You will obviously need to parse the resulting string at the other end - and , may not be the ideal separator. Feel free to use something else...