I'm trying to make a multithreaded server, but for some reason, the threads of my server aren't switching. Only the last thread that was create is running, the other threads aren't running.
This is the code of the main server:
void Server::serve(int port)
{
struct sockaddr_in sa = { 0 };
sa.sin_port = htons(port); // port that server will listen for
sa.sin_family = AF_INET; // must be AF_INET
sa.sin_addr.s_addr = INADDR_ANY; // when there are few ip's for the machine. We will use always "INADDR_ANY"
// Connects between the socket and the configuration (port and etc..)
if (bind(_serverSocket, (struct sockaddr*)&sa, sizeof(sa)) == SOCKET_ERROR)
throw std::exception(__FUNCTION__ " - bind");
std::cout << "binded" << std::endl;
// Start listening for incoming requests of clients
if (listen(_serverSocket, SOMAXCONN) == SOCKET_ERROR)
throw std::exception(__FUNCTION__ " - listen");
std::cout << "Listening on port " << port << std::endl;
while (true)
{
SOCKET client_socket = accept(_serverSocket, NULL, NULL);
if (client_socket == INVALID_SOCKET)
throw std::exception(__FUNCTION__);
std::cout << "Accepting clients..." << std::endl;
std::cout << "Client accepted" << std::endl;
std::thread newClientThread(&Server::clientThread, this, std::ref(client_socket)); // Make thread of client
newClientThread.detach();
}
}
This is the thread of the client:
void Server::clientThread(SOCKET& clientSocket)
{
Helper ourHelper;
std::string msg = "";
int lengthOfMessage = 0;
this->_vectorOfSockets.push_back(clientSocket);
try
{
while (true)
{
std::cout << clientSocket;
/*
// Get message and save into queue
msg = ourHelper.getStringPartFromSocket(clientSocket, 1024);
msg = this->returnFullMsg(msg);
try
{
std::cout << clientSocket << " - d\n";
std::lock_guard<std::mutex> lck(mtx);
std::cout << clientSocket << " - d\n";
this->_messagesQueue.push(msg);
ourConditionVariable.notify_one();
std::this_thread::sleep_for(std::chrono::seconds(1)); // Wait the main thread to take care for the message
}
catch (std::exception e)
{
}*/
std::this_thread::sleep_for(std::chrono::seconds(1));
}
}
catch (const std::exception& e)
{
std::cout << "Client logged out\n";
// this->_users.erase(msg.substr(5, atoi(msg.substr(3, 5).c_str() + 5))); // Remove the user from the connected users list
closesocket(clientSocket);
}
}
And this the code of the main thread:
int main()
{
Server myServer;
std::string newMessage;
std::thread ourConnectorThread (connectorThread, std::ref(myServer));
ourConnectorThread.join();
/*std::cout << "Starting...\n";
while (true)
{
std::unique_lock<std::mutex> lckMessages(ourMutex);
ourConditionVariable.wait(lckMessages); // Wait for new message
newMessage = myServer.getQueue().front(); // Get the new message.
myServer.getQueue().pop(); // Remove the first item
takeCareForMessage(newMessage, myServer);
lckMessages.unlock();
}*/
return 0;
}
The code in the comments is irrelevant.
The result of this code is that the last thread is just printing the number of socket every second, and the other threads aren't printing anything..
What is the problem with my code?
One main error in your code is that client_socket is passed by reference, and then it is modified by the server thread. A fix is to pass it by value.
Another error is that _vectorOfSockets.push_back is modified by multiple threads - a race condition. You need to use a mutex to fix that.
accept may fail when a client has disconnected. That's not an unrecoverable exceptional condition, no need to throw an exception, just retry accept to recover.
Related
I am developing a Bluetooth application to connect to a mobile phone. I'm using Rfcomm Bluetooth sockets from the old C standard library. I have this code to create a Bluetooth object and prepare the connection:
class BtDevice final {
...
static constexpr bdaddr_t bdaddrAny {0, 0, 0, 0, 0, 0};
static constexpr uint8_t rfcommChannel = 1;
const sockaddr_rc ourAddress { .rc_family = AF_BLUETOOTH, .rc_bdaddr = bdaddrAny, .rc_channel = rfcommChannel };
int ourSocket = 0;
...
}
BtDevice::BtDevice() {
ourSocket = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
if (ourSocket == -1)
throw std::runtime_error("Bluetooth: Socket creation failed - " + std::string(strerror(errno)) + ".");
static constexpr auto flag = 1;
if(setsockopt(ourSocket, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(int)) != 0)
std::cerr << "Failed to translate socket into the reusable mode - " << strerror(errno) << "." << std::endl;
}
void BtDevice::prepare() {
if (bind(ourSocket, (sockaddr *) &ourAddress, sizeof(ourAddress)) < 0)
throw std::runtime_error("Bluetooth: Socket binding failed - " + std::string(strerror(errno)) + ".");
if (listen(ourSocket, 1) == -1)
throw std::runtime_error("Bluetooth Socket listening failed - " + std::string(strerror(errno)) + ".");
}
If for some reason the phone connection drops, I want my socket to be destroyed and reopened. I have this code for destroying an object:
BtDevice::~BtDevice() {
if(close(ourSocket) != 0)
std::cerr << "Bluetooth: Socket closure failed - " << strerror(errno) << "." << std::endl;
else
std::cout << "Bluetooth socket closed and object is destructed." << std::endl;
}
And I have my program running in a loop in case of errors:
while(true) {
try {
BtDevice btDevice;
btDevice.prepare();
...
/* Main functionality. */
...
break;
} catch (const std::exception& e) {
std::cerr << "Exception during the process: " << e.what() << "." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(5));
}
}
However, if the connection fails, I see in my output that the bind for the new socket failed with the message "Address already in use" (error 98):
Exception during the initialization process: ... (Connection with phone interrupted).
Bluetooth socket closed and object is destructed.
Exception during the initialization process: Bluetooth: Socket binding failed - Address already in use..
Sleeping for 5 seconds...
I've seen questions with similar problems (this question and links from it) and as you can see I added setting the SO_REUSEADDR option right after the socket creation in the constructor.
I see from the documentation that the SO_REUSEADDR option should work for AF_INET sockets:
For AF_INET sockets this means that a socket may bind, except when there is an active listening socket bound to the address.
Should it work the same for bluetooth sockets? And if not, is there a suitable replacement for this?
I need to close and then reuse the same socket in my app. The first time the socket connects it's able to connect properly, but a second time it's tried to be used, client gets a wsaerror 10054 (existing connection was forcibly closed by the remote host) from the server, and I see that server does not receive the "syn" data from the client. What seems to be wrong here? The client that has connected before is able to connect to a server again, but a server that has received a connection before is unable to accept a new connection as it somehow causes a 10054.
connectionmanager.hpp
#pragma once
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <iostream>
#include <string>
#pragma comment (lib, "Ws2_32.lib")
#define DEFAULT_PORT 27015
#define DEFAULT_BUFFER_LENGTH 64
class ConnectionManager {
private:
fd_set fdset;
struct timeval client_wait_timeout;
struct timeval server_wait_timeout;
SOCKET sock = INVALID_SOCKET;
// This is where we'll be setting up connection parameters or where we'll be storing the parameters for a connection that's made.
SOCKADDR_IN connection_data;
int connection_data_len = sizeof(connection_data);
char receive_buffer[DEFAULT_BUFFER_LENGTH] = { 0 }; // The object where the recieved data will be placed on.
public:
std::wstring server_ipv4;
bool is_connected = false;
std::string type = "none";
ConnectionManager();
void init(std::string connection_type);
void reset();
bool establish_first_connection();
bool await_first_connection();
std::string receive_data();
std::string send_data(std::string data);
};
connectionmanager.cpp
#include "connection_manager.hpp"
ConnectionManager::ConnectionManager() {
WSADATA wsadata;
int result;
// Initialize Windows Sockets library, version 2.2.
result = WSAStartup(MAKEWORD(2, 2), &wsadata);
if (result != 0)
std::cerr << "WSAStartup failed, error: " << result << "\n";
connection_data.sin_family = AF_INET; // Using IPv4
connection_data.sin_port = htons(DEFAULT_PORT);
}
void ConnectionManager::init(std::string connection_type) {
int result = 0;
if (connection_type == "server") {
connection_data.sin_addr.s_addr = INADDR_ANY; // Bind the socket to all available interfaces - or in other words, accept connections from any IPv4 address. We'll change this after we establish our first connection with the client.
// Create a socket for the server to listen from client for data / send data to client.
sock = socket(connection_data.sin_family, SOCK_DGRAM, 0);
if (sock == INVALID_SOCKET) {
std::cerr << "Error occured while creating server socket: " << WSAGetLastError() << "\n";
WSACleanup();
}
// Bind the listening socket.
result = bind(sock, (SOCKADDR*)&connection_data, connection_data_len);
if (result == SOCKET_ERROR) {
std::cerr << "Listening socket bind failed with error: " << WSAGetLastError() << "\n";
closesocket(sock);
WSACleanup();
}
std::cout << "Awaiting connection..." << "\n";
if (!await_first_connection())
std::cerr << "Either no one connnected during the 60 second period, or there was a problem with the server. Last WSA error:" << WSAGetLastError() << "\n";
else {
std::cout << "Connected successfully!" << "\n";
is_connected = true;
}
}
else if (connection_type == "client") {
InetPton(connection_data.sin_family, (PCWSTR)(server_ipv4.c_str()), &connection_data.sin_addr.s_addr); // Set the IP address to connect to on the connection_data structure.
// Create a socket for sending data to server.
sock = socket(connection_data.sin_family, SOCK_DGRAM, IPPROTO_UDP);
if (sock == INVALID_SOCKET) {
std::cerr << "Error occured while creating client socket: " << WSAGetLastError() << "\n";
WSACleanup();
}
std::wcout << "Attempting to connect to " << server_ipv4 << "..." << "\n";
if (!establish_first_connection())
std::cerr << "There was a problem connecting the server. Last WSA error: " << WSAGetLastError() << "\n";
else {
std::wcout << "Successfully connected to " << server_ipv4 << "!" << "\n";
is_connected = true;
}
}
// Put the socket in non-blocking mode.
unsigned long mode = 1;
if (ioctlsocket(sock, FIONBIO, (unsigned long*)&mode) == SOCKET_ERROR) {
std::cerr << "Error while putting the socket into non-blocking mode: " << WSAGetLastError() << "\n";
}
}
void ConnectionManager::reset() {
is_connected = false;
closesocket(sock);
}
/*
Functions "establish_first_connection" and "await_first_connection" do something that's quite similar to the three-way handshake method of a TCP connection.
*/
bool ConnectionManager::establish_first_connection() { // This will be used by the client.
// Set up the file descriptor set.
FD_ZERO(&fdset);
FD_SET(sock, &fdset);
int send_result = INT32_MAX;
std::string syn_message = "SYN";
send_result = sendto(sock, syn_message.c_str(), syn_message.length(), 0, (SOCKADDR*)&connection_data, connection_data_len);
if (send_result == SOCKET_ERROR) {
std::cerr << "Error occured while attempting to send SYN to server: " << WSAGetLastError() << "\n";
}
else {
int result = 0;
int receive_result = 0;
// Set up the timeval struct for the timeout.
// We'll wait for 10 seconds for the server to respond, or else we'll call the connection off.
client_wait_timeout.tv_sec = 10; // seconds
client_wait_timeout.tv_usec = 0; // microseconds
// Wait until the timeout or until we receive data.
result = select(sock, &fdset, NULL, NULL, &client_wait_timeout);
if (result == 0)
std::cout << "Timeout." << "\n"; // todo
else if (result == -1)
std::cerr << "Error occured while awaiting first connection data from server. Last WSA error:" << WSAGetLastError() << "\n";
receive_result = recvfrom(sock, receive_buffer, DEFAULT_BUFFER_LENGTH, 0, (SOCKADDR*)&connection_data, &connection_data_len);
if (receive_result > 0) { // If we received any data before the timeout, return true.
std::string client_ack_message = "ACK";
std::cout << receive_buffer << "\n";
sendto(sock, client_ack_message.c_str(), client_ack_message.length(), 0, (SOCKADDR*)&connection_data, connection_data_len);
return true;
}
}
return false;
}
bool ConnectionManager::await_first_connection() { // This will be used by the server.
int result = 0;
int receive_result = 0;
int send_result = 0;
// Set up the file descriptor set.
FD_ZERO(&fdset);
FD_SET(sock, &fdset);
// Set up the timeval struct for the timeout.
// We'll wait for 60 seconds for someone to connect and if someone doesn't connect, we'll cancel the server.
server_wait_timeout.tv_sec = 60; // seconds
server_wait_timeout.tv_usec = 0; // microseconds
// Wait until the timeout or until we receive data.
result = select(sock, &fdset, NULL, NULL, &server_wait_timeout);
if (result == 0) {
std::cout << "Timeout." << "\n";
return false;
}
else if (result == -1)
std::cerr << "Error occured while awaiting first connection data from client. Last WSA error: " << WSAGetLastError() << "\n";
receive_result = recvfrom(sock, receive_buffer, DEFAULT_BUFFER_LENGTH, 0, (SOCKADDR*)&connection_data, &connection_data_len); // We set the first connected client as the only suitable connector from now on here.
if (receive_result > 0) { // If we received any data before the timeout, let the client know that we acknowledge their request and return true.
std::string ack_message = "ACK";
send_result = sendto(sock, ack_message.c_str(), ack_message.length(), 0, (SOCKADDR*)&connection_data, connection_data_len); // Let the client know that we received their message.
if (send_result != SOCKET_ERROR)
return true;
}
return false;
}
std::string ConnectionManager::receive_data() {
ZeroMemory(receive_buffer, DEFAULT_BUFFER_LENGTH); // Clean the receive buffer of any possibly remaining data.
int receive_result = 42;
u_long ioctl_result = 123;
while (true) { // When ioctl with FIONREAD results 0, that means there's no datagram pending in the receive queue. We'll use this to grab only the last received package.
receive_result = recvfrom(sock, receive_buffer, DEFAULT_BUFFER_LENGTH, 0, (SOCKADDR*)&connection_data, &connection_data_len);
ioctlsocket(sock, FIONREAD, &ioctl_result);
if (ioctl_result == 0)
break;
}
// Handle errors.
if (receive_result > 0) {
return std::string(receive_buffer, receive_result); // Using the built-in method of casting char to std::string.
}
else if (receive_result == 0)
return "RECEIVEDNOTHING";
else if (receive_result == SOCKET_ERROR)
switch (WSAGetLastError()) {
case WSAEWOULDBLOCK:
return "WOULDBLOCK";
break;
case WSAECONNRESET:
return "CONNRESET";
break;
case NTE_OP_OK:
break;
default:
std::cerr << "Unhandled error while receiving data: " << WSAGetLastError() << "\n";
}
return "NONE";
}
std::string ConnectionManager::send_data(std::string data) {
int send_result = 666;
send_result = sendto(sock, data.c_str(), data.length(), 0, (SOCKADDR*)&connection_data, connection_data_len);
// Handle errors.
if (send_result == SOCKET_ERROR) {
std::cerr << "Error while sending data: " << WSAGetLastError() << "\n";
return std::string("FAIL");
}
else
return std::string("OK");
}
main.cpp
#include <iostream>
#include <string>
#include "connectionmanager.hpp"
int main() {
ConnectionManager connection_manager;
std::string connection_type;
std::cout << "server or client?" << "\n";
std::cin >> connection_type;
if (connection_type == "client") {
std::wstring ipv4_addr;
std::cout << "ip address?" << "\n";
std::wcin >> ipv4_addr;
connection_manager.server_ipv4 = ipv4_addr;
}
connection_manager.type = connection_type;
connection_manager.init(); // this works fine
connection_manager.reset();
connection_manager.init(); // client returns wsaerror 10054, server receives no data
}
I was able to solve this issue by moving the sin_family and sin_port initialization to ConnectionManager::init() from the constructor and by editing the ConnectionManager::reset() to look like this:
void ConnectionManager::reset() {
puts("reset!");
is_connected = false;
closesocket(sock);
sock = INVALID_SOCKET;
memset(&connection_data, 0, sizeof(connection_data)); // Get rid of the data from the previous connection.
memset(&receive_buffer, 0, sizeof(receive_buffer));
}
it may be a strange request, but, actually, I would like make my call to recv() blocking.
It appears that it should be blocking by default, but for me, it is not, and I can't find any way to make it.
I have a very simple server application, "open socket - bind - wait for connection - receive" on one hand,
and, on the other hand, a simple "open socket - connect - send" client application.
They communicate on LocalHost.
Server :
int clTcpServer::openSocket(void)
{
this->__iLocalSocketId = socket(AF_INET, //stand for IPV4
SOCK_STREAM, //using TCP protocol
0); //no specified / ip protocol
return 0;
}
int clTcpServer::openServer(uint16_t u16Port)
{
this->__strServerInfo.sin_family = AF_INET; // stand for IPV4
this->__strServerInfo.sin_addr.s_addr = INADDR_ANY; // Listen to any address (no filter)
this->__strServerInfo.sin_port = htons(u16Port);
if(bind(this->__iLocalSocketId, (sockaddr *)&this->__strServerInfo, sizeof(this->__strServerInfo)) < 0)
{
std::cout << "TCP server - ERROR - Can't bind socket to port " << std::dec << (int)u16Port << std::endl;
return -1;
}
std::cout << "TCP server - Server opened" << std::endl;
listen(this->__iLocalSocketId, 3);
return 0;
}
int clTcpServer::waitForConnection(void)
{
int iClientIpInfo;
char* pcClientIp;
std::cout << "Waiting for connection ..." << std::endl;
iClientIpInfo = sizeof(struct sockaddr_in);
this->__iDistantSocketId = accept(this->__iLocalSocketId,
(sockaddr *)&this->__strClientInfo,
(socklen_t*)&iClientIpInfo);
if(this->__iDistantSocketId < 0)
{
std::cout << "TCP server - ERROR - Connection refused" << std::endl;
return -1;
}
pcClientIp = inet_ntoa(this->__strClientInfo.sin_addr);
std::cout << "Connection accepted from " << pcClientIp << std::endl;
return 0;
}
int clTcpServer::receiveDataBuffer(void * pvidBuffer, size_t sizeMaxBufferSize)
{
int iRecvSize;
iRecvSize = recv(this->__iDistantSocketId, pvidBuffer, sizeMaxBufferSize, (int)0);
if (iRecvSize < (int)0 )
{
std::cout << "TCP server - ERROR - Recv failed" << std::endl;
return -1;
}
return iSentSize;
}
Client :
int clTcpClient::openSocket(void)
{
/* Create a new socket for client comm. */
this->__iSocketId = socket( AF_INET, //stand for IPV4
SOCK_STREAM, //using TCP protocol
0); //no specified / ip protocol
if (this->__iSocketId == (int)-1)
{
std::cout << "TCP client - ERROR - Can't open Socket" << std::endl;
return -1;
}
return 0;
}
int clTcpClient::connectToServer(std::string stgIpAddress, uint16_t u16Port)
{
/* configure server info according to parameters */
this->__strServerInfo.sin_family = AF_INET; //stand for IPV4
this->__strServerInfo.sin_port = htons(u16Port);
/* Retrieve IP addr*/
this->__strServerInfo.sin_addr.s_addr = inet_addr(stgIpAddress.c_str());
/* Try to connect to the server */
if (connect(this->__iSocketId,
(sockaddr*)&this->__strServerInfo,
sizeof(this->__strServerInfo)) < 0)
{
std::cout << "TCP client - ERROR - Can't connect to " << stgIpAddress << ":" << std::dec << (int)u16Port << std::endl;
return -1;
}
std::cout << "TCP client - Connected" << std::endl;
return 0;
}
int clTcpClient::sendDataBuffer(const void *kpvidData, size_t sizeDataLength)
{
ssize_t sizeSentSize;
/* Send the buffer */
sizeSentSize = send(this->__iSocketId, kpvidData, sizeDataLength, (int)0);
if (sizeSentSize < (ssize_t)0 )
{
std::cout << "TCP client - ERROR - Send failed" << std::endl;
return -1;
}
std::cout << "TCP client - " << (int)sizeSentSize << " byte(s) sent." << std::endl;
return 0;
}
This is working fine, but recv() here is not blocking. If I call it (after having initialized all the stuff, of course), if there is nothing to receive, the function returns 0, as "0 byte received", and that's all.
It means If I want to put this recv function in a loop, CPU is working 100% to receive ... nothing.
What's wrong here ?How can I make it blocking ?
Thank you
I am trying to implement a small c++ server. i want to receive connections from clients, and handle all of these connections in own threads. So far so good. As long as i introduce no threads, it works fine, but as soon as i try to create a thread for a client, accept returns with -1/error.
Here is my code:
void Server::run()
{
cout << "starting server on port " << this->port << "..." << endl;
this->socket_fd = socket(AF_INET, SOCK_STREAM, 0);
if (this->socket_fd < 0) {
perror("creating socket");
exit(1);
}
struct sockaddr_in myaddr;
myaddr.sin_family = AF_INET;
myaddr.sin_port = htons(this->port);
inet_aton("192.168.201.58", &myaddr.sin_addr);
if ( bind(this->socket_fd, (struct sockaddr*)&myaddr, sizeof(myaddr))) {
perror("bind");
exit(1);
}
if (listen(this->socket_fd, 5)) {
perror("listen");
exit(1);
}
/* waiting for clients */
cout << "waiting for connection..." << endl;
int client_fd;
struct sockaddr_in remote_addr;
socklen_t remote_addr_len;
while (this->running) {
client_fd = accept(this->socket_fd, (struct sockaddr*)&remote_addr, &remote_addr_len);
if (client_fd <= 0) {
perror("accept");
this->running = false;
continue;
}
cout << "got new client with address " << inet_ntoa(remote_addr.sin_addr) << endl;
Client new_client(client_fd, remote_addr.sin_addr);
//new_client.run();
std::thread t ( &Client::run, &new_client );
//t.detach();
}
}
when I am trying to connect via telnet, I get "accept: Invalid argument". As soon as I comment out the line where I create the thread
std::thread t ( &Client::run, &new_client );
everything works fine.
I would appreciate any hints.
Try:
std::thread t ( &Client::run, std::move(new_client) );
According to the documentation, that's how you should call it.
I begin to develop my tool, which works with net at the TCP level, which will present simple functions of web-server.
In testing my program I have got very bad mistakes:
Memory leaks
Creating thousands of threads immediately
In taskmgr.exe you may see about ~1,5 of threads and about ~50kb of allocated memory.
Also, I compiled program as 32 bit, but in vmmap utility you may see a lot of 64 bit stacks. My OS is 64 bit, but in taskmgr.exe you may see *32 , I don’t know how 32 bit program uses 64 bit stack, maybe it’s normal for launching 32 bit program in 64 bit OS, but I have no knowledge about this design of OS, so I shall be very pleased , if you give me a piece of advice on this question.
So, why did my program creates immediately a lot of threads? ( I guess , cause of while(true) block ).
But , I want the next:
Create each thread for each new request
When request has been handled, then terminate the thread and free the memory
How should I remake my code?
Thanks!
Here is my code ( MS VC ++ 9 ):
#include <iostream>
#include <Windows.h>
#pragma comment(lib, "Ws2_32.lib")
typedef struct Header
{
friend struct Net;
private:
WORD wsa_version;
WSAData wsa_data;
SOCKET sock;
SOCKADDR_IN service;
char *ip;
unsigned short port;
public:
Header(void)
{
wsa_version = 0x202;
ip = "0x7f.0.0.1";
port = 0x51;
service.sin_family = AF_INET;
service.sin_addr.s_addr = inet_addr(ip);
service.sin_port = htons(port);
}
} Header;
typedef struct Net
{
private:
int result;
HANDLE thrd;
DWORD exit_code;
void WSAInit(WSAData *data, WORD *wsa_version)
{
result = WSAStartup(*wsa_version, &(*data));
if(result != NO_ERROR)
{
std::cout << "WSAStartup() failed with the error: " << result << std::endl;
}
else
{
std::cout << (*data).szDescription << " " << (*data).szSystemStatus << std::endl;
}
}
void SocketInit(SOCKET *my_socket)
{
(*my_socket) = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if((*my_socket) == INVALID_SOCKET)
{
std::cout << "Socket initialization failed with the error: " << WSAGetLastError() << std::endl;
WSACleanup();
}
else
{
std::cout << "Socket initialization successful!" << std::endl;
}
}
void SocketBind(SOCKET *my_socket, SOCKADDR_IN *service)
{
result = bind((*my_socket), (SOCKADDR*)&(*service), sizeof(*service));
if(result == SOCKET_ERROR)
{
std::cout << "Socket binding failed with the error: " << WSAGetLastError() << std::endl;
closesocket((*my_socket));
WSACleanup();
}
else
{
std::cout << "Socket binding successful!" << std::endl;
}
result = listen(*my_socket, SOMAXCONN);
if(result == SOCKET_ERROR)
{
std::cout << "Socket listening failed with the error: " << WSAGetLastError() << std::endl;
}
else
{
std::cout << "Listening to the socket..." << std::endl;
}
}
static void SocketAccept(SOCKET *my_socket)
{
SOCKET sock_accept = accept((*my_socket), 0, 0);
if(sock_accept == INVALID_SOCKET)
{
std::cout << "Accept failed with the error: " << WSAGetLastError() << std::endl;
closesocket(*my_socket);
WSACleanup();
}
else
{
std::cout << "Client socket connected!" << std::endl;
}
char data[0x400];
int result = recv(sock_accept, data, sizeof(data), 0);
HandleRequest(data, result);
char *response = "HTTP/1.1 200 OK\r\nServer: Amegas.sys-IS/1.0\r\nContent-type: text/html\r\nSet-Cookie: ASD643DUQE7423HFDG; path=/\r\nCache-control: private\r\n\r\n<h1>Hello World!</h1>\r\n\r\n";
result = send(sock_accept, response, (int)strlen(response), 0);
if(result == SOCKET_ERROR)
{
std::cout << "Sending data via socket failed with the error: " << WSAGetLastError() << std::endl;
closesocket(sock_accept);
WSACleanup();
}
else
{
result = shutdown(sock_accept, 2);
}
}
static void HandleRequest(char response[], int length)
{
std::cout << std::endl;
for(int i = 0; i < length; i++)
{
std::cout << response[i];
}
std::cout << std::endl;
}
static DWORD WINAPI Threading(LPVOID lpParam)
{
SOCKET *my_socket = (SOCKET*)lpParam;
SocketAccept(my_socket);
return 0;
}
public:
Net(void)
{
Header *obj_h = new Header();
WSAInit(&obj_h->wsa_data, &obj_h->wsa_version);
SocketInit(&obj_h->sock);
SocketBind(&obj_h->sock, &obj_h->service);
while(true)
{
thrd = CreateThread(NULL, 0, &Net::Threading, &obj_h->sock, 0, NULL);
//if(GetExitCodeThread(thrd, &exit_code) != 0)
//{
// ExitThread(exit_code);
//}
}
delete &obj_h;
}
} Net;
int main(void)
{
Net *obj_net = new Net();
delete &obj_net;
return 0;
}
You should create the thread AFTER you accept a connection, not before.
What you are doing is creating a ton of threads, and then having each of them wait for a connection. Many of them have nothing to do. I don't even know if Windows' accept call is thread-safe - you might end up with multiple threads handling the same connection.
What you need to do instead is, in your main loop (Net's constructor while(true)), you need to call accept(). Since accept() blocks until it has a connection, this will cause the main thread to wait until somebody tries to connect. Then, when they do, you create another thread (or process - more likely on UNIX) to handle that connection. So, your loop now looks like this:
SOCKET sock_accept = accept((*my_socket), 0, 0);
if(sock_accept == INVALID_SOCKET)
{
std::cout << "Accept failed with the error: " << WSAGetLastError() << std::endl;
closesocket(*my_socket);
WSACleanup();
}
else
{
std::cout << "Client socket connected!" << std::endl;
}
thrd = CreateThread(NULL, 0, &Net::Threading, &obj_h->sock, 0, NULL);
//push back thrd into a std::vector<HANDLE> or something like that
//if you want to keep track of it for later: there's more than one thread
Then, delete that code you moved from SocketAccept into this loop. And then, for cosmetic purposes, I would change the name of SocketAccept to SocketHandleConnection.
Now, when your thread starts, it already has a connection, and all you need to do is handle the data (e.g. what you do starting at char data[0x400]).
If you want to handle cleanup for connections, there are a few ways to do this. One, since you are threaded, you can have the thread do its own cleanup. It shares memory with the main process, so you can do this. But in this example, I don't see anything you need to clean up.
Lastly, I think you don't understand what ExitThread does. According to MSDN:
ExitThread is the preferred method of exiting a thread in C code. However, in C++ code,
the thread is exited before any destructors can be called or any other automatic cleanup
can be performed. Therefore, in C++ code, you should return from your thread function.
So it appears that you don't need to call ExitThread- you just return from your function and the thread exits automatically. You don't need to call it from the main thread.
Finally, you should really (if you can) use the new standard C++ threads in c++11, and then if you put in a little bit of effort to port your code over to boost::asio, you'll have a completely cross platform application, with no need for windows API C ugliness :D
DISCLAIMER: I only have a passing understanding of Windows as most of my experience is related to UNIX. I have attempted to be as accurate as I can but if I have any misconceptions about how this knowledge converts over to Windows, well, I warned you.
Why are you creating threads in an infinite loop? This will, of course, create tons of threads. I am referring to this piece of code:
while(true)
{
thrd = CreateThread(NULL, 0, &Net::Threading, &obj_h->sock, 0, NULL);
}