I am trying to follow this tutorial on how to send UDP packages in C++. Unfortunately I cannot get it to work.
I tracked loopback traffic with Wireshark on port 30001, but it did not display any packets, so it seems like the sending is already failing.
I added my executable to the firewall to ensure it's not getting blocked, and ran the program in two separate terminal windows using sudo ./main receiver
or sender
(Also without sudo
). I am on Mac OS, which is why I have not implemented the Windows-specific code yet.
I would really appreciate some help, since I am a bit stuck, and I am quite a beginner in networking on this level.
Here is my code:
Main.cpp
#include <iostream>
#include "Socket.h"
#include "Address.h"
int main(int argc, char* argv[])
{
if (argc != 2) {
std::cout << "Usage: " << argv[0] << " [sender|receiver]" << std::endl;
return 1;
}
const std::string mode = argv[1];
if (mode == "sender")
{
int port = 30001;
Address sendAddress = Address(127, 0, 0, 1, 30000);
Socket socket;
if(!socket.Open(port))
{
std::cout << "Failed to open socket!" << std::endl;
return 1;
}
const char data[] = "Hello World!";
if(!socket.Send(sendAddress, data, sizeof(data))
{
std::cout << "Failed to send message!" << std::endl;
}
}
else if (mode == "receiver")
{
Socket socket;
int port = 30000;
if(!socket.Open(port))
{
std::cout << "Failed to open socket!" << std::endl;
return 1;
}
while(true)
{
Address sender;
unsigned char buffer[512];
int bytes_read = socket.Receive(sender, buffer, sizeof(buffer));
if(!bytes_read){
break;
}
if(bytes_read > 0)
{
std::cout << "Received " << bytes_read << " bytes" << std::endl;
std::cout << buffer << std::endl;
}
else
{
std::cout << "." << std::endl;
}
}
}
else
{
std::cout << "Invalid mode! Use 'sender' or 'receiver'." << std::endl;
return 1;
}
std::cout << "Program closing! Good bye!" << std::endl;
return 0;
}
Socket.cpp
#define PLATFORM_WINDOWS 1
#define PLATFORM_MAC 2
#define PLATFORM_UNIX 3
#if defined(_WIN32)
#define PLATFORM PLATFORM_WINDOWS
#elif defined(__APPLE__)
#define PLATFORM PLATFORM_MAC
#else
#define PLATFORM PLATFORM_UNIX
#endif
#if PLATFORM == PLATFORM_WINDOWS
#include <winsock2.h>
#elif PLATFORM == PLATFORM_MAC || PLATFORM == PLATFORM_UNIX
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <unistd.h>
#endif
#include "Socket.h"
#include "Address.h"
#include <iostream>
Socket::Socket()
{
}
Socket::~Socket()
{
Close();
}
bool Socket::Open(unsigned short port)
{
handle = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (handle <= 0){
std::cout <<("Failed to create socket")<<std::endl;
return false;
}
sockaddr_in address;
address.sin_family = AF_INET;
address.sin_addr.s_addr = htonl(INADDR_ANY);
address.sin_port = htons((unsigned short) port);
if(bind(handle, (const sockaddr*) &address, sizeof(sockaddr_in)) < 0)
{
std::cout <<("Failed to bind socket")<<std::endl;
return false;
}
#if PLATFORM == PLATFORM_MAC || PLATFORM == PLATFORM_UNIX
int nonBlocking = 1;
if ( fcntl( handle, F_SETFL, O_NONBLOCK, nonBlocking ) == -1 )
{
printf( "failed to set non-blocking\n" );
return false;
}
#elif PLATFORM == PLATFORM_WINDOWS
DWORD nonBlocking = 1;
if ( ioctlsocket( handle, FIONBIO, &nonBlocking ) != 0 )
{
printf( "failed to set non-blocking\n" );
return false;
}
#endif
return true;
}
void Socket::Close()
{
#if PLATFORM == PLATFORM_MAC || PLATFORM == PLATFORM_UNIX
close( handle );
#elif PLATFORM == PLATFORM_WINDOWS
closesocket( socket );
#endif
}
bool Socket::Send(const Address& destination, const void* packet_data, int packet_size)
{
int sent_bytes = sendto(
handle,
(const char*)packet_data,
packet_size,
0,
(sockaddr*)&destination,
sizeof(sockaddr_in));
if(sent_bytes != packet_size )
{
printf( "failed to send packet\n" );
return false;
}
std::cout << "Sent " << sent_bytes << " bytes" << std::endl;
return true;
}
int Socket::Receive(Address& sender, void * data, int size)
{
#if PLATFORM == PLATFORM_WINDOWS
typedef int socklen_t;
#endif
unsigned char packet_data[size];
unsigned int max_packet_size = sizeof( packet_data );
sockaddr_in from;
socklen_t fromLength = sizeof( from );
int bytes = recvfrom(handle,
(char*)packet_data,
max_packet_size,
0,
(sockaddr*)&from,
&fromLength );
if ( bytes <= 0 )
return -1;
unsigned int from_address = ntohl( from.sin_addr.s_addr );
unsigned int from_port = ntohs( from.sin_port );
sender = Address( from_address, from_port );
return bytes;
}
Address.cpp
#define PLATFORM_WINDOWS 1
#define PLATFORM_MAC 2
#define PLATFORM_UNIX 3
#if defined(_WIN32)
#define PLATFORM PLATFORM_WINDOWS
#elif defined(__APPLE__)
#define PLATFORM PLATFORM_MAC
#else
#define PLATFORM PLATFORM_UNIX
#endif
#if PLATFORM == PLATFORM_WINDOWS
#include <winsock2.h>
#elif PLATFORM == PLATFORM_MAC || PLATFORM == PLATFORM_UNIX
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#endif
#include "Address.h"
Address::Address()
{
}
Address::Address(unsigned char a, unsigned char b, unsigned char c, unsigned char d, unsigned short port)
{
unsigned int address = ( a << 24 ) |
( b << 16 ) |
( c << 8 ) |
d;
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl( address );
addr.sin_port = htons( port );
Address::port = port;
Address::address = address;
}
Address::Address(unsigned int address, unsigned short port)
{
Address::port = port;
Address::address = address;
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl( address );
addr.sin_port = htons( port );
}
unsigned int Address::GetAddress() const
{
return address;
}
unsigned char Address::GetA() const
{
return ( GetAddress() >> 24 ) & 0xFF;
}
unsigned char Address::GetB() const
{
return ( GetAddress() >> 16 ) & 0xFF;
}
unsigned char Address::GetC() const
{
return ( GetAddress() >> 8 ) & 0xFF;
}
unsigned char Address::GetD() const
{
return GetAddress() & 0xFF;
}
unsigned short Address::GetPort() const
{
return port;
}
As requested here are my header files as well: Socket.h
#pragma once
class Socket
{
public:
Socket();
~Socket();
bool Open(unsigned short port);
void Close();
bool IsOpen() const;
bool Send(const class Address& destination , const void * packet_data, int packet_size);
int Receive(class Address& sender, void * data, int size);
private:
int handle;
};
Address.h
#pragma once
class Address
{
public:
Address();
Address(unsigned char a,
unsigned char b,
unsigned char c,
unsigned char d,
unsigned short port );
Address(unsigned int address, unsigned short port );
unsigned int GetAddress() const;
unsigned char GetA() const;
unsigned char GetB() const;
unsigned char GetC() const;
unsigned char GetD() const;
unsigned short GetPort() const;
private:
unsigned int address;
unsigned short port;
};
Here's the root cause of your issues. This block of code:
bool Socket::Send(const Address& destination, const void* packet_data, int packet_size)
{
int sent_bytes = sendto(
handle,
(const char*)packet_data,
packet_size,
0,
(sockaddr*)&destination,
sizeof(sockaddr_in));
Is casting the custom Address
struct, which is not a sockaddr into a sockaddr*
.
Change Address to have a private member:
sockaddr_in addr;
And a public method:
sockaddr_in GetSocketAddress() const {return addr;}
Then correctly initialize it in your constructor. It looks like you are intializing a local addr
variable. I think you meant for this to be a member variable.
Address::Address(unsigned char a, unsigned char b, unsigned char c, unsigned char d, unsigned short port)
{
unsigned int address = (a << 24) |
(b << 16) |
(c << 8) |
d;
addr = {};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(address);
addr.sin_port = htons(port);
Address::port = port;
Address::address = address;
}
Then when you send:
bool Socket::Send(const Address& destination, const void* packet_data, int packet_size)
{
sockaddr_in addr = destination.GetSocketAddress();
int sent_bytes = sendto(
handle,
(const char*)packet_data,
packet_size,
0,
(sockaddr*)&addr,
sizeof(sockaddr_in));
And your program will successfully send and receive at this point.
Other minor issues:
I had to disable non-blocking mode to get your code to work. Otherwise, your receive mode would just print out an endless stream of periods. Disable non-blocking mode if you aren't familiar with sockets.
Your Socket::Close call has a compile error on windows:
closesocket( socket );
Should be handle
getting passed to closesocket.
Socket::Receive
isn't copying the data over from the local buffer to the buffer passed in.Simplify it to:
int Socket::Receive(Address& sender, void* data, int size)
{
#if PLATFORM == PLATFORM_WINDOWS
typedef int socklen_t;
#endif
sockaddr_in from = {};
socklen_t fromLength = sizeof(from);
int bytes = recvfrom(handle,
(char*)data,
size,
0,
(sockaddr*)&from,
&fromLength);
...
Address sender;
const int buffer_length = 512;
unsigned char buffer[buffer_length + 1] = {}; // zero init, +1 for null char
int bytes_read = socket.Receive(sender, buffer, buffer_length);
if (!bytes_read) {
break;
}
if (bytes_read > 0)
{
buffer[bytes_read] = '\0'; // secure that buffer before printing
std::cout << "Received " << bytes_read << " bytes" << std::endl;
std::cout << buffer << std::endl;
}
To address the debugability of your code, if you captured errno
or WSAGetLastError
after each unsuccessful socket API call:
int result = sendto(...);
int error_code = (result >= 0) ? 0 : errno; // unix
int error_code = (result >= 0) ? 0 : WSAGetLastError(); // windows
Then you could have printed those out and it would have mapped to a bad address format error.