How to handle broadcast messages? - c++

I created a client that sends broadcast messages over my local network:
#define SERVER_PORT "7777"
int main(int argc, char** argv)
{
int sockfd;
struct addrinfo hints, *servinfo, *p;
int rv;
int numbytes;
if (argc != 3) {
fprintf(stderr,"usage: client ip message\n");
exit(1);
}
memset(&hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
if ((rv = getaddrinfo(argv[1], SERVER_PORT, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
return 1;
}
for(p = servinfo; p != NULL; p = p->ai_next)
{
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
{
perror("talker: socket");
continue;
}
break;
}
int broadcast = 1;
if (setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broadcast, sizeof broadcast) == -1)
{
perror("setsockopt (SO_BROADCAST)");
exit(1);
}
if (p == NULL) {
fprintf(stderr, "talker: failed to bind socket\n");
return 2;
}
std::cout << "type any number to start: ";
int temp = 0;
std::cin >> temp;
while(1)
{
if ((numbytes = sendto(sockfd, argv[2], strlen(argv[2]), 0, p->ai_addr, p->ai_addrlen)) == -1)
{
std::cout << "[ERROR] sendto error: " << errno << std::endl;
continue;
}
std::cout << "[INFO] sended: " << numbytes << " bytes!" << std::endl;
sleep(5);
}
freeaddrinfo(servinfo);
close(sockfd);
return 0;
}
Yes I'm using while(1) loop without a chance for exit (that's doesn't matter).
This client successfully sends broadcast messages. But how to handle them from the server side? I tried to do it next way (server was written as a class. Here is to main methods):
virtual void Init() override
{
addrinfo hints;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM;
int iRes = -1;
if((iRes = getaddrinfo(m_sIpAddress.c_str(), std::to_string(m_iPort).c_str(), &hints, &m_pAddr)) != 0)
{
std::cout << "getaddrinfo error: " << gai_strerror(iRes) << std::endl;
exit(-1);
}
std::cout << "getaddrinfo success!" << std::endl;
m_iSocket = socket(AF_INET, SOCK_DGRAM, 0);
if(m_iSocket == -1)
{
std::cout << "socket error: " << errno << std::endl;
exit(-1);
}
std::cout << "socket created!" << std::endl;
if((iRes = bind(m_iSocket, m_pAddr->ai_addr, m_pAddr->ai_addrlen)) == -1)
{
std::cout << "bind error: " << errno << std::endl;
exit(-1);
}
std::cout << "binded successfully!" << std::endl;
}
virtual void Listen() override
{
int iNumOfReadBytes = -1;
std::string msg;
msg.resize(1024);
sockaddr_in clientAddr;
socklen_t addrLen = sizeof(sockaddr);
while(true)
{
if((iNumOfReadBytes = recvfrom(m_iSocket, const_cast<char*>(msg.c_str()), 1024-1, 0, reinterpret_cast<sockaddr*>(&clientAddr), &addrLen)) == -1)
{
std::cout << "recvfrom error: " << errno << std::endl;
continue;
}
char ip[16]; //string for ip
inet_ntop(AF_INET, &(clientAddr.sin_addr), ip, 16);
std::cout << "[";
for (int i = 0; i < 16; i++)
{
std::cout << ip[i];
}
std::cout << "]";
std::cout << msg << std::endl;
}
}
Init method is responsible for initializing of the socket and Listen method for handling broadcast messages.
Lets imagine that server has 192.168.88.123 ip address while client sends broadcast messages on 192.168.88.255 ip.Server should get messages from client, I suppose, but it never actually receives them.
I also tried to configure client to send messages to the 255.255.255.255 ip. Messages were sent, but server didn't get them.
So is it code's fault? Or something with IPs (if it is so, that's very weird)?
P.S. I read the Beej's networking guide. There was a broadcast example, but only from client side. But I'm interesting in handling of such messages from server side.
Thanks!

The code looks fine (though, there are some things I would suggest tweaking, like specifying the IPPROTO_UDP protocol when calling getaddrinfo(), and including the AI_PASSIVE flag on the server side).
Not all network routers allow UDP broadcasts by default. So check your router configuration, as well as your OS firewall. Use a packet sniffer, like WireShark, to make sure the packets are actually leaving the sender's NIC and arriving on the receiver's NIC, that they are not being blocked during transit.
Also, double check to make sure your client socket and server socket are bound to the same subnet, and that the client is sending to the correct broadcast IP for that subnet. Given the server IP of 192.168.88.123, the broadcast IP would be 192.168.88.255 only if the subnet mask is 255.255.255.0, is that the case in your environment?
Also, check if you have the same problem if your server binds to 0.0.0.0 (listens on all local NICs) instead of binds to 192.168.88.123 (listens on just 1 NIC).

Related

Can't send Message from Server( C++ Socket)

I'm new to C++ Socket and my Server can't send message to its client. The send() function return -1 always and it seems to have a problem with accpSocket. However Client can do that smoothly and I don't know what's wrong. Please help me thank you so much!
Server
#include<WinSock2.h>
#include<WS2tcpip.h>
#include<iostream>
#include<sdkddkver.h>
#include<winsock.h>
using namespace std;
int main() {
SOCKET serverSocket, acceptSocket = INVALID_SOCKET;
int port = 2403;
WSADATA wsaData;
int wsaerr;
//Step 1: Set up dll
WORD versionRequested = MAKEWORD(2, 2);
wsaerr = WSAStartup(versionRequested, &wsaData);
if (wsaerr)
cout << "The winsock dll not found";
else {
cout << "The winsock dll found\n";
cout << "Winsock dll status: " << wsaData.szSystemStatus << endl;
}
//Step 2: Set up server socket
serverSocket = INVALID_SOCKET;
serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (serverSocket == INVALID_SOCKET) {
cout << "Error at socket: " << WSAGetLastError();
WSACleanup();
return 0;
}
else
cout << "Server socket successfull!\n";
//Step 3: Binding socket
sockaddr_in service;
service.sin_family = AF_INET;
service.sin_addr.S_un.S_addr = INADDR_ANY;
service.sin_port = htons(port);
if (bind(serverSocket, (sockaddr*)&service, sizeof(service)) == SOCKET_ERROR) {
cout << "Binding failed! " << WSAGetLastError();
return 0;
}
else
cout << "Binding complete!\n";
// Step 4: Listen to the connections
if (listen(serverSocket, 1) == SOCKET_ERROR) {
cout << "Listen failed! " << WSAGetLastError();
return 0;
}
else
cout << "Waiting for connections ...";
SOCKET accpSocket = accept(serverSocket, NULL, NULL);
if (accpSocket == INVALID_SOCKET) {
cout << "Accepting failed! " << WSAGetLastError();
WSACleanup();
return -1;
}
else
cout << "Accept connection!\n";
char recvMess[2000];
char sendMess[2000];
int byterecv = recv(accpSocket, recvMess, sizeof(recvMess), 0);
cout << "Client: " << recvMess << endl;
cout << "Server: ";
cin.getline(sendMess, 2000);
int bytesend = send(acceptSocket, sendMess, 2000, 0);
if (bytesend <= 0)
cout << "Unsent";
return 0;
}
Client
#include<iostream>
#include<WinSock2.h>
#include<WS2tcpip.h>
using namespace std;
int main() {
int port = 2403;
WSADATA wsaData;
int wsaerr;
SOCKET clientSocket;
WORD versionRequested = MAKEWORD(2, 2);
wsaerr = WSAStartup(versionRequested, &wsaData);
if (wsaerr)
cout << "Winsock dll not found!";
else {
cout << "Winsock dll is ok!\n";
cout << "Status: " << wsaData.szSystemStatus << endl;
}
clientSocket = INVALID_SOCKET;
clientSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (clientSocket == INVALID_SOCKET) {
cout << "Set up client socket failed" << WSAGetLastError();
WSACleanup();
return 0;
}
else
cout << "Set up complete!\n";
sockaddr_in clientService;
clientService.sin_family = AF_INET;
clientService.sin_port = htons(port);
if (inet_pton(clientService.sin_family, "127.0.0.1", &clientService.sin_addr) <= 0) {
cout << "Invalid address!";
return -1;
}
if ((connect(clientSocket, (SOCKADDR*)&clientService, sizeof(clientService))) == SOCKET_ERROR) {
cout << "Connection failed!\n";
WSACleanup();
return 0;
}
else
cout << "Connection complete!\n";
char sendMess[2000];
char recvMess[2000];
cout << "Client: ";
cin.getline(sendMess, 2000);
int bytesend = send(clientSocket, sendMess, 2000, 0);
int byterecv = recv(clientSocket, recvMess, 2000, 0);
if (byterecv <= 0)
cout << "Nothing";
else
cout << "Server" << recvMess << endl;
return 0;
}
int bytesend = send(acceptSocket, sendMess, 2000, 0);
is not sending to a connected socket. acceptSocket was defined at the top of main and then ignored up until the call to send
As a general rule of thumb, keep variable definition close to first use.
In the server at
SOCKET serverSocket, acceptSocket = INVALID_SOCKET;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Killlllll meeeeee!!!!
remove acceptSocket to prevent future mistakes and in
int bytesend = send(acceptSocket, sendMess, 2000, 0);
replace acceptSocket with the socket that was actually accepted, accpSocket.
Side notes:
Never ignore the return codes.
int byterecv = recv(accpSocket, recvMess, sizeof(recvMess), 0);
could fail and return -1 or return 0 if the socket was disconnected, yet the program will still
cout << "Client: " << recvMess << endl;
And worse, there's no guarantee that recvMess will be null-terminated, recv on a streaming socket gives you what the socket has available or becomes available up to the maximum number of bytes requested, so if there is any data read, make sure byterecv is a valid index in recvMess by only reading sizeof(recvMess) - 1 bytes and then forcing termination with recvMess[byterecv] = '\0'; before printing.
send(acceptSocket, sendMess, 2000, 0); sends all 2000 bytes of sendMess regardless of how many bytes were read with cin.getline(sendMess, 2000);. Use
send(acceptSocket, sendMess, cin.gcount(), 0);
instead. Add on an extra byte (cin.gcount() + 1) if you want to send the null terminator.

Cause of duplicate and missing messages in multithreaded C++ winsock socket?

void TCPConnectionV5::startServer()
{
/* Initialize Winsock */
int start;
sockaddr_in SERVER;
SERVER.sin_family = AF_INET;
SERVER.sin_addr.s_addr = INADDR_ANY;
//SERVER.sin_port = htons(stoi(DEFAULT_PORT));
SERVER.sin_port = htons(1787);
start = WSAStartup(MAKEWORD(2, 2), &_wsaData);
if (start != 0)
{
cout << "Error on WSAStartup: " << start << endl;
}
/* Create socket that will connect to server */
_listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
_socketCollection.push_back(_listener);
if (_listener == INVALID_SOCKET)
{
cout << "Error creating socket to connect to server: " << WSAGetLastError() << endl;
WSACleanup();
}
/* Bind the socket */
start = bind(_listener, (sockaddr*)&SERVER, sizeof(SERVER));
if (start == SOCKET_ERROR)
{
cout << "Error on bind:" << WSAGetLastError() << endl;
closesocket(_listener);
WSACleanup();
}
/* Create the listener socket */
start = listen(_listener, 16);
if (start == SOCKET_ERROR)
{
cout << "Error on entering the listening state: " << start << endl;
closesocket(_listener);
WSACleanup();
}
printTime();
cout << "Server entered listening state" << endl;
/* Create the thread */
sockaddr_in client;
int clientSize = sizeof(client);
while (true)
{
SOCKET messager = accept(_listener, (struct sockaddr*)&client, &clientSize);
_socketCollection.push_back(messager);
locker.lock();
printTime();
if (messager != SOCKET_ERROR)
{
cout << "Client Connection success!" << endl;
cout << "Messager: " << messager << endl;
locker.unlock();
std::thread newThread([&] {this->exchange(messager); });
newThread.detach();
}
else
{
locker.unlock();
}
}
}
DWORD TCPConnectionV5::exchange(SOCKET messager)
{
int bytesSent = sendMessage(messager, msg);
if (bytesSent <= 0)
{
closesocket(messager);
return -1;
}
int bytesReceived = receiveMessage(messager);
if (bytesReceived <= 0)
{
closesocket(messager);
return -1;
}
}
I noticed that when the server connects with multiple clients, that there sometimes appear to be duplicate messages that send to some clients, which is accompanied by missing messages to another client application. I have mutex lock/unlock in place for sending/receiving messages, but what's causing these duplicate/missing messages? Is there some underlying issue I have to address regarding the threads?

C++ Winsock Library Termination When connecting with Putty Client on 127.0.0.1

I'm trying to create a server client that once its working I can pass a vector into it and send it to a client program through ssh like putty. The issue is whenever I try to connect raw or ssh with putty on 127.0.0.1:45000 the program terminates once it connects.
Here is my code:
#include <iostream>
#include <WS2tcpip.h>
#include <string>
#pragma comment (lib, "ws2_32.lib")
using namespace std;
void main()
{
// Initialize winsock
WSADATA wsData;
WORD ver = MAKEWORD(2, 2);
int wsOk = WSAStartup(ver, &wsData);
if (wsOk != 0)
{
cerr << "Can't Intitialze winsock! Quiting" << endl;
return;
}
// Create a socket to bind
SOCKET listening = socket(AF_INET, SOCK_STREAM, 0);
if (listening == INVALID_SOCKET)
{
cerr << "Can't create a socket! Quitting" << endl;
}
// Bind the socket to an ip address to the port
sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons(45000);
hint.sin_addr.S_un.S_addr = INADDR_ANY; // could also use inet_pton
bind(listening, (sockaddr*)&hint, sizeof(hint));
// Tell winsock the socket is for listening
listen(listening, SOMAXCONN);
// Wait for connection
sockaddr_in client;
int clientSize = sizeof(client);
SOCKET clientSocket = accept(listening, (sockaddr*)&client, &clientSize);
char host[NI_MAXHOST]; //Clients remote name
char service[NI_MAXHOST]; // Service (port) the client is on
ZeroMemory(host, NI_MAXHOST);
ZeroMemory(service, NI_MAXHOST); // use mem set of linux
if (getnameinfo((sockaddr*)&client, sizeof(client), host, NI_MAXHOST, service, NI_MAXSERV, 0) == 0)
{
cout << host << " connected on port " << service << endl;
return;
}
else
{
inet_ntop(AF_INET, &client.sin_addr, host, NI_MAXHOST);
cout << host << " connected on port " <<
ntohs(client.sin_port) << endl;
return;
}
// Close listening socket
closesocket(listening);
// while loop; accept and echo message back to client
char buf[4096];
while (true)
{
ZeroMemory(buf, 4096);
// wait for client to send data
int bytesReceived = recv(clientSocket, buf, 4096, 0);
if (bytesReceived == SOCKET_ERROR)
{
cerr << "Error in recv(). Quitting" << endl;
break;
}
if (bytesReceived == 0)
{
cout << "Client Disconnected, bytes 0" << endl;
break;
}
// echo message back to client
send(clientSocket, buf, bytesReceived + 1, 0);
// Close the socket
}
closesocket(clientSocket);
// Shutdown winsock
WSACleanup();
}
I'm writing it and compiling in Visual Studio 2019.
Here's the message I get from Putty when trying to connect with the ssh option or raw.
If anyone can help it would be greatly appreciated. Thanks!
When calling getnameinfo(), you are return'ing from main() immediately, without calling closesocket() first, regardless of whether getnameinfo() is successful or fails. This is the root of your Putty error. You are explicitly exiting your app whenever a client connects, without informing the client that the connection is being closed.
More generally, if accept() is successful (and socket(), too), you should always call closesocket() on the returned SOCKET, regardless of anything else happening in your code (same with WSACleanup() if WSAStartup() is successful).
There are several other mistakes in your code:
It is illegal for main() to have a non-int return type (though some compilers allow this, as a non-standard extension. Don't rely on this!).
You are missing a return from main() if socket() fails.
You are not checking for errors on bind(), listen(), accept(), or send().
There is no point in setting the backlog to SOMAXCONN if you are only going to accept() 1 client ever.
you have a potential buffer overflow when calling send(). Imagine if recv() returned exactly 4096 bytes received. Sending bytesReceived + 1 number of bytes back to the client would go out of bounds of your buf array.
With that said, try something more like this:
#include <iostream>
#include <WS2tcpip.h>
#include <string>
#pragma comment (lib, "ws2_32.lib")
using namespace std;
int main()
{
// Initialize winsock
WSADATA wsData;
WORD ver = MAKEWORD(2, 2);
int errCode = WSAStartup(ver, &wsData);
if (errCode != 0)
{
cerr << "Can't initialize winsock! Error " << errCode << ". Quitting" << endl;
return 0;
}
// Create a socket to bind
SOCKET listening = socket(AF_INET, SOCK_STREAM, 0);
if (listening == INVALID_SOCKET)
{
errCode = WSAGetLastError();
cerr << "Can't create listening socket! Error " << errCode << ". Quitting" << endl;
WSACleanup();
return 0;
}
// Bind the socket to an ip address to the port
sockaddr_in hint = {};
hint.sin_family = AF_INET;
hint.sin_port = htons(45000);
hint.sin_addr.s_addr = INADDR_ANY; // could also use inet_pton
if (bind(listening, (sockaddr*)&hint, sizeof(hint)) == SOCKET_ERROR)
{
errCode = WSAGetLastError();
cerr << "Can't bind listening socket! Error " << errCode << ". Quitting" << endl;
closesocket(listening);
WSACleanup();
return 0;
}
// Tell winsock the socket is for listening
if (listen(listening, 1) == SOCKET_ERROR)
{
errCode = WSAGetLastError();
cerr << "Can't open listening socket! Error " << errCode << ". Quitting" << endl;
closesocket(listening);
WSACleanup();
return 0;
}
// Wait for connection
sockaddr_in client;
int clientSize = sizeof(client);
SOCKET clientSocket = accept(listening, (sockaddr*)&client, &clientSize);
if (clientSocket == INVALID_SOCKET)
{
errCode = WSAGetLastError();
cerr << "Can't accept a client! Error " << errCode << ". Quitting" << endl;
closesocket(listening);
WSACleanup();
return 0;
}
char host[NI_MAXHOST]; //Clients remote name
char service[NI_MAXHOST]; // Service (port) the client is on
ZeroMemory(host, NI_MAXHOST);
ZeroMemory(service, NI_MAXHOST); // use mem set of linux
if (getnameinfo((sockaddr*)&client, clientSize, host, NI_MAXHOST, service, NI_MAXSERV, 0) == 0)
{
cout << host << " connected on port " << service << endl;
}
else
{
inet_ntop(AF_INET, &(client.sin_addr), host, NI_MAXHOST);
cout << host << " connected on port " << ntohs(client.sin_port) << endl;
}
// Close listening socket
closesocket(listening);
listening = INVALID_SOCKET;
// while loop; accept and echo message back to client
char buf[4096];
while (true)
{
// wait for client to send data
int bytesReceived = recv(clientSocket, buf, sizeof(buf), 0);
if (bytesReceived == SOCKET_ERROR)
{
errCode = WSAGetLastError();
cerr << "Error reading from client: " << errCode << ". Quitting" << endl;
break;
}
if (bytesReceived == 0)
{
cout << "Client Disconnected" << endl;
break;
}
// echo message back to client
char *ptr = buf;
int bytesToSend = bytesReceived;
do
{
int bytesSent = send(clientSocket, ptr, bytesToSend, 0);
if (bytesSent == SOCKET_ERROR)
break;
ptr += bytesSent;
bytesToSend -= bytesSent;
}
while (bytesToSend > 0);
if (bytesToSend != 0)
{
errCode = WSAGetLastError();
cerr << "Error writing to client: " << errCode << ". Quitting" << endl;
break;
}
}
// Close the client socket
closesocket(clientSocket);
// Shutdown winsock
WSACleanup();
return 0;
}

Socket programming between different networks

I'm developing a client-server program in c++ to transfer data from one computer to another.
Everything works fine but now I was asked to make it work on computers on different networks. I have searched everywhere but can't find a reliable solution.
I've seen the TCP hole punching solution but can't seem to find anywhere how to do it in c++.
I want it to work like teamviewer but without an intermediary server.
Connect my client (on one computer) to the server (on another computer in a different network) all programmatically.
#include "../include/ip_tunnel_ms_windows_20180815.h"
#include "../include/message_processor_common_20190410.h"
SOCKET clientSocket;
int n = 0;
void IPTunnel::initialize(void)
{
if (inputSignals.empty()) {
printf("server%d\n", n++);
if (!server()) {
printf("Error opening server\n");
::exit(1);
}
}
else {
printf("client%d\n", n++);
if (!client()) {
printf("Error opening client\n");
::exit(1);
}
}
}
bool IPTunnel::runBlock(void)
{
.....
(transmit data)
.....
return true;
}
void IPTunnel::terminate(void) {
closesocket(clientSocket);
WSACleanup();
}
bool IPTunnel::server() {
WSADATA wsData;
WORD ver = MAKEWORD(2, 2);
int wsOk = WSAStartup(ver, &wsData);
if (wsOk != 0)
{
cerr << "Can't Initialize winsock! Quitting" << endl;
return false;
}
SOCKET listening = socket(AF_INET, SOCK_STREAM, 0);
if (listening == INVALID_SOCKET)
{
cerr << "Can't create a socket! Quitting" << endl;
return false;
}
sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = ntohs(tcpPort);
//inet_pton(AF_INET, (PCSTR)remoteMachineIpAddress.c_str(), &hint.sin_addr.s_addr); // hint.sin_addr.S_un.S_addr = inet_addr(ipAddressServer.c_str());
hint.sin_addr.S_un.S_addr = INADDR_ANY;
if (::bind(listening, (sockaddr*)& hint, sizeof(hint)) < 0) {
printf("\n ERROR on binding");
return false;
}
if (listen(listening, SOMAXCONN) == -1) {
printf("\n ERROR on binding");
return false;
}
sockaddr_in client;
int clientSize = sizeof(client);
clientSocket = accept(listening, (sockaddr*)& client, &clientSize);
char host[NI_MAXHOST];
char service[NI_MAXSERV];
ZeroMemory(host, NI_MAXHOST);
ZeroMemory(service, NI_MAXSERV);
if (getnameinfo((sockaddr*)& client, sizeof(client), host, NI_MAXHOST, service, NI_MAXSERV, 0) == 0)
{
cout << host << " connected on port " << service << endl;
}
else
{
inet_ntop(AF_INET, &client.sin_addr, host, NI_MAXHOST);
cout << host << " connected on port " <<
ntohs(client.sin_port) << endl;
}
return true;
}
bool IPTunnel::client() {
WSAData data;
WORD ver = MAKEWORD(2, 2);
int wsResult = WSAStartup(ver, &data);
if (wsResult != 0)
{
cerr << "Can't start Winsock, Err #" << wsResult << endl;
return false;
}
clientSocket = socket(AF_INET, SOCK_STREAM, 0);
if (clientSocket == INVALID_SOCKET)
{
cerr << "Can't create socket, Err #" << WSAGetLastError() << endl;
WSACleanup();
return false;
}
sockaddr_in hint;
hint.sin_family = AF_INET;
hint.sin_port = htons(tcpPort);
inet_pton(AF_INET, remoteMachineIpAddress.c_str(), &hint.sin_addr);
int connResult = -2;
while (connResult != 0 || numberOfTrials == 0) {
connResult = connect(clientSocket, (sockaddr*)& hint, sizeof(hint));
if (connResult == SOCKET_ERROR)
{
cerr << "Can't connect to server, Err #" << WSAGetLastError() << endl;
cerr << "Waiting " << timeIntervalSeconds << " seconds." << endl;
}
Sleep(timeIntervalSeconds * 1000);
;
if (--numberOfTrials == 0) {
cerr << "Reached maximum number of attempts." << endl;
::exit(1);
}
}
cout << "Connected!\n";
return true;
}
The definition of hole punching includes:
Both clients initiate a connection to an unrestricted server
You say:
I want it to work like teamviewer but without an intermediary server
That's not possible in general.

Winsock Error 10022 on Listen

i'm making a small IRC server, but I've come across a problem; upon trying to listen to the socket, i get error 10022 (Invalid Argument).
The error also appears on accept(), but this is because the socket isn't listening (the problem i'm posting about).
I didn't include the accept function because i feel it isn't necessary and would be adding pointless code.
#include <iostream>
#include <ws2tcpip.h>
#include <winsock2.h>
#include <thread>
#include <string>
#pragma comment(lib, "Ws2_32.lib")
#define maxConnections 10
class Server
{
struct sockaddr_storage their_addr;
struct addrinfo hints, *res;
struct addrinfo *servinfo;
int status;
SOCKET sock;
public:
void Start(const char *port);
};
void Server::Start(const char *port)
{
WSADATA WSAData;
if (WSAStartup(MAKEWORD(2, 0), &WSAData) != 0)
{
std::cout << "[ERROR]: " << GetLastError() << ".\n";
}
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
status = getaddrinfo(NULL, port, &hints, &res);
sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (sock == SOCKET_ERROR)
{
std::cout << "[ERROR]: " << WSAGetLastError() << "Bad Socket.\n";
}
bind(sock, res->ai_addr, res->ai_addrlen);
Error:
if (listen(sock, maxConnections) == SOCKET_ERROR)
{
std::cout << "[ERROR]: " << WSAGetLastError() << " Listening Failed.\n";
}
The code above details the socket creation and binding, all of which are successful (though not necessarily right). The socket creation including 'NULL' might be the issue.
Thanks :)
WSAStartup() and getaddrinfo() do not use (WSA)GetLastError(), they directly return the actual error code instead. You are not accounting for that in your error messages.
socket() returns INVALID_SOCKET on failure, not SOCKET_ERROR.
When using getaddrinfo() to create a listening socket, you should specify AI_PASSIVE in the addrinfo.ai_flags field of the hints parameter. That will fill the output addrinfo with data that is suitable to pass to bind().
Try something more like this:
class Server
{
private:
bool winsockStarted;
SOCKET sock;
...
public:
Server();
~Server();
bool Start(const char *port);
void Stop();
...
};
Server::Server()
: sock(INVALID_SOCKET), winsockStarted(false)
{
WSADATA WSAData = {0};
int status = WSAStartup(MAKEWORD(2, 0), &WSAData);
if (status != 0)
std::cout << "[ERROR]: " << status << " Unable to start Winsock." << std::endl;
else
winsockStarted = true;
}
Server::~Server()
{
Stop();
if (winsockStarted)
WSACleanup();
}
bool Server::Start(const char *port)
{
Stop();
struct addrinfo hints = {0};
struct addrinfo *res = NULL;
hints.ai_flags = AI_PASSIVE;
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
int status = getaddrinfo(NULL, port, &hints, &res);
if (status != 0)
{
std::cout << "[ERROR]: " << status << " Unable to get address info for Port " << port << "." << std::endl;
return false;
}
SOCKET newsock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (newsock == INVALID_SOCKET)
{
std::cout << "[ERROR]: " << WSAGetLastError() << " Unable to create Socket." << std::endl;
freeaddrinfo(res);
return false;
}
if (bind(newsock, res->ai_addr, res->ai_addrlen) == SOCKET_ERROR)
{
std::cout << "[ERROR]: " << WSAGetLastError() << " Unable to bind Socket." << std::endl;
freeaddrinfo(res);
closesocket(newsock);
return false;
}
freeaddrinfo(res);
if (listen(newsock, maxConnections) == SOCKET_ERROR)
{
std::cout << "[ERROR]: " << WSAGetLastError() << " Unable to Listen on Port " << port << "." << std::endl;
closesocket(newsock);
return false;
}
sock = newsock;
return true;
}
void Server::Stop()
{
if (sock != INVALID_SOCKET)
{
closesocket(sock);
sock = INVALID_SOCKET;
}
}
I reread my code and realized that i need to add a check here
status = getaddrinfo(NULL, port, &hints, &res);
I changed it to
if (status = getaddrinfo(NULL, port, &hints, &res) != 0)
{
std::cout << "[ERROR]: " << WSAGetLastError() << "Get Address Info failed.\n";
}
and startup was successful.
If someone could explain why, i'll improve my answer.