Advertisement

send / recv errors

Started by April 09, 2018 02:03 PM
8 comments, last by cowcow 6 years, 7 months ago

Hello, I'm writing a small TCP client/server using Winsock and I'm stuck with errors upon calling send or recv. Am I missing something obvious?


	#include <winsock2.h>
#include <windows.h>
#pragma comment(lib, "ws2_32")
	#include <iostream>
#include <string>
#include <sstream>
#include <cstring>
using std::cout;
using std::endl;
using std::string;
using std::istringstream;
using std::ios;
	bool stop = false;
SOCKET tcp_socket = INVALID_SOCKET;
enum program_mode { talk_mode, listen_mode };
	void print_usage(void)
{
    cout << "  USAGE:" << endl;
    cout << "    Listen mode:" << endl;
    cout << "      tcpspeed PORT_NUMBER" << endl;
    cout << endl;
    cout << "    Talk mode:" << endl;
    cout << "      tcpspeed TARGET_HOST PORT_NUMBER" << endl;
    cout << endl;
    cout << "    ie:" << endl;
    cout << "      Listen mode: tcpspeed 1920" << endl;
    cout << "      Talk mode:   tcpspeed www 342" << endl;
    cout << "      Talk mode:   tcpspeed 10.200.67.1 950" << endl;
    cout << endl;
}
	bool verify_port(const string &port_string, unsigned long int &port_number)
{
    for (size_t i = 0; i < port_string.length(); i++)
    {
        if (!isdigit(port_string[i]))
        {
            cout << "  Invalid port: " << port_string << endl;
            cout << "  Ports are specified by numerals only." << endl;
            return false;
        }
    }
	    istringstream iss(port_string);
    iss >> port_number;
	    if (port_string.length() > 5 || port_number > 65535 || port_number == 0)
    {
        cout << "  Invalid port: " << port_string << endl;
        cout << "  Port must be in the range of 1-65535" << endl;
        return false;
    }
	    return true;
}
	bool init_winsock(void)
{
    WSADATA wsa_data;
    WORD ver_requested = MAKEWORD(2, 2);
	    if (WSAStartup(ver_requested, &wsa_data))
    {
        cout << "Could not initialize Winsock 2.2.";
        return false;
    }
	    if (LOBYTE(wsa_data.wVersion) != 2 || HIBYTE(wsa_data.wVersion) != 2)
    {
        cout << "Required version of Winsock (2.2) not available.";
        return false;
    }
	    return true;
}
	BOOL console_control_handler(DWORD control_type)
{
    stop = true;
    closesocket(tcp_socket);
	    return TRUE;
}
	bool init_options(const int &argc, char **argv, enum program_mode &mode, string &target_host_string, long unsigned int &port_number)
{
    if (!SetConsoleCtrlHandler((PHANDLER_ROUTINE)console_control_handler, TRUE))
    {
        cout << "  Could not add console control handler." << endl;
        return false;
    }
	    if (!init_winsock())
        return false;
	    string port_string = "";
	    if (2 == argc)
    {
        mode = listen_mode;
        port_string = argv[1];
    }
    else if (3 == argc)
    {
        mode = talk_mode;
        target_host_string = argv[1];
        port_string = argv[2];
    }
    else
    {
        print_usage();
        return false;
    }
	    cout.setf(ios::fixed, ios::floatfield);
    cout.precision(2);
	    return verify_port(port_string, port_number);
}
	void cleanup(void)
{
    // if the program was aborted, flush cout and print a final goodbye
    if (stop)
    {
        cout.flush();
        cout << endl << "  Stopping." << endl;
    }
	    // if the socket is still open, close it
    if (INVALID_SOCKET != tcp_socket)
        closesocket(tcp_socket);
	    // shut down winsock
    WSACleanup();
	    // remove the console control handler
    SetConsoleCtrlHandler((PHANDLER_ROUTINE)console_control_handler, FALSE);
}
	int main(int argc, char **argv)
{
    cout << endl << "tcpspeed 1.1 - TCP speed tester" << endl << "Copyright 2018, Shawn Halayka" << endl << endl;
	    program_mode mode = listen_mode;
	    string target_host_string = "";
    long unsigned int port_number = 0;
	    const long unsigned int tx_buf_size = 1450;
    char tx_buf[1450];
	    const long unsigned int rx_buf_size = 8196;
    char rx_buf[8196];
	    // initialize winsock and all of the program's options
    if (!init_options(argc, argv, mode, target_host_string, port_number))
    {
        cleanup();
        return 1;
    }
	    if (talk_mode == mode)
    {
        struct sockaddr_in their_addr;
        struct hostent *he = 0;
        he = gethostbyname(target_host_string.c_str());
	        if (NULL == he)
        {
            cout << "  Could not resolve target host." << endl;
            cleanup();
            return 2;
        }
	        their_addr.sin_family = AF_INET;
        their_addr.sin_port = htons((unsigned short int)port_number);
        their_addr.sin_addr = *((struct in_addr *)he->h_addr);
        memset(&(their_addr.sin_zero), '\0', 8);
	        if (INVALID_SOCKET == (tcp_socket = socket(AF_INET, SOCK_STREAM, 0)))
        {
            cout << "  Could not allocate a new socket." << endl;
            cleanup();
            return 3;
        }
	        if (SOCKET_ERROR == connect(tcp_socket, (struct sockaddr *)&their_addr, sizeof(struct sockaddr))) // (tcp_socket = socket(AF_INET, SOCK_STREAM, 0)))
        {
            cout << "  connect error." << endl;
            cleanup();
            return 3;
        }
	        cout << "  Sending on TCP port " << port_number << " - CTRL+C to exit." << endl;
	        while (!stop)
        {
            if (SOCKET_ERROR == (send(tcp_socket, tx_buf, tx_buf_size, 0)))
            {
                if (!stop)
                    cout << "  send error " << WSAGetLastError() << endl;
	                break;
            }
        }
    }
    else if (listen_mode == mode)
    {
        struct sockaddr_in my_addr;
        int sock_addr_len = sizeof(struct sockaddr);
	        my_addr.sin_family = AF_INET;
        my_addr.sin_port = htons((unsigned short int)port_number);
        my_addr.sin_addr.s_addr = INADDR_ANY;
        memset(&(my_addr.sin_zero), '\0', 8);
        
        if (INVALID_SOCKET == (tcp_socket = socket(AF_INET, SOCK_STREAM, 0)))
        {
            cout << "  Could not allocate a new socket." << endl;
            cleanup();
            return 4;
        }
	        if (SOCKET_ERROR == bind(tcp_socket, (struct sockaddr *) &my_addr, sizeof(struct sockaddr)))
        {
            cout << "  Could not bind socket to port " << port_number << "." << endl;
            cleanup();
            return 5;
        }
	        if (SOCKET_ERROR == listen(tcp_socket, 0))
        {
            cout << "  listen error." << endl;
            cleanup();
            return 6;
        }
	        if (SOCKET_ERROR == accept(tcp_socket, (struct sockaddr *) &my_addr, &sock_addr_len))
        {
            cout << "  accept error." << endl;
            cleanup();
            return 7;
        }
        // https://msdn.microsoft.com/en-us/library/windows/desktop/ms737593(v=vs.85).aspx
        // https://msdn.microsoft.com/en-us/library/windows/desktop/ms737591(v=vs.85).aspx
        // https://msdn.microsoft.com/en-us/library/windows/desktop/ms741394(v=vs.85).aspx
        //
        cout << "  Listening on TCP port " << port_number << " - CTRL+C to exit." << endl;
	        long unsigned int temp_bytes_received = 0;
	        while (!stop)
        {
            if (SOCKET_ERROR == (temp_bytes_received = recv(tcp_socket, rx_buf, rx_buf_size, 0)))
            {
                if (!stop)
                {
                    cout << "  recv error " << WSAGetLastError() << endl;
                    cleanup();
                    return 6;
                }
            }
        }
    }
	    cleanup();
	    return 0;
}
	 
	

 

The obvious thing you're missing is telling us which errors you get!

Advertisement

Sorry about that. Here are the errors:

server send error 10054 ( WSAECONNRESET , Connection reset by peer)

client recv error 10057 ( WSAENOTCONN , Socket is not connected)

I've tried using localhost for loopback. I also tried it on a pair of computers.

What?

That was just a spammer, now dealt with and the post removed.

Connection Reset By Peer means the other side "hung up". It's not necessarily an error as such. 'Socket is not connected' means just what it says, but you aren't reporting any error after the connect call, so who knows.

Are you running the server first, and then the client? What are the command line arguments that you use? What is the output? Can you add extra output to make absolutely sure that the connect and accept calls are working?

Advertisement

I've tried running the server first, and also the client first. Both result in the same errors.

I use the following command line arguments...

For the server (listen mode) : tcpspeed 1920

For the client (talk mode): tcpspeed localhost 1920

The full output for the server is:


tcpspeed 1.1 - TCP speed tester
Copyright 2018, Shawn Halayka
Listening on TCP port 1920 - CTRL+C to exit.
recv error 10057
	

The full output for the client is:


tcpspeed 1.1 - TCP speed tester
Copyright 2018, Shawn Halayka
Sending on TCP port 1920 - CTRL+C to exit.
send error 10054
	

Here is the code without any error checking, to simplify the code while testing:


	#include <winsock2.h>
#include <windows.h>
#pragma comment(lib, "ws2_32")
	#include <iostream>
#include <string>
#include <sstream>
#include <cstring>
using std::cout;
using std::endl;
using std::string;
using std::istringstream;
using std::ios;
	bool stop = false;
SOCKET tcp_socket = INVALID_SOCKET;
enum program_mode { talk_mode, listen_mode };
	bool verify_port(const string &port_string, unsigned long int &port_number)
{
    for (size_t i = 0; i < port_string.length(); i++)
    {
        if (!isdigit(port_string[i]))
        {
            cout << "  Invalid port: " << port_string << endl;
            cout << "  Ports are specified by numerals only." << endl;
            return false;
        }
    }
	    istringstream iss(port_string);
    iss >> port_number;
	    if (port_string.length() > 5 || port_number > 65535 || port_number == 0)
    {
        cout << "  Invalid port: " << port_string << endl;
        cout << "  Port must be in the range of 1-65535" << endl;
        return false;
    }
	    return true;
}
	bool init_winsock(void)
{
    WSADATA wsa_data;
    WORD ver_requested = MAKEWORD(2, 2);
	    if (WSAStartup(ver_requested, &wsa_data))
    {
        cout << "Could not initialize Winsock 2.2.";
        return false;
    }
	    if (LOBYTE(wsa_data.wVersion) != 2 || HIBYTE(wsa_data.wVersion) != 2)
    {
        cout << "Required version of Winsock (2.2) not available.";
        return false;
    }
	    return true;
}
	bool init_options(const int &argc, char **argv, enum program_mode &mode, string &target_host_string, long unsigned int &port_number)
{
    if (!init_winsock())
        return false;
	    string port_string = "";
	    if (2 == argc)
    {
        mode = listen_mode;
        port_string = argv[1];
    }
    else if (3 == argc)
    {
        mode = talk_mode;
        target_host_string = argv[1];
        port_string = argv[2];
    }
    else
    {
        return false;
    }
	    cout.setf(ios::fixed, ios::floatfield);
    cout.precision(2);
	    return verify_port(port_string, port_number);
}
	void cleanup(void)
{
    // if the program was aborted, flush cout and print a final goodbye
    if (stop)
    {
        cout.flush();
        cout << endl << "  Stopping." << endl;
    }
	    // if the socket is still open, close it
    if (INVALID_SOCKET != tcp_socket)
        closesocket(tcp_socket);
	    // shut down winsock
    WSACleanup();
}
	int main(int argc, char **argv)
{
    cout << endl << "tcpspeed 1.1 - TCP speed tester" << endl << "Copyright 2018, Shawn Halayka" << endl << endl;
	    program_mode mode = listen_mode;
	    string target_host_string = "";
    long unsigned int port_number = 0;
	    const long unsigned int tx_buf_size = 1450;
    char tx_buf[1450];
	    const long unsigned int rx_buf_size = 8196;
    char rx_buf[8196];
	    // initialize winsock and all of the program's options
    if (!init_options(argc, argv, mode, target_host_string, port_number))
    {
        cleanup();
        return 1;
    }
	    if (talk_mode == mode)
    {
        struct sockaddr_in their_addr;
        struct hostent *he = 0;
        he = gethostbyname(target_host_string.c_str());
	        if (NULL == he)
        {
            cout << "  Could not resolve target host." << endl;
            cleanup();
            return 2;
        }
	        their_addr.sin_family = AF_INET;
        their_addr.sin_port = htons((unsigned short int)port_number);
        their_addr.sin_addr = *((struct in_addr *)he->h_addr);
        memset(&(their_addr.sin_zero), '\0', 8);
	        tcp_socket = socket(AF_INET, SOCK_STREAM, 0);
        bind(tcp_socket, (struct sockaddr *) &their_addr, sizeof(struct sockaddr));
        connect(tcp_socket, (struct sockaddr *)&their_addr, sizeof(struct sockaddr));
	        cout << "  Talking on TCP port " << port_number << " - CTRL+C to exit." << endl;
	        while (!stop)
            send(tcp_socket, tx_buf, tx_buf_size, 0);
    }
    else if (listen_mode == mode)
    {
        struct sockaddr_in my_addr;
        int sock_addr_len = 0;
	        my_addr.sin_family = AF_INET;
        my_addr.sin_port = htons((unsigned short int)port_number);
        my_addr.sin_addr.s_addr = INADDR_ANY;
        memset(&(my_addr.sin_zero), '\0', 8);
        sock_addr_len = sizeof(struct sockaddr);
	        tcp_socket = socket(AF_INET, SOCK_STREAM, 0);
        bind(tcp_socket, (struct sockaddr *) &my_addr, sizeof(struct sockaddr));
        listen(tcp_socket, 0);
        accept(tcp_socket, (struct sockaddr *) &my_addr, &sock_addr_len);
	        cout << "  Listening on TCP port " << port_number << " - CTRL+C to exit." << endl;
	        while (!stop)
            recv(tcp_socket, rx_buf, rx_buf_size, sizeof(struct sockaddr));
    }
	    cleanup();
	    return 0;
}
	 
	

You cannot call send() without first calling connect(). You also cannot call recv() without first calling bind() and listen() and accept() (and calling recv() on the socket returned by accept(), not the original listening socket.)


bind(tcp_socket, (struct sockaddr *) &their_addr, sizeof(struct sockaddr));

You cannot bind to someone else's address. You bind() when you want to listen(), not when you want to connect().

You should check errors from all calls you make, bind() would have told you this.

Also, you should set your editor settings to "always convert tabs to spaces" so the code is easier to read when you paste it.

You can also make your code shorter and more concise. For example:


struct sockaddr_in their_addr;
        memset(&(their_addr.sin_zero), '\0', 8);

Instead, do this:


struct sockaddr_in their_addr = {};

Separately, to verify that something is a number, it's easier to just do:


char *end = nullptr;
long l = strtol(portstr, &end, 10);
if !end || *end || l < 1 || l > 65535) {
  // the input port string is wrong
}

Finally, it's typically a better idea to use getaddrinfo() to look up hostnames, than to use gethostbyname() and create the address yourself. This is much more compatible with IPv4/IPv6, as well as different socket types. getaddrinfo() will also convert the port number for you.

 

enum Bool { True, False, FileNotFound };

Thanks very much for the help... it did the trick. I will look into replacing the port verification function.

Yes I was misusing accept() and recv().

A working code is at https://github.com/sjhalayka/tcpspeed

This topic is closed to new replies.

Advertisement