I am building a server using Sockets on Linux, and I'm having a weird problem: if I try to send a request via my browser (Chrome), my code will create two sockets for communication using accept. Here's the code that accepts a connection:
server->poll();
if (server->canRead(server))
{
cout << "Connection!" << endl;
newSocket = server->accept(poolSize);
}
And here are the functions used (which are part of a wrapper I wrote for the C socket library).
int ServerSocket::poll(int timeout)
{
int rc = ::poll(this->descriptors, this->maxDescriptors, timeout);
if(rc == -1)
throw std::runtime_error(std::string("Error: ") + strerror(errno));
return rc;
}
bool ServerSocket::canRead(Socket *socket)
{
if(socket == NULL)
throw std::invalid_argument(std::string("Error: null socket!"));
for (int i = 0; i < this->maxDescriptors; i++)
{
if (socket->socketDescriptor == this->descriptors[i].fd)
{
if (this->descriptors[i].revents & POLLIN)
return true;
break;
}
}
return false;
}
ClientSocket* ServerSocket::accept(const int poolSize)
{
socklen_t endpointSize = sizeof(this->endpoint);
int newDescriptor = ::accept(this->socketDescriptor,
(struct sockaddr *)&endpoint, &endpointSize);
if(newDescriptor == -1)
throw std::runtime_error(std::string("Error in accept: ") + strerror(errno));
ClientSocket *newSocket = NULL;
try
{
newSocket = new ClientSocket(newDescriptor, this->port, poolSize);
}
catch (std::exception e)
{
std::cout << "Allocation error!" << endl;
throw e;
}
return newSocket;
}
If I compile and run this code, my output is:
Connection!
Connection!
I've used both telnet and netcat to analyse the requisition made by the browser, and it shows no anomaly. I've also sent requests manually via telnet and netcat, and the server works just fine, no duplicate connections. What could be causing this behaviour?
Related
So I've been creating a winsock server/client in UE4. I can get the client to connect to the server however once the client sends the first message it seems to close the socket, preventing any further messages to be sent to the server. It also seems like the server is doing the same thing when sending data.
client
// Convert IP & port to standard lib
const std::string IP = std::string(TCHAR_TO_UTF8((*GameInstance->GetIPAddress())));
const std::string PORT = std::string(TCHAR_TO_UTF8(*GameInstance->GetPort()));
// Set the version of WSA we are using
auto Version = MAKEWORD(2, 2);
WSAData WSData;
struct addrinfo* Result = nullptr, * ptr = nullptr, hints;
int iResult; // Store Initializing results
std::string message; // Define a message to send to the server
UE_LOG(LogTemp, Log, TEXT("Starting Client"));
// Initialize WinSock
iResult = WSAStartup(Version, &WSData); // Start winsock
if(iResult != 0)
{
UE_LOG(LogTemp, Error, TEXT("Failed to initialize winsock"));
return ECreateConnectionFlag::WINSOCK_FAILED;
}
UE_LOG(LogTemp, Log, TEXT("Initialized WinSock"));
// Setup hints
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
UE_LOG(LogTemp, Log, TEXT("Connecting"));
// Get the address details
iResult = getaddrinfo(IP.c_str(), PORT.c_str(), &hints, &Result);
if(iResult != 0)
{
UE_LOG(LogTemp, Error, TEXT("Error getting address info from the server"));
WSACleanup();
return 0;
}
// Connect the player
for(ptr = Result; ptr != nullptr; ptr->ai_next)
{
GameInstance->SetPlayerSocket(socket(ptr->ai_family, ptr->ai_socktype, ptr->ai_protocol));
if(GameInstance->GetLoggedInPlayer().PlayerSocket == INVALID_SOCKET)
{
UE_LOG(LogTemp, Error, TEXT("Failed to create socket"));
WSACleanup();
return 0;
}
iResult = connect(GameInstance->GetLoggedInPlayer().PlayerSocket, ptr->ai_addr, ptr->ai_addrlen);
if(iResult == SOCKET_ERROR)
{
closesocket(GameInstance->GetLoggedInPlayer().PlayerSocket);
GameInstance->SetPlayerSocket(INVALID_SOCKET);
continue;
}
break;
}
freeaddrinfo(Result); // Release Address information as it's no longer required
// Ensure the socket is valid
if(GameInstance->GetLoggedInPlayer().PlayerSocket == INVALID_SOCKET)
{
UE_LOG(LogTemp, Error, TEXT("Unable to connect to server..."));
WSACleanup();
return 0;
}
// WE ARE CONNECTED
/* CONNECT AND SEND USERNAME */
FString SignInMessage = FString("Username-" + GameInstance->GetLoggedInPlayer().Username);
std::string ConnectionMessage = std::string(TCHAR_TO_UTF8(*SignInMessage));
iResult = send(GameInstance->GetLoggedInPlayer().PlayerSocket, ConnectionMessage.c_str(), (int)strlen(ConnectionMessage.c_str()), 0);
if(iResult <= 0)
{
int error = WSAGetLastError();
UE_LOG(LogTemp, Error, TEXT("Failed to send message: %d"), error);
return 0;
}
while(bRunThread)
{
UE_LOG(LogTemp, Log, TEXT("Receiving Data"));
/* DISCONNECT FROM SERVER */
const std::string msg = "Hello World";
iResult = send(GameInstance->GetLoggedInPlayer().PlayerSocket, msg.c_str(), (int)strlen(msg.c_str()), 0);
if(iResult <= 0)
{
int error = WSAGetLastError();
UE_LOG(LogTemp, Error, TEXT("Failed to send message: %d"), error);
return 0;
}
FPlatformProcess::Sleep(1.0f);
}
/* DISCONNECT FROM SERVER */
const std::string DisconnectMsg = "Disconnect";
iResult = send(GameInstance->GetLoggedInPlayer().PlayerSocket, DisconnectMsg.c_str(), (int)strlen(DisconnectMsg.c_str()), 0);
if(iResult <= 0)
{
int error = WSAGetLastError();
UE_LOG(LogTemp, Error, TEXT("Failed to send message: %d"), error);
return 0;
}
UE_LOG(LogTemp, Warning, TEXT("Client Disconnected"));
closesocket(GameInstance->GetLoggedInPlayer().PlayerSocket);
WSACleanup();
return 0;
}
server
WSAData wsa;
struct addrinfo hints; // Server Hint details
struct addrinfo* server = NULL; // Address info of the server
SOCKET serverSocket = INVALID_SOCKET; // Server Listening Socket
PlayerArray* Players = new PlayerArray(); // Reference to all the players in the server
LobbyArray* Lobbies = new LobbyArray(); // Reference to all the lobbies in the server
// Initialize the winsock library
std::cout << "Initializing WinSock..." << std::endl;
int WSA_Init = WSAStartup(MAKEWORD(2, 2), &wsa);
if (WSA_Init != 0)
{
std::cerr << "Error Initializing Winsock";
WSACleanup();
return;
}
else
{
std::cout << "Winsock Initialized" << std::endl;
}
// Setup Hints
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
// Setup the server
std::cout << "Setting up Server" << std::endl;
getaddrinfo(IP_ADDRESS, PORT, &hints, &server);
// Create the listening socket
std::cout << "Creating Listening socket" << std::endl;
serverSocket = socket(server->ai_family, server->ai_socktype, server->ai_protocol);
if (serverSocket == INVALID_SOCKET)
{
std::cerr << "Failed creating listening socket" << std::endl;
WSACleanup();
return;
}
else
{
std::cout << "Created listen socket" << std::endl;
}
// Set the socket to be TCP
setsockopt(serverSocket, IPPROTO_TCP, TCP_NODELAY, &OPTION_VALUE, sizeof(int));
// Bind the socket
std::cout << "Binding Socket..." << std::endl;
bind(serverSocket, server->ai_addr, (int)server->ai_addrlen);
// Start the server
std::cout << "Server has started & is listening..." << std::endl;
listen(serverSocket, SOMAXCONN);
while (true)
{
std::cout << "Players Connected: " << Players->Count() << std::endl;
SOCKET Incoming = INVALID_SOCKET; // Define a socket for anything incoming
Incoming = accept(serverSocket, NULL, NULL); // Accept the incoming message from the socket
// If the socket is not valid than continue through the loop
if (Incoming == INVALID_SOCKET)
{
std::cout << "Invalid Socket" << std::endl;
continue;
}
else
{
std::cout << "Valid Socket" << std::endl;
}
char tempmsg[DFT_BUFLEN] = ""; // Define a temp msg to store the message from the client
int received = recv(Incoming, tempmsg, DFT_BUFLEN, 0); // Receive a message from the client
std::string convertedMessage = tempmsg;
// Check that the received message is from a valid socket
if (received != SOCKET_ERROR)
{
std::string message = tempmsg; // Assing the temp message to a string to split
if (convertedMessage == "Disconnect")
{
Players->RemovePlayer(Incoming);
std::cout << "Player Disconnected..." << std::endl;
continue;
}
else
{
std::cout << tempmsg << std::endl; // === DEBUG ===
// Split the string
char* next_split;
char* split_string = strtok_s(tempmsg, "-", &next_split);
std::string FirstMsg = split_string;
if (FirstMsg == "Username")
{
std::cout << next_split << " Has joined the server" << std::endl; // Server message
// Get the player that we want to set the username to
// Create the player and add it to the server list
Player* NewPlayer = new Player();
Players->AddPlayer(NewPlayer);
NewPlayer->SetUsername(next_split); // Set the usernames
continue;
}
else if (split_string == "Lobby")
{
if (next_split == "Create")
{
Lobby* NewLobby = Lobbies->CreateLobby(); // Create a new lobby
Player* SocketPlayer = Players->GetPlayerBySocket(Incoming); // Get the player creating it by socket
// ensure that the player is valid, if so add the player to the lobby
// Otherwise send an error message to the console.
if (SocketPlayer != nullptr)
{
NewLobby->AddPlayerToLobby(SocketPlayer);
}
else
{
std::cerr << "Failed Locate player to add to lobby" << std::endl;
}
}
else if (next_split == "Destroy")
{
// TODO: Destroy Specific lobby
}
continue;
}
else
{
std::cout << "Error Reading Message" << std::endl;
}
}
}
else
{
std::cerr << "Socket Error when recieving message" << std::endl;
}
}
// Clean up the server
delete Players;
delete Lobbies;
closesocket(serverSocket);
WSACleanup();
return;
Console output after disconnecting
You are making a fundamental TCP mistake. TCP is a stream protocol., its only gurantees are
the bytes you send will be received in the same order they were sent
they will be received only once
BUT there are no 'messages' or 'records' in TCP. You can send a 100 byte message and the other end can receive
one 100 byte message
25 4 byte messages
100 1 byte messages
one 25, one 12, one 3, and one 60 (hope my math is correct)
So in the receive logic you must do this
char buffer[1000]; // or whatever
int length = ????;
char* bptr = buffer;
while(length > 0){
int recvLen = recv(sock, bptr, length,0);
if (recvLen < 1){
// error - disconnect or other failure
break;
}
bptr += recvLen;
length -= recvLen;
}
Ie keep pulling data till you have the whole message
BUT this means you need to know the messages length in advance. So either
send a well known sized length first
send fixed length messages
Or you can have a recognizable termination sequence- ie 10 byte of FF means end of message (see crlfcrlf at end of HTTP get for example)
The first option is the most robust (send length then data)
I am trying to let my c++ program communicate with simulink model via udp, and in particularly the plan is that data will be transmitted from c++ to simulink, and will be processed using some simulink models, then the result will be sent back to c++ for further programming, and data trasmissions will be realized through udp, (udp socket for c++, and udp real time send/receive block for simulink). And it will repeat a few times.
I've built a while loop, where c++ keeps reading data from a txt file and sends it to simulink via udp, which works well, the data has been multiplied by 2 and sent back towards c++, but c++ cannot receive correct data as was sent from simulink. The data it received during each cycle is "0". But if the data I sent was a constant, the receiving function in c++ works fine as well.
ifstream file_x("x1.txt");
ifstream file_y("y1.txt");
ifstream file_z("z1.txt");
double x;
double y;
double z;
int main()
{
//Local Varaiable definition
cout<<"\t\t--------------------UDP Server----------------"<<endl;
cout<<endl;
WSADATA WinsockData;
int iWsaStartup;
int iWsaCleanup;
SOCKET UDPSocketServer1;
SOCKET UDPSocketClient3;
struct sockaddr_in UDPClient1;
struct sockaddr_in UDPServer3;
char Buffer1[200];
char Buffer3[200];
int iBufferLen1 = 200+1;
int iBufferLen3 = 200+1;
int iBind3;
int iReceiveFrom;
int iSendTo;
int iUDPClientLen1 = sizeof(UDPClient1);
int iUDPClientLen2 = sizeof(UDPClient2);
int iUDPServerLen3 = sizeof(UDPServer3);
int iCloseSocket1;
int iCloseSocket3;
//STEP-1: initialization of Winsock
iWsaStartup = WSAStartup(MAKEWORD(2,2), &WinsockData);
if (iWsaStartup != 0)
{
cout<<"WSAStartUp Fun Failed!"<<endl;
}
else
{
cout<<"WSAStartUp Success"<<endl;
}
//STEP-2: Fill the UDPClient(SOCKET ADDRESS) Structure
UDPClient1.sin_family = AF_INET;
UDPClient1.sin_addr.s_addr = inet_addr("127.0.0.1");
UDPClient1.sin_port = htons(8001);
UDPServer3.sin_family = AF_INET;
UDPServer3.sin_addr.s_addr = inet_addr("127.0.0.3");
UDPServer3.sin_port = htons(8003);
//STEP-3: Socket Creation
UDPSocketServer1 = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (UDPSocketServer1 == INVALID_SOCKET)
{
cout<<"Socket1 Creation Failed & Error No ->"<<WSAGetLastError()<<endl;
}
else
{
cout<<"Socket1 Creation Success"<<endl;
}
UDPSocketClient3 = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (UDPSocketClient3 == INVALID_SOCKET)
{
cout<<"Socket3 reation Failed = "<<WSAGetLastError()<<endl;
}
else
{
cout<<"Socket3 Creation Success"<<endl;
}
//STEP-4: Bind the server
iBind3 = bind(UDPSocketClient3, (SOCKADDR*)&UDPServer3, sizeof(UDPServer3));
if (iBind3 == SOCKET_ERROR)
{
cout<<"Binding Failed & Error No ->"<<WSAGetLastError()<<endl;
}
//STEP-4-1: SendTo Fun send data to client
while (!file_x.eof())
{
file_x >> x;
file_y >> y;
file_z >> z;
sprintf_s(Buffer1,200,"%0.2f\r\n %0.2f\r\n %0.2f\r\n",x,y,z);
iSendTo = sendto(UDPSocketServer1, Buffer1, iBufferLen1, MSG_DONTROUTE, (SOCKADDR*)&UDPClient1, sizeof(UDPClient1));
if (iSendTo == SOCKET_ERROR)
{
cout<<"Sending Data Failed & Error No ->"<<WSAGetLastError()<<endl;
}
else
{
cout<<"Sending Data Success"<<endl;
}
iReceiveFrom = recvfrom(UDPSocketClient3, Buffer3, iBufferLen3, MSG_PEEK, (SOCKADDR*)&UDPServer3, &iUDPServerLen3);
if (iReceiveFrom == SOCKET_ERROR)
{
cout<<"Receiving failed & Error No ->"<<WSAGetLastError()<<endl;
}
else
{
cout<<"Receiving Success"<<endl;
cout<<"Receive Data ->"<<Buffer3<<endl;
}
}
//STEP-6: CloseSocket Function
iCloseSocket1 = closesocket(UDPSocketServer1);
if (iCloseSocket1 == SOCKET_ERROR)
{
cout<<"Socket1 Closing failed & Error No ->"<<WSAGetLastError()<<endl;
}
else
{
cout<<"Socket1 Closing Success"<<endl;
}
iCloseSocket3 = closesocket(UDPSocketClient3);
if (iCloseSocket3 == SOCKET_ERROR)
{
cout<<"Socket3 Closing failed & Error No ->"<<WSAGetLastError()<<endl;
}
else
{
cout<<"Socket3 Closing Success"<<endl;
}
//STEP-7: WSACleanUp Fun for Terminating from DLL
iWsaCleanup = WSACleanup();
if (iWsaCleanup == SOCKET_ERROR)
{
cout<<"WSA Cleanup Success"<<endl;
}
else
{
cout<<"WSA Cleanup Success"<<endl;
}
system("pause");
return 0;
}
I found that flag in recvfrom function matters in this issue, which can be referred to its document and I've changed MSG_PEEK to 0 and considering my application, I've used another project for only receiving data sent from simulink, and the issue has been solved.
I am trying to implement client-server communication via sockets. The main tasks are:
Sending commands from clients to server
Sending data from server to clients
Sending data from clients to server
Commands should come via port1, data via port2.
I got it working without without multi-threading but I have some problems with understanding how do I need to handle sockets.
Current scenario:
1.Server starts (socket, bind, listen for both command and info sockets) and goes for infinite loop with this function:
void FTPServer::waitForConnection()
{
sockaddr_in client;
int clientsize = sizeof(client);
SOCKET clientSocket = accept(_infoSocket, (struct sockaddr*)&client, &clientsize);
if (clientSocket == INVALID_SOCKET)
{
cout << " Accept Info Error" << endl;
}
else
{
cout << " Accept Info OK" << endl;
char* buff = new char[CHUNK_SIZE];
string fullRequest;
int rc = recv(clientSocket, buff, CHUNK_SIZE, 0);
if (rc == SOCKET_ERROR)
{
cout << " Recieve Info Error" << endl;
}
else
{
buff[rc] = NULL;
fullRequest.append(buff);
cout << " Recieve Info OK" <<endl;
if (executeCommand(fullRequest, clientSocket))
{
logOperation(client, fullRequest.c_str());
}
}
delete buff;
}
}
2.Client starts (socket, connect), creates 2 sockets on same ports, waits for user input.
3.User types "LIST", clients checks that it's a valid command and sends it.
bool FTPClient::sendToServer(string data, const bool verbose)
{
int n = 0;
while (data.size() > CHUNK_SIZE)
{
string s = data.substr(CHUNK_SIZE).c_str();
n += send(_infoSocket, data.substr(CHUNK_SIZE).c_str(), CHUNK_SIZE, 0);
data = data.substr(CHUNK_SIZE+1);
}
n+=send(_infoSocket, data.c_str(), data.size(), 0);
cout<<n<<endl;
if(n<0)
{
cout<<"Error: sending"<<endl;
return 0;
}
if (verbose)
cout<<"Send "<<n<<" bytes"<<endl;
return true;
}
4.Servers receives it, accepts on _dataSocket and sends the list of available files.
5.Client receives the list:
string FTPClient::getDataFromServer(const bool verbose)
{
char data[CHUNK_SIZE];
int size = recv(_dataSocket, data, strlen(data), 0);
if (size > 0)
{
int n = 0;
string res;
while (size > CHUNK_SIZE)
{
int buff = recv(_dataSocket, data, CHUNK_SIZE, 0);
res.append(data);
size -= buff;
n += buff;
}
n+= recv(_dataSocket, data, CHUNK_SIZE, 0);
res.append(data);
if (verbose)
cout<<"Recevied "<<n<<" bytes"<<endl;
res.resize(n);
return res;
}
else
{
return "";
}
}
Till this, it works. But if try to execute same command again, I got nothing.
I think, problem is that for each connect we need an accept on server side.
In main loop server gets only one connect from client. Is closing client command socket and reconnecting it on every request only option here? Any other advices (except for "Google it") are highly appreciated.
I have a TCP application written in C++, where a client and a server exchange data. I've istantiated a socket, believing that it would have been blocking by default; on the contrary, after server waits for a client, I have that client calls the recv function without waiting for data. This is the code in which I inizialize the socket fr the client.
int TCPreceiver::initialize(char* address, int port)
{
sock = socket (AF_INET, SOCK_STREAM, 0);
cout << "Socket: " << sock << endl;
sockaddr_in target;
target.sin_family = AF_INET;
target.sin_port = htons (port);
target.sin_addr.s_addr = inet_addr(address);
int fails=0;
while (connect(sock, (sockaddr*) &target, sizeof(target)) == -1)
{
fails++;
if (fails==10)
{
close(sock);
cout << "Error with connection to the server, try again"<< endl;
exit(-1);
}
}
cout << "Client connected (control channel)" << endl;
unsigned char text[10]; //Request message
//fill text[]
if(send(sock, (char*)text, 10, 0)==-1)
{
printf("send() failed with error code : %d" , -1);
exit(EXIT_FAILURE);
}
return 0;
}
I've tried adding this code:
int opts;
opts = fcntl(sock,F_GETFL);
if (opts < 0) {
perror("fcntl(F_GETFL)");
exit(0);
}
opts = (opts & (~O_NONBLOCK));
if (fcntl(sock,F_SETFL,opts) < 0) {
perror("fcntl(F_SETFL)");
exit(0);
}
but it still doesn't work, and if I call the recv(), the application doesn't block (and recv() always returns 0). Here is the function where I call the recv():
void TCPreceiver::receive(char* text, int& dim)
{
int ret;
ret = recv(sock, text, dim, 0);
dim=ret;
if(ret == -1){
printf("recv() failed with error (%d)\n", ret);
//system("PAUSE");
exit(1);
}
}
Where am I wrong?
recv() returning zero indicates either (1) you passed a zero length, which is just a programming error which I won't discuss further here, or (2) end of stream. The peer has close the connection. This isn't a non-blocking situation, this is the end of the connection. You must close the socket and stop using it. It will never return anything. It zero ever again.
See the man pages.
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.