13

As an attempt at wrapping my head around udp sockets I've tried to port the code from this tutorial page http://www.linuxhowtos.org/C_C++/socket.htm to winsock (running on win 8 if it matters). [direct links below]

The code currently compiles and runs but I never receive a packet, both programs just wait and block on recvfrom(). I keep having this same issue (for example with a modified version of this code https://stackoverflow.com/a/679529/873217 that includes a recvfrom() call. And with this code C++ problem with Datagram (UDP)winsocket to sendto and recvfrom on the same socket through the loopback adapter making the changes suggested). I imagine I'm making some simple and fundamental mistake; but I'm unable to locate it on my own. Hopefully someone with more experience can shine some light on this issue for me. Thanks.

Additional notes:
I am running the server exe and then the client exe on the same computer. It is connected to the internet. And I have tried using a domain that resolves to my IP address as well. I have also tried disabling the firewall, though I have permitted both programs full access.

direct links to original client and server code:
http://www.linuxhowtos.org/data/6/client_udp.c
http://www.linuxhowtos.org/data/6/server_udp.c

My current attempt:

both linked to libws2_32.a

client code:

#include <stdio.h>
#include <winsock2.h>
#include <string.h>

void error(const char *);

int main()
{
    WSAData data;
    WSAStartup( MAKEWORD( 2, 2 ), &data );

    int sock, n;
    int length;
    struct sockaddr_in server, from;
    struct hostent *hp;
    char buffer[256];
    unsigned short serverPort = 27072;
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) error("socket");
    server.sin_family = AF_INET;
    hp = gethostbyname("localhost");//have also tried my url
    if (hp==0) error("Unknown host");
    memmove((char *)hp->h_addr, (char *)&server.sin_addr, hp->h_length);
    server.sin_port = htons(serverPort);
    length = sizeof(struct sockaddr_in);
    printf("Please enter the message: ");
    memset(buffer, 0, 256);
    fgets(buffer, 255, stdin);
    n = sendto (sock, buffer, strlen(buffer), 0, (const struct sockaddr *)&server, length);
    if (n < 0) error("Sendto");
    printf("sent %i bytes\n", n);
    n = recvfrom(sock, buffer, 256, 0, (struct sockaddr *)&server, &length);
    if (n < 0) error("recvfrom");
    //write(1,"Got an ack: ", 12);
    //write(1, buffer, n);
    printf("Got msg: %s\n",buffer);

    closesocket(sock);
    WSACleanup();
    return(0);
}

void error(const char *msg)
{
    perror(msg);
    exit(0);
}

server code:

#include <stdio.h>
#include <winsock2.h>
#include <string.h>

void error(const char *);

int main()
{
    WSAData data;
    WSAStartup( MAKEWORD( 2, 2 ), &data );

    int sock, length, n;
    int fromlen;
    struct sockaddr_in server;
    struct sockaddr_in from;
    char buf[1024];
    unsigned short serverPort = 27072;
    sock=socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0) error("Opening socket");
    length = sizeof(server);
    memset(&server, 0, length);
    server.sin_family = AF_INET;
    server.sin_addr.s_addr=INADDR_ANY;
    server.sin_port=htons(serverPort);
    if (bind(sock,(struct sockaddr *)&server, length)<0) error ("binding");
    fromlen = sizeof(struct sockaddr_in);
    while(1)
    {
        n = recvfrom(sock, buf, 1024, 0, (struct sockaddr *)&from, &fromlen);
        if (n<0) error("recvfrom");
        //write(1,"Received a datagram: ", 21);
        //write(1,buf,n);
        printf("Received a datagram: %s", buf);
        n = sendto(sock, "Got your message\n",17,0,(struct sockaddr *)&from,fromlen);
        if (n<0)error("sendto");
    }
    closesocket(sock);
    WSACleanup();
    return 0;
}


void error (const char *msg)
{
    perror(msg);
    exit(0);
}
Community
  • 1
  • 1
Adam Phelps
  • 469
  • 1
  • 5
  • 13
  • Have found a working example here: http://www.codeproject.com/articles/11740/a-simple-udp-time-server-and-client-for-beginners HTH – Leo Chapiro Feb 02 '13 at 20:06
  • [Wireshark](http://www.wireshark.org/) can sometimes be a useful tool to debug this kind of network code issues, as it will show you what packets, if any, were exchanged between the client and the server. That should then tell you which program you should debug first. – m01 Feb 03 '13 at 00:35
  • Get it to work on 'localhost', (127.0.0.1), first. – Martin James Feb 03 '13 at 12:23
  • Thanks, I downloaded wireshark yesterday. I'm going to try to use it today. – Adam Phelps Feb 03 '13 at 13:22
  • I checked it. the code is fine and working. Recheck if you are using the same port number assigned in the server code. – Amit Kumar Jun 06 '19 at 08:35

2 Answers2

21

The following UDP test application works correctly on my setup - Windows 7, VS 2013.

I created some light-weight wrapper classes to do resource management, and turns error codes into exceptions. This makes the client and server code easier to read. Please be sure to run the server first.

Network.h

#include <WinSock2.h>
#include <WS2tcpip.h>
#include <system_error>
#include <string>
#include <iostream>

#pragma once

class WSASession
{
public:
    WSASession()
    {
        int ret = WSAStartup(MAKEWORD(2, 2), &data);
        if (ret != 0)
            throw std::system_error(WSAGetLastError(), std::system_category(), "WSAStartup Failed");
    }
    ~WSASession()
    {
        WSACleanup();
    }
private:
    WSAData data;
};

class UDPSocket
{
public:
    UDPSocket()
    {
        sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
        if (sock == INVALID_SOCKET)
            throw std::system_error(WSAGetLastError(), std::system_category(), "Error opening socket");
    }
    ~UDPSocket()
    {
        closesocket(sock);
    }

    void SendTo(const std::string& address, unsigned short port, const char* buffer, int len, int flags = 0)
    {
        sockaddr_in add;
        add.sin_family = AF_INET;
        add.sin_addr.s_addr = inet_addr(address.c_str());
        add.sin_port = htons(port);
        int ret = sendto(sock, buffer, len, flags, reinterpret_cast<SOCKADDR *>(&add), sizeof(add));
        if (ret < 0)
            throw std::system_error(WSAGetLastError(), std::system_category(), "sendto failed");
    }
    void SendTo(sockaddr_in& address, const char* buffer, int len, int flags = 0)
    {
        int ret = sendto(sock, buffer, len, flags, reinterpret_cast<SOCKADDR *>(&address), sizeof(address));
        if (ret < 0)
            throw std::system_error(WSAGetLastError(), std::system_category(), "sendto failed");
    }
    sockaddr_in RecvFrom(char* buffer, int len, int flags = 0)
    {
        sockaddr_in from;
        int size = sizeof(from);
        int ret = recvfrom(sock, buffer, len, flags, reinterpret_cast<SOCKADDR *>(&from), &size);
        if (ret < 0)
            throw std::system_error(WSAGetLastError(), std::system_category(), "recvfrom failed");

        // make the buffer zero terminated
        buffer[ret] = 0;
        return from;
    }
    void Bind(unsigned short port)
    {
        sockaddr_in add;
        add.sin_family = AF_INET;
        add.sin_addr.s_addr = htonl(INADDR_ANY);
        add.sin_port = htons(port);

        int ret = bind(sock, reinterpret_cast<SOCKADDR *>(&add), sizeof(add));
        if (ret < 0)
            throw std::system_error(WSAGetLastError(), std::system_category(), "Bind failed");
    }

private:
    SOCKET sock;
};

Server

#include "Network.h"

int main()
{
    try
    {
        WSASession Session;
        UDPSocket Socket;
        char buffer[100];

        Socket.Bind(100);
        while (1)
        {
            sockaddr_in add = Socket.RecvFrom(buffer, sizeof(buffer));

            std::string input(buffer);
            std::reverse(std::begin(input), std::end(input));
            Socket.SendTo(add, input.c_str(), input.size());
        }
    }
    catch (std::system_error& e)
    {
        std::cout << e.what();
    }
}

Client

#include "Network.h"

int main()
{
    try
    {
        WSASession Session;
        UDPSocket Socket;
        std::string data = "hello world";
        char buffer[100];

        Socket.SendTo("127.0.0.1", 100, data.c_str(), data.size());
        Socket.RecvFrom(buffer, 100);
        std::cout << buffer;
    }
    catch (std::exception &ex)
    {
        std::cout << ex.what();
    }
    char c;
    std::cin >> c;
}
Peter R
  • 2,865
  • 18
  • 19
  • 1
    Why doesn't use the function `SendTo(const std::string& address, unsigned short port, ...` the function `Sendto(sockaddr_in& address, ...)`? – harper Aug 12 '14 at 07:33
  • I'm sorry I didn't update my question when I found the error. And unfortunately I'm not really in the position to test right now. If you can confirm that your code works and mine doesn't on your machine, I'll accept your answer. It kinda seems like port forwarding was part of my problem. (though now that I think of it, I'm not sure that's applicable to localhost) – Adam Phelps Aug 13 '14 at 11:33
  • `add.sin_addr.s_addr = inet_addr(address.c_str());` is deprecated! – Rui Monteiro Apr 01 '23 at 23:24
0

Well, I'm not sure if winsock2.h works the same way in Windows and Linux, but, in Windows, when you create the socket, you must set the protocol you're using, either TCP or UDP:

SOCKET sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

AF_INET = IPv4; SOCK_STREAM = Byte stream for TCP; IPPROTO_TCP = TCP Protocol.

For UDP (which i've never used before), according to MSDN it would be:

SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

SOCK_DGRAM = Byte stream for UDP; IPPROTO_UDP = UDP Protocol.

That code would work in Windows. I guess in Linux it would be similar.

Mr. Hell
  • 116
  • 1
  • 5
  • Thanks for your reply, I'm currently using sock=socket(AF_INET, SOCK_DGRAM, 0); "If a value of 0 is specified, the caller does not wish to specify a protocol and the service provider will choose the protocol to use." Never the less I tried "IPPROTO_UDP" and got the same result. – Adam Phelps Feb 03 '13 at 22:55