Asynchronous I/O on C++ Sockets - c++

I'm writing a multithreaded sockets applications (for windows), however, I have a problem when connecting multiple clients. I can send messages from the server to the clients, however, I can only send messages from one client to the server. The other clients can't send messages to the server. I've googled some stuff and found that Overlapping/Asynchronous I/O is the way to go. There's just one problem with that, I don't know how to implement that, so I'm basically asking how I would go about doing this or if this approach is wrong.
The RecieveFromClients() function is what I want to make Asynchronous.
Thanks in advance.
Here is my code:
main.cpp
#include "server.h"
int main()
{
Server server;
server.StartServer();
return 0;
}
Server.h
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <iphlpapi.h>
#include <stdio.h>
#include <string>
#include <iostream>
#include <vector>
#include <thread>
#pragma comment(lib, "Ws2_32.lib")
#define DEFAULT_PORT 4566
#define BACKLOG 10
class Server
{
public:
Server();
~Server();
int StartServer();
private:
int Socket();
int Bind();
int Listen();
void AcceptConnections();
int StopServer();
int CloseClientSocket(int client_num);
void GetClients(int server_socket, std::vector<int>* clients,
std::vector<sockaddr_in> *client_addrs);
void SendToClients();
void RecieveFromClients(int id);
private:
sockaddr_in server_addr;
int connected_clients, counted_clients;
int server_socket;
int result;
int msgSize;
std::vector<int> clients;
std::vector<sockaddr_in> client_addrs;
private:
std::thread get_clients;
std::thread send_messages;
std::thread recieve_messages;
};
Server.cpp
#include "server.h"
Server::Server()
:
connected_clients(0),
counted_clients(0),
server_socket(0),
result(0)
{
ZeroMemory(&server_addr, 0);
WSAData wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
printf("wsastartip falied\n");
}
}
Server::~Server()
{
WSACleanup();
}
int Server::StartServer()
{
if (Socket() != 0)
{
return 1;
}
if (Bind() != 0)
{
return 1;
}
if (Listen() != 0)
{
return 1;
}
AcceptConnections();
return 0;
}
int Server::Socket()
{
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket == INVALID_SOCKET)
{
std::cout << "Failed to create socket" << std::endl;
return 1;
}
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(DEFAULT_PORT);
server_addr.sin_addr.S_un.S_addr = INADDR_ANY;
return 0;
}
int Server::Bind()
{
result = bind(server_socket, (sockaddr*)&server_addr,
sizeof(server_addr));
if (result == SOCKET_ERROR)
{
std::cout << "Failed to bind socket" << std::endl;
return 1;
}
return 0;
}
int Server::Listen()
{
result = listen(server_socket, BACKLOG);
if (result == SOCKET_ERROR)
{
std::cout << "Listening failed" << std::endl;
return 1;
}
return 0;
}
void Server::AcceptConnections()
{
get_clients = std::thread(&Server:: GetClients, this, server_socket,
&clients, &client_addrs);
send_messages = std::thread(&Server::SendToClients, this);
recieve_messages = std::thread(&Server::RecieveFromClients, this, counted_clients);
get_clients.join();
send_messages.join();
recieve_messages.join();
}
int Server::StopServer()
{
std::terminate();
for (int client : clients)
{
result = closesocket(client);
if (result == SOCKET_ERROR)
{
std::cout << "Failed to close client socket" << std::endl;
return 1;
}
}
return 0;
}
int Server::CloseClientSocket(int client_num)
{
result = closesocket(clients[client_num]);
if (result == SOCKET_ERROR)
{
std::cout << "Failed to close client socket" << std::endl;
return 1;
}
return 0;
}
void Server::GetClients(int server_socket, std::vector<int>* clients,
std::vector<sockaddr_in>* client_addrs)
{
while(true)
{
sockaddr_in client_addr = { 0 };
socklen_t client_addrstrlen = sizeof(client_addr);
int client;
client = accept(server_socket, (sockaddr*)&client_addr,
&client_addrstrlen);
clients->push_back(client);
client_addrs->push_back(client_addr);
++connected_clients;
char ip[INET_ADDRSTRLEN] = "";
char port[100] = "";
inet_ntop(AF_INET, &client_addr.sin_addr, ip, sizeof(ip));
std::cout << "Client connected from " << ip << ":" <<
client_addr.sin_port << std::endl;
}
}
void Server::SendToClients()
{
std::string msg;
do
{
msg.clear();
getline(std::cin, msg);
if (msg.size() > 255)
{
std::cout << "Message must be less than 256 bytes"
<< std::endl;
continue;
}
for (int client : clients)
{
int size;
size = send(client, msg.data(), msg.size(), 0);
if (size == SOCKET_ERROR)
{
std::cout << "Failed to send message to client"
<< std::endl;
}
}
} while (msg != "exit");
if (StopServer() != 0)
{
std::cout << "Failed to close client sockets" << std::endl;
}
}
void Server::RecieveFromClients(int id)
{
std::vector<char> msgBuffer(256);
do
{
if (connected_clients > 0)
{
msgBuffer.clear();
msgBuffer.resize(256);
char ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &client_addrs[id].sin_addr, ip, sizeof(ip));
if (msgSize = recv(clients[id], msgBuffer.data(),
msgBuffer.size(), 0) > 0)
{
std::cout << ip << ": ";
for (char c : msgBuffer)
{
if (c != 0)
{
std::cout << c;
}
}
std::cout << std::endl;
}
else
{
if (msgSize == SOCKET_ERROR)
{
std::cout << "Failed to recieve data" << std::endl;
break;
}
else if (clients[id] > 0)
{
std::cout << "Client " << ip << " has disconnected" << std::endl;
CloseClientSocket(0);
break;
}
}
}
else
{
continue;
}
} while (true);
}

Using Overlapped I/O will be a fairly large change to the design of your code. But fortunately, there is a simpler solution. In your RecieveFromClients() method, you can use select() to determine which client sockets actually have pending data to read before you attempt to read from them. You are using blocking sockets, so a call to recv() will block the calling thread until data is received, so you don’t want to perform a blocking read until you are actually ready to read something.
Also, since you are not creating a new thread for each accepted client, the id parameter of RecieveFromClients(), and the client_num parameter of CloseClientSocket(), are being used incorrectly and should just be removed completely. The receive function should run a loop over the list of connected clients. And the close function should take the specific socket handle to close.
That being said, another major design problem with your code is that you are sharing variables and containers across multiple threads, but you are not synchronizing access to any of them. That will cause you big problems in the long run.

Related

How to return TCP receive data and send call back data?

How can I send a command and Handle the command in c++ for listener.
I want something like the below code....
switch (args[i][1])
{
case 'command1':
command1function();
break;
case 'command2':
command2function();
break;
case 'command3':
command3function();
break;
default:
return 0;
}
TCP Listener c++ (Please see the GITHUB link this is not my project however I have been trying for hours now with no luck at all, Any help would be greatly appreciated) https://github.com/GreatBullet/Listener_Project/
void Class_tcp_Listener::Run()
{
char buf[MAX_BUFFER_SIZE];
while (true)
{
//create a listening socket
SOCKET listening = CreateSocket();
if (listening == INVALID_SOCKET)
{
break;
}
SOCKET client = WaitForConnection(listening);
if (client != INVALID_SOCKET)
{
closesocket(listening);
int bytesReceived = 0;
do
{
ZeroMemory(buf, MAX_BUFFER_SIZE);
bytesReceived = recv(client, buf, MAX_BUFFER_SIZE, 0);
if (bytesReceived > 0)
{
if (MessageReceived != NULL)
{
MessageReceived(this, client, std::string(buf, 0, bytesReceived));
std::cout << std::string(buf, 0, bytesReceived) << '\n';
}
}
} while (bytesReceived > 0);
closesocket(client);
}
}
}
You can use std::unordered_map to store function wrappers:
#include <iostream>
#include <string>
#include <unordered_map>
#include <functional>
void command3() {
std::cout << "command3\n";
}
int main() {
std::unordered_map<std::string, std::function<void(void)>> commands;
commands["command1"] = [] {std::cout << "command1\n"; };
commands["command2"] = [] {std::cout << "command2\n"; };
commands["command3"] = command3;
std::string args = "command3";
auto it = commands.find(args);
if (it != commands.end()) {
it->second();
} else {
std::cerr << "Invalid command: " << args << "\n";
}
}
P.S. It is possible that pattern matching will be added in future standards.

Sockets not sending data

I'm currently working on an instant messaging system that require a server and a client to communicate between each other.
I tried it in C++ using the default socket API.
The problem is that even if both programs (server & client) compile fine, there isn't any socket sent from the client that reaches the server.
I don't know what did I do wrong here (I went over my code like 5 times and even did it again from scratch, no success).
I used "debugs" messages to locate the problems and they all concern the processing loops that I use.
// code from the server
#include <stdio.h>
#include <tchar.h>
#include <winsock2.h>
#include <stdlib.h>
#include <iostream>
#include <thread>
#include <vector>
#include <Ws2tcpip.h>
#include <string>
int main()
{
std::locale::global(std::locale("fr-FR"));
WSAData wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
std::cout << "Error initializing winsock";
return -1;
}
SOCKET server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (server == INVALID_SOCKET)
{
std::cout << "Error initializing the socket ";
return -2;
}
const unsigned short port = 9999;
sockaddr_in addr;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(port);
addr.sin_family = AF_INET;
int res = bind(server, (sockaddr*)&addr, sizeof(addr));
if (res != 0)
{
std::cout << "Error when binding";
return -3;
}
res = listen(server, SOMAXCONN);
if (res != 0)
{
std::cout << "Error on calling listen";
return -4;
}
std::cout << "Server successfully launched" << std::endl;
char buffer[1025];
while (true)
{
sockaddr_in from = { 0 };
int addrlen = sizeof(from);
SOCKET newClient = accept(server, (SOCKADDR*)(&from), &addrlen);
if (newClient != SOCKET_ERROR)
{
std::cout << "Client connected successfully" << std::endl;
int Bytes = recv(newClient, buffer, 1024, 0);
if (Bytes <= 0)
{
break;
}
std::cout << "Message received from client : " << buffer << std::endl;
send(newClient, buffer, 1024, 0); // send it back to client
}
}
return 0;
}
// code from the client
#include <stdio.h>
#include <tchar.h>
#include <winsock2.h>
#include <stdlib.h>
#include <iostream>
#include <thread>
#include <vector>
#include <Ws2tcpip.h>
#include <string>
#define _WINSOCK_DEPRECATED_NO_WARNINGS
void sendMessage(SOCKET s);
void recvMessage(SOCKET s);
int main()
{
std::locale::global(std::locale("fr-FR"));
WSAData wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
std::cout << "Error initializing winsock";
return -1;
}
SOCKET server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (server == INVALID_SOCKET)
{
std::cout << "Error initializing the socket ";
return -2;
}
sockaddr_in addr;
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_family = AF_INET;
addr.sin_port = htons(9999);
int res = bind(server, (sockaddr*)&addr, sizeof(addr));
if (res != 0)
{
std::cout << "Error when binding";
return -3;
}
if (connect(server, (const sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR)
{
std::cout << "Erreur when connecting : " << WSAGetLastError() << std::endl;
return -4;
}
std::cout << "You are connected to server" << std::endl;
std::thread sending(sendMessage, server);
std::thread receiving(recvMessage, server);
sending.detach();
receiving.detach();
while (true)
{ }
return 0;
}
void sendMessage(SOCKET s)
{
while (true)
{
std::string buff;
std::cout << "Type your message :" << std::endl;
std::getline(std::cin, buff);
std::cout << std::endl;
int Bytes = send(s, buff.c_str(), 1024, 0);
if (Bytes <= 0)
{
break;
}
}
}
void recvMessage(SOCKET s)
{
while (true)
{
char buffer[1025];
int Bytes = recv(s, buffer, 1024, 0);
if (Bytes <= 0)
{
break;
}
std::cout << "The server sent : " << buffer << std::endl;
}
}
The server should display the message that a client has connected when the client is launched and displays the chat command, but the only thing displayed in the server console is the message saying that the server has launched correctly... Yet the client displays the message supposedly "received" by the server.
PS : I'm aware the code doesn't need that many "include" statements, it's just I didn't remember which ones contained which functions so I'd rather include more than not enough for anybody wanting to compile the code.
Several Things:
First, this is wrong:
send(s, buff.c_str(), 1024, 0)
You're basically telling send that the buffer it addresses is a full 1kb, and to send it all. It hasn't a clue how much memory there is actually valid, and knows nothing about terminated strings.
Second, the client setup is wrong. Don't bind to a client socket; connect is sufficient. Notice these:
int res = bind(server, (sockaddr*)&addr, sizeof(addr));
if (res != 0)
{
std::cout << "Error when binding";
return -3;
}
if (connect(server, (const sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR)
{
std::cout << "Erreur when connecting : " << WSAGetLastError() << std::endl;
return -4;
}
That bind shouldn't be there. The connect will bind to the socket if the connection can be made. Removing the bind section of the client, fixing the buffer management to be correct, and putting closesocket calls where they belong, and your program will be much further down the road to functional. There are still some question logic workflows, but at least the connectivity will be setup better.

Sfml networking, SocketSelector member function isReady not working correctly

I am coding simple networking system based on sfml-network liblary and tcp sockets.
When i compile and run my program i am getting that output on screen(screenshot).
It looks like selector.wait() (Server.cpp:20) is not waiting for any packet and selector.isReady(TCPSOCKET) (Server.cpp:43) is not checking correctly if client is sending package to server.
Code:
main.cpp
#include <iostream>
#include "Server.h"
int main()
{
int mode = 0;
std::cin >> mode;
if(mode == 0)
{
Server server(55200);
}else if(mode == 1){
sf::TcpSocket socket;
if (socket.connect("localhost", 55200) != sf::Socket::Done)
{
std::cout << "Error1\n";
}
//Sleep(2000);
sf::Packet packet;
packet << 11;
if (socket.send(packet) != sf::Socket::Done)
{
std::cout << "Error2\n";
}
}
std::cin.get();
std::cin.get();
}
Server.h
#pragma once
#include <SFML/Network.hpp>
#include <vector>
class Server
{
private:
sf::TcpListener listener;
std::vector<sf::TcpSocket*> clients;
sf::SocketSelector selector;
unsigned short port;
public:
Server(unsigned short port);
~Server();
};
Server.cpp
#include "Server.h"
#include <iostream>
#include <SFML/Network.hpp>
#include <vector>
Server::Server(unsigned short pport)
{
port = pport;
std::cout << "Starting Server....\n";
if (listener.listen(port) != sf::Socket::Done)
{
std::cout << "Failed while starting listening on port: " << port << std::endl;
return;
}
selector.add(listener);
while(true)
{
if (selector.wait())
{
//new connection
if (selector.isReady(listener))
{
std::cout << "New connection!!\n";
sf::TcpSocket* tmp = new sf::TcpSocket; // !!!!!!!!!!!
if (listener.accept(*tmp) != sf::Socket::Done)
{
std::cout << "Error while accepting new connection\n";
delete tmp;
}
else {
selector.add(*tmp);
clients.push_back(tmp);
}
}
else {
for (int i = 0; i < clients.size(); i++)
{
//new incoming packet
if(selector.isReady(*(clients[i])))
{
sf::Packet pakiet;
if (clients[i]->receive(pakiet) != sf::Socket::Done)
{
std::cout << "Error while receiving packet\n";
}
else {
int x;
pakiet >> x;
std::cout << "Recived new data!!!: " << x << std::endl;
}
}
}
}
}
}
}
Server::~Server()
{
for (int i = 0; i < clients.size();i++)
{
delete clients[i];
}
}
You created a client which connected to the server, sent one package and at the end of the scope was destroyed. Socket at the client side doesn't exist and the connection between client-server is closed. So, how do you want to get the information about closed connection at server side ?
Function isReady returns true, then you call receive for this socket and as output you get one of Status codes: Done, NotReady, Disconnected, Error. You need to check if status is Disconnected, if so, the client socket should be removed from selector.
if(selector.isReady(*(clients[i])))
{
sf::Packet pakiet;
if ( clients[i]->receive(pakiet) == sf::Socket::Done)
{
// process data
}
else if ( clients[i]->receive(pakiet) == sf::Socket::Disconnected ) {
// delete socket, remove from selector
}
else {
//
}
}

How do I receive data from NTP server?

I have no idea why send data is 48 bytes 010,0,0..., someone can explain? the problem is buffer for data received, I don't know how big he should be, and even if I receive data, how to make normal time from it?
Here's the code:
#include <iostream>
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define WIN32_MEAN_AND_LEAN
#include <winsock2.h>
#include <windows.h>
#include <time.h>
using namespace std;
class HRException
{
public:
HRException() :
m_pMessage("") {}
virtual ~HRException() {}
HRException(const char *pMessage) :
m_pMessage(pMessage) {}
const char * what() { return m_pMessage; }
private:
const char *m_pMessage;
};
const int REQ_WINSOCK_VER = 2; // Minimum winsock version required
const char DEF_SERVER_NAME[] = "0.pl.pool.ntp.org";
const int SERVER_PORT = 123;
const int TEMP_BUFFER_SIZE = 128;
const char msg[48] = { 010,0,0,0,0,0,0,0,0 };
// IP number typedef for IPv4
typedef unsigned long IPNumber;
IPNumber FindHostIP(const char *pServerName)
{
HOSTENT *pHostent;
// Get hostent structure for hostname:
if (!(pHostent = gethostbyname(pServerName)))
throw HRException("could not resolve hostname.");
// Extract primary IP address from hostent structure:
if (pHostent->h_addr_list && pHostent->h_addr_list[0])
return *reinterpret_cast<IPNumber*>(pHostent->h_addr_list[0]);
return 0;
}
void FillSockAddr(sockaddr_in *pSockAddr, const char *pServerName, int portNumber)
{
// Set family, port and find IP
pSockAddr->sin_family = AF_INET;
pSockAddr->sin_port = htons(portNumber);
pSockAddr->sin_addr.S_un.S_addr = FindHostIP(pServerName);
}
bool RequestHeaders(const char *pServername)
{
SOCKET hSocket = INVALID_SOCKET;
char tempBuffer[TEMP_BUFFER_SIZE];
sockaddr_in sockAddr = { 0 };
bool bSuccess = true;
try
{
// Lookup hostname and fill sockaddr_in structure:
cout << "Looking up hostname " << pServername << "... ";
FillSockAddr(&sockAddr, pServername, SERVER_PORT);
cout << "found.\n";
// Create socket
cout << "Creating socket... ";
if ((hSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == INVALID_SOCKET)
throw HRException("could not create socket.");
cout << "created.\n";
// Connect to server
cout << "Attempting to connect to " << inet_ntoa(sockAddr.sin_addr)
<< ":" << SERVER_PORT << "... ";
if (connect(hSocket, reinterpret_cast<sockaddr*>(&sockAddr), sizeof(sockAddr)) != 0)
throw HRException("could not connect.");
cout << "connected.\n";
cout << "Sending request... ";
// send request part 1
if (send(hSocket, msg, sizeof(msg) , 0) == SOCKET_ERROR)
throw HRException("failed to send data.");
cout << "request sent.\n";
cout << "Dumping received data...\n\n";
// Loop to print all data
recv(hSocket, tempBuffer, sizeof(tempBuffer), 0); // <-- the problem
///
//part where we take time out of tempBuffer
///
}
catch (HRException e)
{
cerr << "\nError: " << e.what() << endl;
bSuccess = false;
}
if (hSocket != INVALID_SOCKET)
{
closesocket(hSocket);
}
return bSuccess;
}
int main(int argc, char* argv[])
{
int iRet = 1;
WSADATA wsaData;
cout << "Initializing winsock... ";
if (WSAStartup(MAKEWORD(REQ_WINSOCK_VER, 0), &wsaData) == 0)
{
// Check if major version is at least REQ_WINSOCK_VER
if (LOBYTE(wsaData.wVersion) >= REQ_WINSOCK_VER)
{
cout << "initialized.\n";
// Set default hostname:
const char *pHostname = DEF_SERVER_NAME;
// Set custom hostname if given on the commandline:
if (argc > 1)
pHostname = argv[1];
iRet = !RequestHeaders(pHostname);
}
else
{
cerr << "required version not supported!";
}
cout << "Cleaning up winsock... ";
// Cleanup winsock
if (WSACleanup() != 0)
{
cerr << "cleanup failed!\n";
iRet = 1;
}
cout << "done.\n";
}
else
{
cerr << "startup failed!\n";
}
int x;
cin >> x;
return iRet;
}
Most part of code is from madwizard.org
Ok it works, main part of code:
const char msg[48] = { 010,0,0,0,0,0,0,0,0 };
if (send(hSocket, msg, sizeof(msg) , 0) == SOCKET_ERROR)
throw HRException("failed to send data.");
cout << "request sent.\n";
cout << "Dumping received data...\n\n";
char tempBuffer[1024];
int bytes = recv(hSocket, tempBuffer, sizeof(tempBuffer), 0);
cout << "bytes received: " << bytes << endl;
time_t tmit;
tmit = ntohl(((time_t*)tempBuffer)[4]);
tmit -= 2208988800U;
cout << ctime(&tmit);
No idea why data that we send is
msg[48] = { 010,0,0,0,0,0,0,0,0 };
and why received data contains many numbers? for example if change code to
tmit = ntohl(((time_t*)tempBuffer)[6]);
I will get date 2008y, why?
Guys why so many minuses?, still waiting for an explanation :D
Here's whole code http://pastebin.com/Sv3ERGfV , dont forget to link ws2_32.lib
Similar to my issue when trying to query the time from a self-hostet Windows-NTP-Server with the C++ library NTPClient which uses boost for the network tasks, msg[48] = { 010,0,0,0,0,0,0,0,0 }; configures the ntp.flags.mode. After comparing the network traffic of w32tm /stripchart /computer:10.159.96.65 using Wireshark, flag 27 or 11 seem to be the choices for my usecase:
Comparison of NTP network packages
tmit = ntohl(((time_t*)tempBuffer)[6]); extracts the data from the received package. It looks like
4 yields the reference time (last sync with timeserver I assume),
8 the time when server received request and
10 the transmit time (which should be almost equal).

C++ Winsock SO_REUSEADDR: accept() not returning socket on connection

I am completely new to Winsock and have been trying to write a small HTTP server listening on localhost for educational purposes mainly. Currently the server simply returns a web page to whoever connects to it, without parsing any request.
Logically, I must always be listening for new connections on the listening port (I chose 81 here) after I finish with a client and close the connection, so I've googled quite a bit and found that I should probably be using SO_REUSEADDR for this purpose, but maybe I got it wrong. I am using Firefox as the client.
The first connection always goes without a hitch. However, the second time a client attempts to connect, the accept function doesn't seem to accept the connection. On the other hand, I can see that a connection IS established at that time using a utility that watches local ports (CurrPorts). I've looked for hours for a solution and have tried to make the socket non-blocking, but no luck. What did I do wrong?
#pragma comment(lib,"Ws2_32.lib")
#include <WinSock2.h>
#include <iostream>
#include <thread>
#include <string>
#include <array>
#include <ctime>
#include <winerror.h>
inline std::string getAddress(sockaddr_in* sin)
{
std::string res = std::to_string(sin->sin_addr.S_un.S_un_b.s_b1) + '.' + std::to_string(sin->sin_addr.S_un.S_un_b.s_b2) + '.' + std::to_string(sin->sin_addr.S_un.S_un_b.s_b3) + '.' + std::to_string(sin->sin_addr.S_un.S_un_b.s_b4);
return res;
}
void acceptTCP(SOCKET& origSock)
{
SOCKET tempSock = SOCKET_ERROR;
struct sockaddr* sa = new sockaddr();
int size = sizeof(*sa);
while (tempSock == SOCKET_ERROR)
{
tempSock = accept(origSock, sa, &size);
int err = WSAGetLastError();
if (err != 0 && err != WSAEWOULDBLOCK) std::cout << "\r\n" << err;
}
struct sockaddr_in* sin = (struct sockaddr_in*)sa;
std::cout << "\r\nConnected to " << getAddress(sin) << ":" << htons(sin->sin_port);
origSock = tempSock;
}
int closeSocket(SOCKET socket)
{
shutdown(socket, 2); //I've tried using 0
std::clock_t start = std::clock();
char buf[1];
while ((std::clock() - start) / (double)CLOCKS_PER_SEC < 5)
{
int res = recv(socket, buf, strlen(buf), IPPROTO_TCP);
//std::cout << "\r\n" << res;
bool br = false;
switch (res)
{
case 0: br = true; break; //client closed connection
case -1:
{
int err = WSAGetLastError();
if (err != WSAEWOULDBLOCK && err != WSAEINTR) //client closed connection
{
br = true;
break;
}
else std::cout << "\r\nError on close socket: " << err;
}
default: exit(1); //data is being sent after shutdown request
};
if (br) break;
//if (res == -1) std::cout << ": " << WSAGetLastError();
//else std::cout << ": " << buf;
//Sleep(1000);
}
return closesocket(socket);
}
int main()
{
WSADATA WsaDat;
if (WSAStartup(MAKEWORD(1, 1), &WsaDat) != 0) std::cout << "???";
while (true)
{
SOCKET socket0 = socket(AF_INET, SOCK_STREAM, 0);
if (socket0 == INVALID_SOCKET) std::cout << "Invalid socket!";
struct sockaddr_in saServer;
saServer.sin_family = AF_INET;
saServer.sin_port = htons(81);
saServer.sin_addr.S_un.S_un_b.s_b1 = 127;
saServer.sin_addr.S_un.S_un_b.s_b2 = 0;
saServer.sin_addr.S_un.S_un_b.s_b3 = 0;
saServer.sin_addr.S_un.S_un_b.s_b4 = 1;
int enable = 1;
if (setsockopt(socket0, SOL_SOCKET, SO_REUSEADDR, (const char*)&enable, sizeof(int)) < 0)
std::cout << "setsockopt(SO_REUSEADDR) failed";
u_long iMode = 1;
ioctlsocket(socket0, FIONBIO, &iMode);
if (bind(socket0, (SOCKADDR*)&saServer, sizeof(saServer)) == SOCKET_ERROR) std::cout << "\r\nSocket Error " << WSAGetLastError();
else std::cout << "Socket bound!";
listen(socket0, 1);
std::thread threadConnection(&acceptTCP, std::ref(socket0)); //I use a thread in case I will want to handle more than one connection at a time in the future, but it serves no purpose here
threadConnection.join();
std::string content = "<!DOCTYPE html><html><head><title>test</title></head><body><p>test</p></body></html>";
std::string response = "HTTP/1.1 200 OK\r\nServer: myServer\r\nContent-Type: text/html\r\nConnection: close\r\nContent-Length: " + std::to_string(content.length()) + "\r\n\r\n" + content;
std::cout << "\r\n" << send(socket0, response.c_str(), strlen(response.c_str())*sizeof(char), 0);
Sleep(1000);
std::cout << "\r\n" << closeSocket(socket0);
}
WSACleanup();
}
Here's how your code should work:
Main function:
Open listening socket.
Bind it.
Call listen.
Call accept.
Dispatch a thread to handle the socket we just accepted.
Go to step 4.
Note that the thread never touches the listening socket.