22

I would like to send a string: "Jane Doe" to intranet ip 192.168.0.4 to port 9000 over UDP. I have done this many times via UDP and TCP by Java, but now I have to do it with standard C++ libraries and I can't find any samples only topics where people just can't make it work.

I know that I have to encode "Jane Doe" as array of bytes then just open socket, pack it in datagram and send it.

C++ is not my first language and this is small part of code I can't figure out, I've chosen UDP because it is always much simpler than TCP.

Yoda
  • 17,363
  • 67
  • 204
  • 344
  • 1
    I'd probably use Boost ASIO. If I couldn't use that (for whatever reason), I'd probably use the class I posted in [an answer on CodeReview](http://codereview.stackexchange.com/a/46354/489). As it stands, that code is Windows-specific, but nearly the only parts specific to Windows are the `socket_user` class and the `#pragma comment lib` line (I think those are the only parts, but I haven't tested to be sure). – Jerry Coffin Jul 03 '14 at 17:38
  • 2
    @Yoda please choose an answer – James Wierzba Jan 24 '18 at 17:41

4 Answers4

23

A good source for network programming is Beej's Guide to Network Programming. Below is some sample Unix code.

If this is Windows programming:

  • "sock" should be of type SOCKET instead of int.
  • Use closesocket instead of close
  • #include <winsock2.h> instead of all those unix headers

#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <memory.h>
#include <ifaddrs.h>
#include <net/if.h>
#include <errno.h>
#include <stdlib.h>
#include <iostream>


int resolvehelper(const char* hostname, int family, const char* service, sockaddr_storage* pAddr)
{
    int result;
    addrinfo* result_list = NULL;
    addrinfo hints = {};
    hints.ai_family = family;
    hints.ai_socktype = SOCK_DGRAM; // without this flag, getaddrinfo will return 3x the number of addresses (one for each socket type).
    result = getaddrinfo(hostname, service, &hints, &result_list);
    if (result == 0)
    {
        //ASSERT(result_list->ai_addrlen <= sizeof(sockaddr_in));
        memcpy(pAddr, result_list->ai_addr, result_list->ai_addrlen);
        freeaddrinfo(result_list);
    }

    return result;
}


int main()
{
    int result = 0;
    int sock = socket(AF_INET, SOCK_DGRAM, 0);

    char szIP[100];

    sockaddr_in addrListen = {}; // zero-int, sin_port is 0, which picks a random port for bind.
    addrListen.sin_family = AF_INET;
    result = bind(sock, (sockaddr*)&addrListen, sizeof(addrListen));
    if (result == -1)
    {
       int lasterror = errno;
       std::cout << "error: " << lasterror;
       exit(1);
    }


    sockaddr_storage addrDest = {};
    result = resolvehelper("192.168.0.4", AF_INET, "9000", &addrDest);
    if (result != 0)
    {
       int lasterror = errno;
       std::cout << "error: " << lasterror;
       exit(1);
    }

    const char* msg = "Jane Doe";
    size_t msg_length = strlen(msg);

    result = sendto(sock, msg, msg_length, 0, (sockaddr*)&addrDest, sizeof(addrDest));

    std::cout << result << " bytes sent" << std::endl;
    
    return 0;

}

stackprotector
  • 10,498
  • 4
  • 35
  • 64
selbie
  • 100,020
  • 15
  • 103
  • 173
13

This is very easy to do if you are willing to use the boost library.

Here is the code snippit

#include "boost/asio.hpp"
using namespace boost::asio;

...

io_service io_service;
ip::udp::socket socket(io_service);
ip::udp::endpoint remote_endpoint;

socket.open(ip::udp::v4());

remote_endpoint = ip::udp::endpoint(ip::address::from_string("192.168.0.4"), 9000);

boost::system::error_code err;
socket.send_to(buffer("Jane Doe", 8), remote_endpoint, 0, err);

socket.close();
James Wierzba
  • 16,176
  • 14
  • 79
  • 120
  • Shouldn't the buffer be of size 9? Including `\0`? – Maik Klein Dec 22 '15 at 13:59
  • if you don't want to have string literals, you could just put in as bytes: and IPv4 address takes 4 bytes with values between 0-255 (inclusive). And we all know 2^8 = 256 possible combinations for 8 bits = 1 byte – dGRAMOP Jan 06 '18 at 21:42
  • @MaikKlein char[] would have a null terminator, but this is not the same as a std:string (which i think is used here) from what I understand. so If he had made a character array, then the \0 wold be expected, so the size would be 9. https://stackoverflow.com/questions/11752705/does-stdstring-contain-null-terminator – Goku Jul 13 '18 at 19:48
  • It's 8 in the scripts above too, isn't it? – france1 Jul 30 '22 at 18:13
5

I rewrote selbie's code to make it more C++-like and I minimized it a bit.

#include <iostream>
#include <string>

#include <arpa/inet.h> // htons, inet_addr
#include <netinet/in.h> // sockaddr_in
#include <sys/types.h> // uint16_t
#include <sys/socket.h> // socket, sendto
#include <unistd.h> // close

int main(int argc, char const *argv[])
{
    std::string hostname{"192.168.0.4"};
    uint16_t port = 9000;

    int sock = ::socket(AF_INET, SOCK_DGRAM, 0);

    sockaddr_in destination;
    destination.sin_family = AF_INET;
    destination.sin_port = htons(port);
    destination.sin_addr.s_addr = inet_addr(hostname.c_str());

    std::string msg = "Jane Doe";
    int n_bytes = ::sendto(sock, msg.c_str(), msg.length(), 0, reinterpret_cast<sockaddr*>(&destination), sizeof(destination));
    std::cout << n_bytes << " bytes sent" << std::endl;
    ::close(sock);

    return 0;
}
stackprotector
  • 10,498
  • 4
  • 35
  • 64
Mikolasan
  • 626
  • 10
  • 19
0

For Windows, I took Mikolasan's minimised version of selbie's code and modified according to https://beej.us/guide/bgnet/html/#windows to get a small standalone example.

To get this to compile, you'll need to link the Winsock library.

#include <iostream>
#include <string>

#include <winsock2.h>

int main()
{
    // Initialise Winsock DLL
    // See https://beej.us/guide/bgnet/html/#windows 
    WSADATA wsaData;      
    // MAKEWORD(1,1) for Winsock 1.1, MAKEWORD(2,0) for Winsock 2.0
    if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) {
        fprintf(stderr, "WSAStartup failed.\n");
        exit(1);
    }
    
    // Set up connection and send 
    std::string hostname{ "192.168.0.4" };
    uint16_t port = 9000;

    SOCKET sock = ::socket(AF_INET, SOCK_DGRAM, 0);
     
    sockaddr_in destination;
    destination.sin_family = AF_INET;
    destination.sin_port = htons(port);
    destination.sin_addr.s_addr = inet_addr(hostname.c_str());

    std::string msg = "Jane Doe";
    int n_bytes = ::sendto(sock, msg.c_str(), msg.length(), 0, reinterpret_cast<sockaddr*>(&destination), sizeof(destination));
    std::cout << n_bytes << " bytes sent" << std::endl;
    ::closesocket(sock);

    // Clean up sockets library
    WSACleanup();

    return 0;

}
KMcN
  • 3
  • 2