-1

I've been doing the Winsock tutorials and following it exactly. I can't seem to get either send() or recv() to function properly. I have a basic Server and Client program set up, and the connection is being made, but the Server isn't sending a message to the Client. Using the Telnet client also doesn't receive a response from the server. I'm not really sure what's happening, and all the questions I looked at were not basic or had stuff I couldn't really understand what they were doing.

Server.cpp

#include<WinSock2.h>
#include <io.h>
#include <stdio.h>

#pragma comment(lib, "ws2_32.lib")  //winsock library

int main(int argc, char *argv[])
{
    WSADATA wsa;
    SOCKET s, new_socket;
    sockaddr_in server, client;
    int c;
    char *message;

    printf("\nInitialising Winsock...");
    if (WSAStartup(MAKEWORD(2,2), &wsa) != 0)
    {
        printf("Failed. Error Code : %d", WSAGetLastError());
        return 1;
    }
    else
        printf("Initialised.\n");

    //create a socket
    if((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
    {
        printf("Could not create socket : %d", WSAGetLastError());
        return 2;
    }
    else
        printf("Socket created.\n");

    //prepare the sockaddr_in structure
    server.sin_family = AF_INET;
    server.sin_addr.S_un.S_addr = INADDR_ANY;
    server.sin_port = htons(8888);

    //bind the socket
    if (bind(s, (sockaddr *)&server, sizeof(server)) == SOCKET_ERROR)
    {
        printf("Bind failed with error code : &d", WSAGetLastError());
        return 3;
    }
    else
        puts("Bind done");

    //listen
    listen(s, 3);

    //accept an incoming connection
    puts("Waiting for incoming connections...");

    c = sizeof(sockaddr_in);

    while (new_socket = accept(s, (sockaddr *)&client, &c) != INVALID_SOCKET)
    {
        printf("Connect successful...\n");

        //reply to the client
        message = "Hello Client, I have recieved your connection, but I have to go now, bye!\n";
        send(new_socket, message, strlen(message), 0);

        puts("Message sent.\n");
    }

    if (new_socket == INVALID_SOCKET)
    {
        printf("accept() failed with error code : %d", WSAGetLastError());
        return 4;
    }

    //close the socket
    closesocket(s);
    WSACleanup();

    return 0;
}

Client.cpp

#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif

#include <Windows.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <IPHlpApi.h>
#include <stdio.h>

#pragma comment(lib, "Ws2_32.lib")

int main(int argc, char *argv[])
{
    //intialize variables
    WSADATA wsa;
    char ip[100] = "192.168.1.117";
    SOCKET s;
    sockaddr_in server;
    char *message, server_reply[75];
    int recv_size;

    //initialize Winsock
    printf("\nInitialising Winsock...\n");
    if(WSAStartup(MAKEWORD(2,2), &wsa) != 0)
    {
        printf("Failed. Error Code : %d", WSAGetLastError());
        return 1;
    }
    printf("Initialised.\n");

    //create the socket
    if((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
    {
        printf("Could not create socket : %d", WSAGetLastError());
        return 3;
    }
    printf("Socket created.\n");

    server.sin_addr.S_un.S_addr = inet_addr(ip);
    server.sin_family = AF_INET;
    server.sin_port = htons(8888);

    //connect to the server
    if (connect(s, (sockaddr *)&server, sizeof(server)) < 0)
    {
        puts("connect error");
        return 4;
    }
    else
    {
        printf("Connect successful");

        recv_size = recv(s, server_reply, 75, 0);
    }

    if (recv_size <= 0)
    {
        puts("recv() failed\n");
    }
    else
    {
        //add a NULL terminating character to make it a proper string before printing
        server_reply[recv_size] = '\0';
        puts(server_reply);
    }

    getchar();

    //close the socket
    closesocket(s);
    WSACleanup();

    return 0;
}

The client also fails to print the "recv() failed" line; it's like it's stuck at the recv() call.

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
L.Moyer
  • 81
  • 1
  • 10
  • 1
    You'll have to dig deeper than "it fails" "it doesn't function correctly". You could run Wireshark and examine network traffic. I think your packets are being blocked somewhere. Did you open port 8888 in your firewall? – Lightness Races in Orbit May 15 '17 at 16:11
  • I'd start by handling the error conditions of send and recv, then examining them in the debugger. – Christopher Pisz May 15 '17 at 16:14
  • 1
    You should check if `send()` failed: `if( send(...) < 0){...}` – Rama May 15 '17 at 16:33
  • Thank you guys for your help. Because the tutorial didn't error check the send(), recv() functions I didn't really think you needed to. They even said error checking was your friend. I'll have to download Wireshark for the future. After making the couple changes in the answer I chose, like a do-while loop, more error checking, and changing how the output for the message received would display, they managed to send and receive the packet. – L.Moyer May 16 '17 at 17:18

2 Answers2

2

On the server side:

  • you are not checking the return value of listen() for error.

  • you are not resetting c on each call to accept(), and you are not calling closesocket() on each client that is accepted.

  • you are not checking the return value of send() for error.

Try this instead:

#include <WinSock2.h>
#include <stdio.h>

#pragma comment(lib, "ws2_32.lib")  //winsock library

int main(int argc, char *argv[])
{
    WSADATA wsa;
    SOCKET s, new_socket;
    sockaddr_in server, client;
    int c, res, messagelen;
    const char *message;

    printf("\nInitializing Winsock...");
    res = WSAStartup(MAKEWORD(2,2), &wsa);
    if (res != 0)
    {
        printf("Failed. Error: %d\n", res);
        return 1;
    }
    printf("Initialized.\n");

    //create a socket
    if((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET)
    {
        printf("Could not create socket. Error: %d\n", WSAGetLastError());
        return 2;
    }
    printf("Socket created.\n");

    //prepare the sockaddr_in structure
    server.sin_family = AF_INET;
    server.sin_addr.S_un.S_addr = INADDR_ANY;
    server.sin_port = htons(8888);

    //bind the socket
    if (bind(s, (sockaddr *)&server, sizeof(server)) == SOCKET_ERROR)
    {
        printf("Bind failed. Error: &d", WSAGetLastError());
        return 3;
    }
    printf("Bind done.\n");

    //listen
    if (listen(s, 3) == SOCKET_ERROR)
    {
        printf("Listen failed. Error: &d", WSAGetLastError());
        return 4;
    }
    printf("Listening.\n");

    //accept incoming connections
    printf("Waiting for incoming connections...\n");

    do
    {
        c = sizeof(sockaddr_in);

        new_socket = accept(s, (sockaddr *)&client, &c);
        if (new_socket == INVALID_SOCKET)
        {
            printf("Failed to accept a client. Error: %d\n", WSAGetLastError());
            return 5;
        }

        printf("Client connected...\n");

        //reply to the client
        message = "Hello Client, I have received your connection, but I have to go now, bye!\n";
        messagelen = strlen(message);

        do
        {
            res = send(new_socket, message, messagelen, 0);
            if (res == SOCKET_ERROR)
            {
                printf("Failed to send message. Error: %d\n", WSAGetLastError());
                break;
            }

            message += res;
            messagelen -= res;
        }
        while (messagelen > 0);

        if (messagelen == 0)
            printf("Message sent.\n");

        //close the client socket
        closesocket(new_socket);
    }
    while (true);

    //close the server socket
    closesocket(s);
    WSACleanup();

    return 0;
}

On the client side, the only real problem I see is your recv() call has a potential buffer overflow waiting to happen, since you ask it to read 75 bytes, and that is the exact size of your buffer. It just happens that your server is only sending 74 bytes max, but if it ever sent more, you could overflow the buffer when appending the '\0' terminator to it.

So, either:

  • call recv() with sizeof(server_reply)-1 as the buffer size, to give yourself room for the added terminator:

    recv_size = recv(s, server_reply, sizeof(server_reply)-1, 0);
    
  • use printf() instead of puts() so you don't need to null-terminate the buffer at all when printing it to the console. You can pass recv_size as a parameter to limit the amount of text being printed:

    //server_reply[recv_size] = '\0';
    //puts(server_reply);
    printf("%.*s", recv_size, server_reply);
    
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • I didn't really think about checking if listen() had failed because the tutorial didn't check that or send()/recv() for errors either. I never used do-while loops either and was never really sure when they would be relevant. One thing I noticed is that the first "do" doesn't have a corresponding "while", so I just removed it. What condition should I put in that do-while loop so that it doesn't close after sending the data? Would using a variable in place of 3 in the "if (listen(s, 3) == SOCKET_ERROR", and then check the number of connections hasn't surpassed this number be good? – L.Moyer May 16 '17 at 17:07
  • @L.Moyer always check return values, you never know when something may go wrong. And you are right about the missing `while`, I added it now. – Remy Lebeau May 16 '17 at 17:16
  • I'll make sure to do that from now on. Thank you! – L.Moyer May 16 '17 at 17:30
0

From the MSDN documentation on closesocket():

Note To assure that all data is sent and received on a connection, an application should call shutdown before calling closesocket (see Graceful shutdown, linger options, and socket closure for more information). Also note, an FD_CLOSE network event is not posted after closesocket is called.

Basically the data you have sent was still pending when you closed the socket.

B. Szablak
  • 34
  • 4
  • `closesocket()` alone is sufficient. MSDN is talking about the rare case where you want to assure that all data is sent *and received,* which can be accomplished by both sides shutting down and then reading to end of stream. This is not necessary just to assure that all data is sent. – user207421 May 16 '17 at 00:05