UDP Server is binding to the wrong port number - c++

I am attempting to make a very simple UDP client / server setup with non-blocking sockets. I've isolated my problem to the server not binding to the requested Port number and instead binding to one arbitrarily (though it does seem to consistently be the same port).
Is it possible to request a specific port number?
I am attempting to not be IPv4 or IPv6 specific. So I'm not sure if I need to use getaddrinfo if I don't care about the IP address, but I thought that this way it would find whatever was available, or better yet, listen to all IP addresses coming into this port? But it's reporting :: which causes me a problem if I try to tell my clients later what IP address to connect to.
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <Ws2tcpip.h>
#include <cstring>
#include <iostream>
#include <chrono>
#include <iomanip>
#include <sstream>
static uint16_t SERVER_PORT = 56000;
std::wstring get_error_string(const DWORD error)
{
std::wstring returnResult = L"";
LPWSTR errorText = NULL;
FormatMessageW(
FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPWSTR)&errorText,
0,
NULL);
if(NULL != errorText)
{
returnResult = errorText;
LocalFree(errorText);
errorText = NULL;
}
std::time_t t = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
std::tm tm = *std::localtime(&t);
std::wstringstream sstr;
sstr << std::put_time<wchar_t>(&tm, L"%d%m%y %H:%M:%S");
return sstr.str() + L" ERROR (" + std::to_wstring(error) + L"): " + returnResult;
}
std::string convert_to_string(const sockaddr* addr)
{
char* str;
if(addr->sa_family == AF_INET)
{
str = new char[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(reinterpret_cast<const sockaddr_in*>(addr)->sin_addr), str, INET_ADDRSTRLEN);
}
else
{
str = new char[INET6_ADDRSTRLEN];
inet_ntop(AF_INET6, &(reinterpret_cast<const sockaddr_in6*>(addr)->sin6_addr), str, INET6_ADDRSTRLEN);
}
std::string returnVal = str;
if(addr->sa_family == AF_INET)
returnVal += ":" + std::to_string(reinterpret_cast<const sockaddr_in*>(addr)->sin_port);
else
returnVal = "[" + returnVal + "]:" + std::to_string(reinterpret_cast<const sockaddr_in6*>(addr)->sin6_port);
return returnVal;
}
int main()
{
WSADATA wsaData;
int result = WSAStartup(MAKEWORD(2,2), &wsaData);
if(result != 0)
{
std::wcerr << L"Network init failed: ";
switch(result)
{
case WSASYSNOTREADY:
std::wcerr << L"WSASYSNOTREADY";
break;
case WSAVERNOTSUPPORTED:
std::wcerr << L"WSAVERNOTSUPPORTED";
break;
case WSAEINPROGRESS:
std::wcerr << L"WSAEINPROGRESS";
break;
case WSAEPROCLIM:
std::wcerr << L"WSAEPROCLIM";
break;
case WSAEFAULT:
std::wcerr << L"WSAEFAULT";
break;
default:
std::wcerr << L"Unknown error code " << std::to_wstring(result);
}
std::wcerr << std::endl;
return 0;
}
addrinfo hints = {};
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE;
addrinfo* serverInfoList;
int status = getaddrinfo(nullptr, std::to_string(SERVER_PORT).c_str(), &hints, &serverInfoList);
if(status != 0)
{
int lastError = WSAGetLastError();
std::wcerr << L"ERROR getaddrinfo: " << get_error_string(lastError) << std::endl;
}
addrinfo* addr = nullptr;
SOCKET sock;
for(addr = serverInfoList; addr != nullptr; addr = addr->ai_next)
{
sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
if(sock == INVALID_SOCKET)
{
int lastError = WSAGetLastError();
std::wcerr << L"ERROR socket: " << get_error_string(lastError) << std::endl;
continue; // we'll try the next option
}
u_long blocking = 1;
ioctlsocket(sock, FIONBIO, &blocking);
BOOL conrest = FALSE;
DWORD dwBytesReturned = 0;
WSAIoctl(sock, _WSAIOW(IOC_VENDOR, 12), &conrest, sizeof(conrest), NULL, 0, &dwBytesReturned, NULL, NULL);
int flag = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char*>(&flag), (socklen_t)sizeof(flag));
if(bind(sock, addr->ai_addr, addr->ai_addrlen) == SOCKET_ERROR)
{
int lastError = WSAGetLastError();
std::wcerr << L"ERROR bind: " << get_error_string(lastError) << std::endl;
if(closesocket(sock) == SOCKET_ERROR)
{
lastError = WSAGetLastError();
std::wcerr << L"ERROR closesocket: " << get_error_string(lastError) << std::endl;
}
continue;
}
sockaddr_storage connectedDetails;
std::memset(&connectedDetails, 0, sizeof(connectedDetails));
int addrLen = sizeof(connectedDetails);
if(getsockname(sock, reinterpret_cast<sockaddr*>(&connectedDetails), &addrLen) == SOCKET_ERROR)
{
int lastError = WSAGetLastError();
std::wcerr << L"ERROR getsockname: " << get_error_string(lastError) << std::endl;
}
std::cout << "Connected to " << convert_to_string(reinterpret_cast<sockaddr*>(&connectedDetails)) << std::endl;
break; // we've bound to one of them
}
if(addr == nullptr)
{
std::wcerr << L"ERROR: Could not bind to any addr for localhost port \"" << std::to_wstring(SERVER_PORT) << "\"" << std::endl;
return false;
}
freeaddrinfo(serverInfoList);
while(!(GetAsyncKeyState(VK_ESCAPE) & 0x8000))
{
}
result = WSACleanup();
if(result != 0)
{
std::wcerr << L"Network cleanup failed: ";
switch(result)
{
case WSANOTINITIALISED:
std::wcerr << L"WSANOTINITIALISED";
break;
case WSAENETDOWN:
std::wcerr << L"WSAENETDOWN";
break;
case WSAEINPROGRESS:
std::wcerr << L"WSAEINPROGRESS";
break;
default:
std::wcerr << L"Unknown error code " << std::to_wstring(result);
}
std::wcerr << std::endl;
}
return 0;
}
I'm expecting it to bind to port 56000 but it binds to other ports.

The easiest way is to build the sockaddr_in yourself, since you want to bind to all addresses.
// Create IPv6 UDP socket
X = socket(AF_INET6,SOCK_DGRAM,IPPROTO_UDP);
// Allow both ipv6/ipv4 bind
DWORD ag = 0;
setsockopt(X,IPPROTO_IPV6, IPV6_V6ONLY, (char*)&ag, sizeof(DWORD));
sockaddr_in s = {0};
s.sin_port = htons(SERVER_PORT);
s.sin_family= AF_INET6;
if (bind(X,(sockaddr*)&s,sizeof(s)) == 0) { ... }

Related

c++ multithreaded tcp server using futures and async

I recently started studying c++ and I'm following some tutorials. I was looking into sockets and I decided as a side project to create a small multithreaded server as you can see below.
I was trying to close the servers listening socket once CLIENTS_MAX_NUM reached and then reopen it once a socket disconnects, however this is giving me an error 10022 (WSAEINVAL) and I'm not quite sure what I'm doing wrong.
In case you want to reproduce the error just connect using telnet and close the client connection (ctrl+] , quit).
Any help would be greatly appreciated.
#include <iostream>
#include <vector>
#include <string>
#include <future>
#include <chrono>
#include <WS2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
static constexpr const unsigned int PORT = 5000;
static constexpr const unsigned int CLIENTS_MAX_NUM = 1;
static constexpr const unsigned int CLIENTS_QUEUE_NUM = 10;
SOCKET server_sock;
std::vector<std::future<void>> futures;
std::mutex mtx;
void initialize_winsock() {
WSAData wsData;
WORD ver = MAKEWORD(2, 2);
int wsResult = WSAStartup(ver, &wsData);
if (wsResult != 0) {
std::cerr << WSAGetLastError() << std::endl;
WSACleanup();
exit(EXIT_FAILURE);
}
}
void bind_server_socket() {
int keep_alive = 1;
int re_use = 1;
if (setsockopt(server_sock, SOL_SOCKET, SO_KEEPALIVE, (const char*)&keep_alive, sizeof(keep_alive)) == SOCKET_ERROR) {
closesocket(server_sock);
WSACleanup();
exit(EXIT_FAILURE);
}
if (setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&re_use, sizeof(re_use)) == SOCKET_ERROR) {
closesocket(server_sock);
WSACleanup();
exit(EXIT_FAILURE);
}
sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr.S_un.S_addr = INADDR_ANY;
memset(&server.sin_zero, 0, 8);
if (bind(server_sock, (sockaddr*)&server, sizeof(sockaddr)) == SOCKET_ERROR) {
std::cerr << WSAGetLastError() << std::endl;
closesocket(server_sock);
WSACleanup();
exit(EXIT_FAILURE);
}
}
void open_server_socket(bool &listening) {
server_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (server_sock == INVALID_SOCKET) {
std::cerr << WSAGetLastError() << std::endl;
listening = false;
WSACleanup();
exit(EXIT_FAILURE);
}
listening = true;
}
void close_server_socket(bool &listening) {
closesocket(server_sock);
listening = false;
}
void handle_client(SOCKET client_sock, sockaddr_in client) {
char buf[4096];
char host[NI_MAXHOST];
char service[NI_MAXHOST];
memset(host, 0, NI_MAXHOST);
memset(service, 0, NI_MAXHOST);
//std::cout << std::this_thread::get_id() << std::endl;
if (getnameinfo((sockaddr*)&client, sizeof(client), host, NI_MAXHOST, service, NI_MAXSERV, 0) == 0) {
std::cout << host << " connected on port " << service << std::endl;
}
else {
inet_ntop(AF_INET, &client.sin_addr, host, NI_MAXHOST);
std::cout << host << " connected on port " << ntohs(client.sin_port) << std::endl;
}
while (true) {
memset(&buf, 0, 4096);
const int bytes_received = recv(client_sock, buf, 4096, 0);
if (bytes_received == SOCKET_ERROR) {
std::cerr << WSAGetLastError() << std::endl;
WSACleanup();
}
else if (bytes_received == 0) {
std::cout << "client disconnected" << std::endl;
break;
}
else {
send(client_sock, buf, bytes_received + 1, 0);
}
}
}
int main(int argc, const char* argv[]) {
bool listening = false;
initialize_winsock();
open_server_socket(listening);
bind_server_socket();
// -----------------------------------------------------------
if (listen(server_sock, CLIENTS_QUEUE_NUM) == SOCKET_ERROR) {
std::cerr << WSAGetLastError() << std::endl;
closesocket(server_sock);
WSACleanup();
exit(EXIT_FAILURE);
}
else {
std::cout << "listening for incoming connections on port " << PORT << std::endl;
while (true) {
unsigned int removed = 0;
for (int i = 0; i < futures.size(); i++) {
auto status = futures.at(i).wait_for(std::chrono::milliseconds(0));
if (status == std::future_status::ready) {
futures.erase(futures.begin() + i);
removed++;
}
}
if (removed > 1) {
std::cout << removed << " clients removed" << std::endl;
}
else if (removed) {
std::cout << removed << " client removed" << std::endl;
}
if (futures.size() < CLIENTS_MAX_NUM && !listening) {
std::cout << "re-opening server socket" << std::endl;
open_server_socket(listening);
// BOOM <--- 10022 (WSAEINVAL) - https://learn.microsoft.com/en-us/windows/win32/winsock/windows-sockets-error-codes-2
}
if (listening) {
sockaddr_in client;
memset(&client.sin_zero, 0, 8);
int client_size = sizeof(client);
SOCKET client_sock = accept(server_sock, (sockaddr*)&client, &client_size);
if (client_sock == INVALID_SOCKET) {
std::cerr << WSAGetLastError() << std::endl;
closesocket(server_sock);
WSACleanup();
exit(EXIT_FAILURE);
}
else {
futures.emplace_back(std::async(std::launch::async, &handle_client, client_sock, client));
if (futures.size() >= CLIENTS_MAX_NUM && listening) {
std::cout << "closing server socket" << std::endl;
close_server_socket(listening);
}
std::cout << futures.size() << " clients connected" << std::endl;
}
}
}
}
// ----------------------------------------------------------
std::cout << "bye!" << std::endl;
WSACleanup();
exit(EXIT_SUCCESS);
}
After some digging I found a couple silly errors, below is the working code:
update
Also fixed the vector loop
#include <iostream>
#include <vector>
#include <string>
#include <future>
#include <chrono>
#include <WS2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
static constexpr const unsigned int PORT = 5000;
static constexpr const unsigned int CLIENTS_MAX_NUM = 5;
static constexpr const unsigned int CLIENTS_QUEUE_NUM = 0;
void handle_client(SOCKET client_sock, sockaddr_in client) {
char buf[4096];
char host[NI_MAXHOST];
char service[NI_MAXHOST];
memset(host, 0, NI_MAXHOST);
memset(service, 0, NI_MAXHOST);
//std::cout << std::this_thread::get_id() << std::endl;
if (getnameinfo((sockaddr*)&client, sizeof(client), host, NI_MAXHOST, service, NI_MAXSERV, 0) == 0) {
std::cout << host << " connected on port " << service << std::endl;
}
else {
inet_ntop(AF_INET, &client.sin_addr, host, NI_MAXHOST);
std::cout << host << " connected on port " << ntohs(client.sin_port) << std::endl;
}
while (true) {
memset(&buf, 0, 4096);
const int bytes_received = recv(client_sock, buf, 4096, 0);
if (bytes_received == SOCKET_ERROR) {
std::cerr << WSAGetLastError() << std::endl;
WSACleanup();
}
else if (bytes_received == 0) {
std::cout << "client disconnected" << std::endl;
break;
}
else {
send(client_sock, buf, bytes_received + 1, 0);
}
}
}
void initialize_winsock() {
WSAData wsData;
WORD ver = MAKEWORD(2, 2);
int wsResult = WSAStartup(ver, &wsData);
if (wsResult != 0) {
std::cerr << WSAGetLastError() << std::endl;
WSACleanup();
exit(EXIT_FAILURE);
}
}
void bind_server_socket(SOCKET &server_sock) {
int keep_alive = 1;
int re_use = 1;
if (setsockopt(server_sock, SOL_SOCKET, SO_KEEPALIVE, (const char*)&keep_alive, sizeof(keep_alive)) == SOCKET_ERROR) {
closesocket(server_sock);
WSACleanup();
exit(EXIT_FAILURE);
}
if (setsockopt(server_sock, SOL_SOCKET, SO_REUSEADDR, (const char*)&re_use, sizeof(re_use)) == SOCKET_ERROR) {
closesocket(server_sock);
WSACleanup();
exit(EXIT_FAILURE);
}
sockaddr_in server;
server.sin_family = AF_INET;
server.sin_port = htons(PORT);
server.sin_addr.S_un.S_addr = INADDR_ANY;
memset(&server.sin_zero, 0, 8);
if (bind(server_sock, (sockaddr*)&server, sizeof(sockaddr)) == SOCKET_ERROR) {
std::cerr << WSAGetLastError() << std::endl;
closesocket(server_sock);
WSACleanup();
exit(EXIT_FAILURE);
}
}
void open_server_socket(SOCKET &server_sock, bool &accepting) {
server_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (server_sock == INVALID_SOCKET) {
std::cerr << WSAGetLastError() << std::endl;
accepting = false;
WSACleanup();
exit(EXIT_FAILURE);
}
accepting = true;
}
void close_server_socket(SOCKET &server_sock, bool &accepting) {
closesocket(server_sock);
accepting = false;
}
void clear_futures(std::vector<std::future<void>> &futures) {
std::vector<std::future<void>>::iterator it = futures.begin();
while (it != futures.end()) {
auto status = (*it).wait_for(std::chrono::milliseconds(0));
if (status == std::future_status::ready) {
it = futures.erase(it);
}
else {
++it;
}
}
}
void wait_for_connections(std::vector<std::future<void>> &futures, SOCKET &server_sock, bool& accepting) {
if (listen(server_sock, CLIENTS_QUEUE_NUM) == SOCKET_ERROR) {
std::cerr << WSAGetLastError() << std::endl;
closesocket(server_sock);
WSACleanup();
exit(EXIT_FAILURE);
}
else {
std::cout << "accepting for incoming connections on port " << PORT << std::endl;
while (true) {
if (futures.size() < CLIENTS_MAX_NUM && !accepting) {
std::cout << "re-opening server socket" << std::endl;
open_server_socket(server_sock, accepting);
bind_server_socket(server_sock);
wait_for_connections(futures, server_sock, accepting);
break;
}
if (accepting) {
sockaddr_in client;
memset(&client.sin_zero, 0, 8);
int client_size = sizeof(client);
SOCKET client_sock = accept(server_sock, (sockaddr*)&client, &client_size);
if (client_sock == INVALID_SOCKET) {
std::cerr << WSAGetLastError() << std::endl;
closesocket(server_sock);
WSACleanup();
exit(EXIT_FAILURE);
}
clear_futures(futures);
futures.emplace_back(std::async(std::launch::async, &handle_client, client_sock, client));
if (futures.size() >= CLIENTS_MAX_NUM && accepting) {
std::cout << "closing server socket" << std::endl;
close_server_socket(server_sock, accepting);
}
std::cout << futures.size() << " clients connected" << std::endl;
}
}
}
}
int main(int argc, const char* argv[]) {
std::vector<std::future<void>> futures;
bool accepting = false;
SOCKET server_sock;
initialize_winsock();
open_server_socket(server_sock, accepting);
bind_server_socket(server_sock);
wait_for_connections(futures, server_sock, accepting);
std::cout << "bye!" << std::endl;
WSACleanup();
exit(EXIT_SUCCESS);
}

c++ winsock irc client questions

I'm attempting to make a IRC chat bot using Winsock in C++. I am a newb at programming, and am new to programming with sockets.
I am attempting to connect to my Twitch channel. I can make the connection successfully, and pass several buffers (namely, my bot's password or oauth token, user name, and what channel I am trying to join).
However, when I call recv(), there's no data being sent from the Twitch server.
#define WIN32_LEAN_AND_MEAN
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define DEFAULT_BUFLEN 512
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <WinSock2.h>
#include <Ws2tcpip.h>
#include <cstdlib>
#include <iostream>
#pragma comment(lib, "Ws2_32.lib")
#pragma comment (lib, "Mswsock.lib")
#pragma comment (lib, "AdvApi32.lib")
using namespace std;
int main()
{
string Buffer;
char buffers[1024 * 8] = { "0" };
string oauth = "oauthtoken";
string nick = "text_adventure_bot";
char recvbuf[DEFAULT_BUFLEN];
int recvbuflen = DEFAULT_BUFLEN;
int iResult;
string hostname = "irc.chat.twitch.tv";
struct addrinfo *result = NULL,
*ptr = NULL,
hints;
WSABUF DataBuf;
WSADATA wsadata;
WORD wVersionRequested;
WORD DllVersion = MAKEWORD(2, 1);
iResult = WSAStartup(MAKEWORD(2, 2), &wsadata);
if(iResult != 0)
{
MessageBoxA(NULL, "Winsock startup failed", "Error", MB_OK | MB_ICONERROR);
exit(1);
}
SOCKADDR_IN addr; //the ip
int sizeofaddr = sizeof(addr);
addr.sin_addr.s_addr = inet_addr("52.25.27.117");
addr.sin_port = htons(6667);
addr.sin_family = AF_INET;
SOCKET sock = socket(AF_INET, SOCK_STREAM, NULL);
if (connect(sock, (SOCKADDR*)&addr, sizeofaddr) != 0)
{
cout << "Connection error" << endl;
}
cout << "connected" << endl;
Buffer = "PASS " + oauth;
send(sock, Buffer.c_str(), (int)strlen(Buffer.c_str()), 0);
recv(sock, buffers, 1024 * 8, 0);
cout << Buffer.c_str() << endl << buffers << endl << endl;
Buffer + "NICK " + nick;
send(sock, Buffer.c_str(), strlen(Buffer.c_str()), 0);
recv(sock, buffers, 1024 * 8, 0);
cout << Buffer.c_str() << endl << buffers << endl << endl;
while (true) {
recv(sock, buffers, 1024 * 8, 0);
cout << buffers << endl << endl;
if (buffers[0] == 'PING') {
Buffer = "PONG :" + hostname + "\r\n";
send(sock, Buffer.c_str(), strlen(Buffer.c_str()), 0);
cout << Buffer.c_str() << endl << buffers << endl << endl;
}
}
return 0;
}
When I run this, all I see are my variable being passed, and then an infinite amount of zeros.
There are a number of problems with your code.
You are not checking the return value of socket() for error (I assume you are calling WSAStartup() beforehand, right?).
You are not sending any line breaks at the end of your PASS and NICK commands. IRC is a line-based protocol. That is why you are not getting any data from the server - it is waiting for you to complete your commands first.
various reserved characters in IRC must be escaped.
You are sending the PASS command twice, as you are using the + operator instead of the = operator when setting up your NICK command.
you are not sending any USER and JOIN commands.
You should not be using strlen() to calculate the length of a std::string. It has its own length() and size() methods for that purpose.
You are ignoring the return values of send() and recv(). TCP is a byte stream, but you are not taking into account that send() and recv() can return fewer bytes than requested. You need to call them in a loop until you have sent/receive all of the bytes you are expecting.
Try something more like this instead:
#include <windows.h>
#include <winsock.h>
#include <iostream>
#include <string>
#include <algorithm>
void replaceStr(std::string &str, const std::string &oldStr, const std::string &newStr)
{
std::string::size_type index = 0;
do
{
index = str.find(oldStr, index);
if (index == std::string::npos)
return;
str.replace(index, oldStr.length(), newStr);
index += newStr.length();
}
while (true);
}
std::string quote(const std::string &s)
{
std::string result = s;
replaceStr(result, "\x10", "\x10""\x10");
replaceStr(result, "\0", "\x10""0");
replaceStr(result, "\n", "\x10""n");
replaceStr(result, "\r", "\x10""r");
return result;
}
std::string unquote(const std::string &s)
{
std::string result = s;
std::string::size_type len = result.length();
std::string::size_type index = 0;
while (index < len)
{
index = result.find("\x10", index);
if (index = std::string::npos)
break;
result.erase(index, 1);
--len;
if (index >= len)
break;
switch (result[index])
{
case '0':
result[index] = '\0';
break;
case 'n':
result[index] := '\n';
break;
case 'r':
result[index] = '\r';
break;
}
++index;
}
return result;
}
std::string fetch(std::string &s, const std::string &delim)
{
std::string result;
std::string::size_type pos = s.find(delim);
if (pos == std::string::npos)
{
result = s;
s = "";
}
else
{
result = s.substr(0, pos);
s.erase(0, pos+delim.length());
}
return result;
}
bool sendStr(SOCKET sock, const std::string &s)
{
const char *ptr = s.c_str();
int len = s.length();
while (len > 0)
{
int ret = send(sock, ptr, len, 0);
if (ret == SOCKET_ERROR)
{
std::cout << "send() error: " << WSAGetLastError() << std::endl;
return false;
}
ptr += ret;
len -= ret;
}
return true;
}
bool sendCmd(SOCKET sock, const std::string &cmd)
{
std::cout << "Sending: " << cmd << std::endl;
return sendStr(sock, quote(cmd)) && sendStr(sock, "\r\n");
}
int main()
{
int exitCode = -1;
WSADATA wsa;
int ret = WSAStartup(MAKEWORD(2, 0), &wsa);
if (ret != 0)
{
std::cout << "Winsock init error: " << ret << std::endl;
goto done;
}
SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock == INVALID_SOCKET)
{
std::cout << "socket() error: " << WSAGetLastError() << std::endl;
goto done;
}
SOCKADDR_IN addr = {0};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("52.25.27.117"); //the ip
addr.sin_port = htons(6667);
if (connect(sock, (SOCKADDR*)&addr, sizeof(addr)) != 0)
{
std::cout << "connect() error: " << WSAGetLastError() << std::endl;
goto cleanup:
}
std::cout << "connected" << std::endl;
std::string oauth = ...;
std::string nick = ...;
std::string user = ...;
std::string channel = ...;
sendCmd("PASS " + oauth);
sendCmd("NICK " + nick);
sendCmd("USER " + user);
sendCmd("JOIN " + channel);
char buf[1024];
std::string LineBuffer;
std::string::size_type StartIdx = 0;
do
{
int ret = recv(sock, buf, sizeof(buf), 0);
if (ret == SOCKET_ERROR)
{
std::cout << "recv() error: " << WSAGetLastError() << std::endl;
goto cleanup;
}
if (ret == 0)
{
std::cout << "Server disconnected" << std::endl;
break;
}
LineBuffer.append(buf, ret);
do
{
std::string::size_type pos = LineBuffer.find('\n', StartIdx);
if (pos == std::string::npos)
break;
std::string::size_type len = pos;
if ((pos > 0) && (LineBuffer[pos-1] == '\r'))
--len;
std::string msg = unquote(LineBuffer.substr(0, len));
LineBuffer.erase(0, pos+1);
StartIdx = 0;
std::string senderNick;
std::string senderHost;
if (!msg.empty() && (msg[0] == ':'))
{
std::string tmp = fetch(msg, " ");
tmp.erase(0, 1); // remove ':'
senderNick = fetch(tmp, "!");
senderHost = tmp;
}
std::cout << "Received: " << msg << std::endl;
if (msg == "PING")
sendCmd("PONG :" + hostname);
}
while(true);
}
while (true);
exitCode = 0;
cleanup:
closesocket(sock);
done:
return exitCode;
}
First, you ignore the return value of recv, so you have no idea how many bytes you received.
Second, nowhere did you actually implement the IRC message protocol (see section 2.3 of RFC1459). So you have no reason to assume that the first byte of your buffer will contain the first byte of the IRC protocol message. Only an actual implementation of the IRC message protocol can produce a buffer whose first byte is the first byte of the IRC message.
Similarly, you can't do this:
cout << buffers << endl << endl;
The operator<<(const char *) for a stream needs a pointer to a C-style string. Until you parse the data you received from the TCP connection and produce a C-style string from it, you must not treat it as a C-style string.
Also:
if (buffers[0] == 'PING') {
You really meant PING as a multi-byte character constant? There is no multibyte character named PING so far as I know. And, in any event, an IRC server sends the literal four character string "PING", not a single 'PING' character.

Asynchronous socket client code and some doubts about getsockopt and error treatment

Here is the code I ended up doing for an asynchronous client:
struct addrinfo *currentAddress = NULL;
void GetAddresses(std::string hostname, int port)
{
struct addrinfo hints;
std::cout << "Get adresses for hostname " << hostname << " port " << port << std::endl;
std::memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC; /* Allow IPV4 or IPV6 */
hints.ai_socktype = SOCK_STREAM; /* Datagram socket */
hints.ai_flags = 0;
hints.ai_protocol = 0; /* Any protocol */
std::string portStr;
portStr = std::to_string(port);
int status = getaddrinfo(hostname.c_str(), portStr.c_str(), &hints, &currentAddress);
if (status != 0)
{
std::stringstream ss;
ss << "Cannot resolve hostname " << hostname << ". Error: " << gai_strerror(status);
throw std::runtime_error(ss.str());
}
}
void StepNextAddress()
{
currentAddress = currentAddress->ai_next;
}
void Connect(const std::string& address, int port)
{
/*
* Get all addresses for connection
*/
GetAddresses(address, port);
bool success = false;
while (currentAddress != NULL)
{
int s = socket(currentAddress->ai_family, currentAddress->ai_socktype, currentAddress->ai_protocol);
if (s == -1)
{
freeaddrinfo(currentAddress);
std::stringstream ss;
ss << "Error creating socket. Out of resources.";
throw std::runtime_error(ss.str());
}
std::cout << "Socket created: " << s << std::endl;
#ifdef _WIN32
unsigned long on = 1;
ioctlsocket(s, FIONBIO, &on); // Make it non blocking
#else
fcntl(s, F_SETFL, O_NONBLOCK); Make if non blocking
#endif
/*
* Connect to socket
*/
int status = connect(s, currentAddress->ai_addr, currentAddress->ai_addrlen);
if (status < 0 && errno != EINPROGRESS)
{
freeaddrinfo(currentAddress);
std::stringstream ss;
ss << "Error connecting socket to " << address << " port " << port << ".";
throw std::runtime_error(ss.str());
}
/*
* Wait socket to get ready to select
*/
fd_set readSet, writeSet, exceptSet;
FD_ZERO(&readSet);
FD_ZERO(&writeSet);
FD_ZERO(&exceptSet);
timeval tout;
tout.tv_sec = CONNECT_TIMEOUT_SECONDS;
int retval = select (s + 1, &readSet, &writeSet, &exceptSet, &tout);
if (retval == -1)
{
freeaddrinfo(currentAddress);
std::stringstream ss;
ss << "Error selecting socket.";
throw std::runtime_error(ss.str());
}
/*
* Timeout. Try next resources
*/
if (retval == 0)
{
std::cout << "Connection timedout. Trying next resource." << std::endl;
#ifdef _WIN32
_close(s);
#else
close(s);
#endif
StepNextAddress();
continue;
}
/*
* Wait socket to be ready to work
*/
int result = 0;
socklen_t result_len = sizeof(result);
int sts = getsockopt(s, SOL_SOCKET, SO_ERROR, (char *) &result, &result_len);
if (sts < 0)
{
freeaddrinfo(currentAddress);
std::stringstream ss;
ss << "Error getting socket option.";
throw std::runtime_error(ss.str());
}
if (result != 0)
{
freeaddrinfo(currentAddress);
std::stringstream ss;
ss << "Error getting socket resources.";
throw std::runtime_error(ss.str());
}
}
freeaddrinfo(currentAddress);
std::cout << "CONNECTED!!!!!!!!!!!!" << std::endl;
}
int main()
{
Connect("test.test.com", 21);
}
I don´t understand yet the selectusage together with the getsockopt. Are they necessary or in my case select would be enough ?
In that case, the select is thowing an error... What is wrong with the select code ?
Also, what about the error treatments here... The only case I´m trying to connect the next address in list is on timeout... Does it make sense ?
To finish, comments in the code are well appreciated.

garbage text with c++ strings on send recv calls

// In server.cpp after connection has established
std::string input;
input.reserve(5);
std::cout << "Enter message to send: ";
std::cin.ignore(); // =====(1)=====
std::getline(std::cin, input);
std::cout << "Sending..." << std::endl;
auto len = input.length();
auto bytes_sent = send(newFD, input.data(), len, 0); // =====(2)=====
std::cout << "Input length : " << input.length() << std::endl
<< "Input bytes sent : " << bytes_sent << std::endl;
My aim is to use std::string instead of plain old char[fixed] in simple tcp client server program. So in server.cpp I have 2 doubts. So far my initial guesses are working as expected. I've marked them above in code.
cin.ignore() vs cin.clear() + cin.sync()
std::string.data() vs std::string.c_str()
Which should I use? I'm not even sure of the difference b/w any of these and I don't know if they're contributing to my problem.
// In client.cpp
std::string message;
message.reserve(5);
auto len = message.capacity();
auto bytes_recv = recv(sockFD, &message.front(), len - 1, 0); // =====(1)=====
message[len] = 0; // =====(2)=====
close(sockFD);
freeaddrinfo(res);
std::cout << "Bytes recieved :" << bytes_recv << std::endl;
std::cout << message.c_str() << std::endl; // =====(3)=====
And in client.cpp, everything goes wrong when I try to send bigger strings. But I probably know the cause, however solution is somewhat tricky to implement.
Am I doing right thing to pass &std::string.front() to write incoming data?
This is wrong, string class should manage this, right? However since I'm directly writing to &front(), I guess length won't get updated or I'm not sure what happens but data surely gets lost when outputting with std::cout << message;.
I'm doing this just because I'm directly writing to &front, still it produces garbage if somehow data returned is smaller than total length, probably because it cannot find terminating character at right place?
server.cpp
// compile as 'g++ server.cpp -o server.app -std=c++14'
// run as : './server.app 8080'
#include <iostream>
#include <string>
#include <cstring>
extern "C" {
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
}
int main(int argc, char *argv[])
{
if(argc != 2) {
std::cerr << "Run program as 'program port'" << std::endl;
return -1;
}
auto &portNum = argv[1];
const unsigned int backLog = 5;
struct addrinfo hints, *res, *p;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
int gAddRes = getaddrinfo(NULL, portNum, &hints, &res);
if(gAddRes != 0) {
std::cerr << gai_strerror(gAddRes) << std::endl;
return -2;
}
std::cout << "Detecting addresses" << std::endl;
unsigned int numOfAddr = 0;
char ipStr[INET6_ADDRSTRLEN];
for(p = res; p != NULL; p = p->ai_next) {
void *addr;
std::string ipVer = "IPv0";
if(p->ai_family == AF_INET) {
ipVer = "IPv4";
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
addr = &(ipv4->sin_addr);
++numOfAddr;
}
else {
ipVer = "IPv6";
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
addr = &(ipv6->sin6_addr);
++numOfAddr;
}
inet_ntop(p->ai_family, addr, ipStr, sizeof(ipStr));
std::cout << "(" << numOfAddr << ") " << ipVer << " : " << ipStr
<< std::endl;
}
if(!numOfAddr) {
std::cerr << "Found no host address to use" << std::endl;
return -3;
}
std::cout << "Enter the number of host address to bind with:" << std::endl;
unsigned int choice = 0;
bool madeChoice = false;
do {
std::cin >> choice;
if(choice > (numOfAddr + 1) || choice < 1) {
madeChoice = false;
std::cout << "Wrong choice, try again!" << std::endl;
}
else
madeChoice = true;
} while(!madeChoice);
p = res;
bool isIPv4 = true;
if(choice > 1) {
unsigned int temp = 1;
while(choice < temp) {
p = p->ai_next;
++temp;
}
if(p->ai_family == AF_INET) {
isIPv4 = true;
}
else
isIPv4 = false;
}
int sockFD = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if(sockFD == -1) {
std::cerr << "Error while creating socket" << std::endl;
freeaddrinfo(res);
return -4;
}
int bindR = bind(sockFD, p->ai_addr, p->ai_addrlen);
if(bindR == -1) {
std::cerr << "Error while binding socket" << std::endl;
close(sockFD);
freeaddrinfo(res);
return -5;
}
int listenR = listen(sockFD, backLog);
if(listenR == -1) {
std::cerr << "Error while Listening on socket" << std::endl;
close(sockFD);
freeaddrinfo(res);
return -6;
}
struct sockaddr_storage client_addr;
socklen_t client_addr_size = sizeof(client_addr);
int newFD =
accept(sockFD, (struct sockaddr *)&client_addr, &client_addr_size);
if(newFD == -1) {
std::cerr << "Error while Accepting on socket" << std::endl;
close(sockFD);
freeaddrinfo(res);
return -7;
}
std::string input;
input.reserve(5);
std::cout << "Enter message to send: ";
std::cin.ignore();
std::getline(std::cin, input);
std::cout << "Sending..." << std::endl;
auto len = input.length();
auto bytes_sent = send(newFD, input.data(), len, 0);
std::cout << "Input length : " << input.length() << std::endl
<< "Input bytes sent : " << bytes_sent << std::endl;
close(newFD);
close(sockFD);
freeaddrinfo(res);
return 0;
}
client.cpp
// compile as 'g++ client.cpp -o client.app -std=c++14'
// run as : './client.app 0 8080'
#include <iostream>
#include <cstring>
extern "C" {
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <netinet/in.h>
}
int main(int argc, char *argv[])
{
if(argc != 3) {
std::cerr << "Run program as 'program ipaddress port'" << std::endl;
return -1;
}
auto &ipAddress = argv[1];
auto &portNum = argv[2];
struct addrinfo hints, *res, *p;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE;
int gAddRes = getaddrinfo(ipAddress, portNum, &hints, &res);
if(gAddRes != 0) {
std::cerr << gai_strerror(gAddRes) << std::endl;
return -2;
}
std::cout << "Detecting addresses" << std::endl;
unsigned int numOfAddr = 0;
char ipStr[INET6_ADDRSTRLEN];
for(p = res; p != NULL; p = p->ai_next) {
void *addr;
std::string ipVer = "IPv0";
if(p->ai_family == AF_INET) {
ipVer = "IPv4";
struct sockaddr_in *ipv4 = (struct sockaddr_in *)p->ai_addr;
addr = &(ipv4->sin_addr);
++numOfAddr;
}
else {
ipVer = "IPv6";
struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)p->ai_addr;
addr = &(ipv6->sin6_addr);
++numOfAddr;
}
inet_ntop(p->ai_family, addr, ipStr, sizeof(ipStr));
std::cout << "(" << numOfAddr << ") " << ipVer << " : " << ipStr
<< std::endl;
}
if(!numOfAddr) {
std::cerr << "Found no host address to use" << std::endl;
return -3;
}
std::cout << "Enter the number of host address to bind with:" << std::endl;
unsigned int choice = 0;
bool madeChoice = false;
do {
std::cin >> choice;
if(choice > (numOfAddr + 1) || choice < 1) {
madeChoice = false;
std::cout << "Wrong choice, try again!" << std::endl;
}
else
madeChoice = true;
} while(!madeChoice);
p = res;
bool isIPv4 = true;
if(choice > 1) {
unsigned int temp = 1;
while(choice < temp) {
p = p->ai_next;
++temp;
}
if(p->ai_family == AF_INET) {
isIPv4 = true;
}
else
isIPv4 = false;
}
int sockFD = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
if(sockFD == -1) {
std::cerr << "Error while creating socket" << std::endl;
return -4;
}
int connectR = connect(sockFD, p->ai_addr, p->ai_addrlen);
if(connectR == -1) {
close(sockFD);
std::cerr << "Error while connecting socket" << std::endl;
return -5;
}
std::string message;
message.reserve(5);
auto len = message.capacity();
auto bytes_recv = recv(sockFD, &message.front(), len - 1, 0);
message[len] = 0;
close(sockFD);
freeaddrinfo(res);
std::cout << "Bytes recieved :" << bytes_recv << std::endl;
std::cout << message.c_str() << std::endl;
return 0;
}
Your question on this is too vague to provide a helpful answer.
data() and c_str() are effectively the same thing since C++11. It doesn't matter which one you use. EDIT: In C++17, data() will have a non-const overload that returns a non-const char*, so you will not need to do &message.front() to access a modifiable form of the underlying buffer. c_str() will remain const.
&message.front() is right... and wrong. That is the way to get a non-const char* to the contents of your std::string. BUT message is uninitialized and has a size() of 0 at that point in the code, so I'm not even sure that line of code is well-defined behavior. Rather than doing a reserve(5) I would construct your string like this: auto message = std::string(5, ' '); Then when you pass it into recv there will actually be valid stuff there for it to overwrite and you'll be able to read it from message afterwards.
Yes, this is wrong. You should be setting your string up to be the actual size you need. I suspect you can just pass in len instead of len - 1 if you do this. On this topic, are you certain everything you'll ever receive is only 4 bytes long? Or are you intentionally only reading 4 bytes at a time?
a) you don't need to pass c_str() to std::cout. << is overloaded to accept std::string as well. b) recv returns the number of bytes that you received. If that value is less than the size you initialized your message to, then the remaining characters in your string will be garbage (or ' ' chars if you followed my advice re:#3). I would do message.resize(bytes_recv); after receiving the message.
Your questions have been addressed by caps,, but my 2 cents. What about having your own send/recv functions and hiding the complexity?
For example along the lines of:
ssize_t recv(int sockfd, std::string &buf, size_t len, int flags) {
buf.resize(len); // current status unknown -> make it fit
ssize_t n = ::recv(sockfd, (void *)buf.data(), len, flags);
buf.resize(n >= 0 ? n : 0); // take error into account
return n;
}

(WinSocket) Spam on Disconnect

I'm trying to write a server for a client/server chat program.
The only problem is that if once client disconnects, the server starts spamming their last message to the server. Even if they reconnect, it keeps spamming..I've tried googling, but I can't find anything.
Here's the Server Code:
#include "MasterServer.h"
using namespace std;
SOCKADDR_IN Server;
int addrlen = sizeof(Server);
SOCKET sListen;
SOCKET Connect;
SOCKET* Connections;
int port = 444;
int ConCounter = 0;
char* Recv = new char[256];
int ServerThread(int ID)
{
ZeroMemory(Recv, sizeof(Recv));
for(;;)
{
if(recv(Connections[ID],Recv ,256 ,NULL))
{
cout << Recv << endl;
}
}
}
int InitWinSock()
{
int RetVal = 0;
WSAData wsa;
WORD Version = MAKEWORD(2,1);
RetVal = WSAStartup(Version, &wsa);
return RetVal;
}
int main()
{
int RetVal = 0;
RetVal = InitWinSock();
if(RetVal != 0)
{
cout << "WinSock Start Up Failure";
}
Connections = (SOCKET*)calloc(64, sizeof(SOCKET));
sListen = socket(AF_INET, SOCK_STREAM, NULL);
Connect = socket(AF_INET, SOCK_STREAM, NULL);
cout << "\t\t----====ModernBacon Server====----"<< endl;
cout << "What Port Would You Like To Run On [Default 444]: ";
cin >> port;
system("cls");
Server.sin_addr.s_addr = inet_addr("127.0.0.1");
Server.sin_family = AF_INET;
Server.sin_port = htons(port);
bind(sListen, (SOCKADDR*)&Server, sizeof(Server));
//Listening
listen(sListen, 64);
cout << "Listening For Connections IP:"<<inet_ntoa(Server.sin_addr);"Port:"<<port<<
endl;
for(;;)
{
if(Connect = accept(sListen, (SOCKADDR*)&Server, &addrlen))
{
Connections[ConCounter] = Connect;
char* ID = new char[64];
ZeroMemory(ID, sizeof(ID));
itoa(ConCounter, ID, 10);
send(Connections[ConCounter], ID, sizeof(ID),NULL);
ConCounter = ConCounter + 1;
CreateThread(NULL,NULL, (LPTHREAD_START_ROUTINE)
ServerThread, (LPVOID)(ConCounter -1), NULL, NULL);
cout << "\t\tClient Connected!\tID: " <<
ConCounter << endl;
}
}
return(0);
}
I have no clue whats going on...
There are quite a few problems with your code. Try something more like this instead:
#include "MasterServer.h"
#include <string>
using namespace std;
static const int MAX_CLIENTS = 64;
static const int MAX_RECV = 256;
struct ClientInfo
{
int ID;
SOCKET Connection;
char Recv[MAX_RECV];
};
ClientInfo Clients[MAX_CLIENTS];
DWORD WINAPI ServerThread(LPVOID lpParam)
{
ClientInfo *Client = (ClientInfo*) lpParam;
char szID[64] = {0};
itoa(Client->ID, szID, 10);
int RetVal = send(Client->Connection, szID, sizeof(szID), NULL);
if (RetVal != sizeof(szID))
{
cout << "Client " << Client->ID << " Send Failure. Error " << WSAGetLastError() << endl;
}
else
{
do
{
RetVal = recv(Client->Connection, Client->Recv, MAX_RECV, NULL);
if (RetVal == SOCKET_ERROR)
{
cout << "Client " << Client->ID << " Recv Failure. Error " << WSAGetLastError() << endl;
break;
}
if (RetVal == 0)
{
cout << "Client " << Client->ID << " disconnected" << endl;
break;
}
cout << "Client " << Client->ID << ": " << string(Client->Recv, RetVal) << endl;
}
while (true);
}
closesocket(Client->Connection);
Client->Connection = INVALID_SOCKET;
return 0;
}
int InitWinSock()
{
WSAData wsa;
return WSAStartup(MAKEWORD(2,1), &wsa);
}
int main()
{
SOCKADDR_IN addr;
int addrlen;
SOCKET sListen;
SOCKET sClient;
int port = 444;
int index;
cout << "\t\t----====ModernBacon Server====----"<< endl;
for (int i = 0; i < MAX_CLIENTS; ++i)
Connections[i] = INVALID_SOCKET;
int RetVal = InitWinSock();
if (RetVal != 0)
{
cout << "WinSock Start Up Failure. Error " << RetVal << endl;
return 0;
}
sListen = socket(AF_INET, SOCK_STREAM, NULL);
if (sListen == INVALID_SOCKET)
{
cout << "WinSock Socket Failure. Error " << WSAGetLastError() << endl;
return 0;
}
cout << "What Port Would You Like To Run On [Default 444]: ";
cin >> port;
system("cls");
ZeroMemory(&addr, sizeof(addr));
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
RetVal = bind(sListen, (SOCKADDR*)&addr, sizeof(addr));
if (RetVal != 0)
{
cout << "WinSock Bind Failure. Error " << WSAGetLastError() << endl;
return 0;
}
//Listening
RetVal = listen(sListen, MAX_CLIENTS);
if (RetVal != 0)
{
cout << "WinSock Listen Failure. Error " << WSAGetLastError() << endl;
return 0;
}
cout << "Listening For Connections. IP: " << inet_ntoa(addr.sin_addr) << ", Port: " << port << endl;
do
{
addrlen = sizeof(addr);
sClient = accept(sListen, (SOCKADDR*)&addr, &addrlen);
if (sClient == INVALID_SOCKET)
{
cout << "WinSock Accept Failure. Error " << WSAGetLastError() << endl;
return 0;
}
index = -1;
for (int i = 0; i < MAX_CLIENTS; ++i)
{
if (Clients[i].Connection == INVALID_SOCKET)
{
index = i;
break;
}
}
if (index == -1)
{
closesocket(sClient);
cout << "\t\tClient Connected. Max Clients Exceeded!" << endl;
continue;
}
Clients[index].ID = index + 1;
Clients[index].Connection = sClient;
CreateThread(NULL, NULL, &ServerThread, &Clients[index], NULL, NULL);
cout << "\t\tClient Connected!\tID: " << Clients[index].ID << endl;
}
while (true);
return 0;
}