Programming & Scripting Tutorials

C++: Winsock (Blocking Sockets)



Winsock is the Windows interface to TCP/IP (which means unfortunately that this lesson will be windows only).
We will be using blocking sockets (easier), which means the users will have to take it in turns to speak.

Since by the time you are reading this tutorial I expect you to be pretty good in C++; I'm simply going to give you the code, and you should be able to figure out whats going on from that.
Since this is a lesson, and not some type of exam, I've pretty much given you step by step comments of how everything works (so don't panic).
You can either download the heavily commented code here; or view it here:

Socket.h



#pragma once //Only process header files once
#pragma comment(lib, "WS2_32.lib") //Add "WS2_32.lib" as a linker input
#include <iostream> //Input/output stream
#include "WinSock2.h" //Include WinSock

using namespace std; //Standard namespace

const int STRLEN = 256; //Max length of our messages

class Socket //Socket base class
{
    protected:
        WSADATA wsaData; //wsaData
        SOCKET mySocket; //Socket
        SOCKET myBackup; //Socket
        SOCKET acceptSocket; //Socket
        sockaddr_in myAddress; //Socket Address
    public:
        Socket(); //"Prototype" constructor<
        ~Socket(); //"Prototype" destructor
        bool SendData( char* ); //"Prototype" SendData
        bool RecvData( char*, int ); //"Prototype" RecvData
        void CloseConnection(); //"Prototype" CloseConnection
        void GetAndSendMessage(); //"Prototype" GetAndSendMessage
}; //End base class

class ServerSocket : public Socket //Create our server socket class (derived)
{
    public:
        void Listen(); //"Prototype" Listen
        void Bind( int port ); //"Prototype" Bind
        void StartHosting( int port ); //"Prototype" StartHosting
}; //End ServerSocket class

class ClientSocket : public Socket //Create our client socket class (derived)
{
    public:
        void ConnectToServer( const char *ipAddress, int port ); //"Prototype" ConnectToServer
};


Socket.cpp



#include "Socket.h"

Socket::Socket() //Constructor
{
    if( WSAStartup( MAKEWORD(2, 2), &wsaData ) != NO_ERROR ) //WsaStartup
    {
        cerr<<"Socket Initialization: Error with WSAStartup\n"; //Error
        system("pause"); //Pause the program
        WSACleanup(); //Clean up
        exit(10); //Exit
    }

    //Create a socket
    mySocket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP ); //Set our socket variable

    if ( mySocket == INVALID_SOCKET ) //If the socket didn't set correctly
    {
        cerr<<"Socket Initialization: Error creating socket"<<endl; //Error
        system("pause"); //Pause the program
        WSACleanup(); //Clean up
        exit(11); //Exit
    }

    myBackup = mySocket; //Set the backup socket to the socket
}

Socket::~Socket() //Destructor
{
    WSACleanup(); //Clean up
}

bool Socket::SendData( char *buffer ) //SendData
{
    send( mySocket, buffer, strlen( buffer ), 0 ); //Send the data
    return true; //Return true
}

bool Socket::RecvData( char *buffer, int size ) //RecvData
{
    int i = recv( mySocket, buffer, size, 0 );
    buffer[i] = '\0';
    return true;
}

void Socket::CloseConnection()
{
    //cout<<"==CLOSE CONNECTION=="<<endl;
    closesocket( mySocket ); //Close the socket
    mySocket = myBackup; //Set the socket to the backup
}

void Socket::GetAndSendMessage() //GetAndSendMessage
{
    char message[STRLEN];//Char array
    cin.ignore();//Without this it uses the return char from last cin and passes this one!
    cout<<"> ";
    cin.get( message, STRLEN ); //Get the message
    SendData( message ); //Send the data
}

void ServerSocket::StartHosting( int port ) //StartHosting (for our ServerSocket class)
{
     Bind( port ); //Bind to the port
     Listen(); //Listen for connection attempts
}

void ServerSocket::Listen() //Listen (for our ServerSocket class)
{
    //cout<<"LISTEN FOR CLIENT..."<<endl;

    if ( listen ( mySocket, 1 ) == SOCKET_ERROR ) //If there is a problem
    {
        cerr<<"ServerSocket: Error listening on socket\n"; //Error
        system("pause"); //Pause the program
        WSACleanup(); //Clean up
        exit(15); //Exit
    }

    //cout<<"==ACCEPT CONNECTION=="<<endl;

    acceptSocket = accept( myBackup, NULL, NULL ); //Attempt to accept connection
    while ( acceptSocket == SOCKET_ERROR ) //While there is a problem
    {
        acceptSocket = accept( myBackup, NULL, NULL ); //Try to accept
    }
    mySocket = acceptSocket; //Set mySocket to acceptSocket
}

void ServerSocket::Bind( int port ) //Bind (for our ServerSocket class)
{
    myAddress.sin_family = AF_INET; //sin_family is always supposed to be AF_INET
    myAddress.sin_addr.s_addr = inet_addr( "0.0.0.0" ); //IP it connects to (dont need for server)
    myAddress.sin_port = htons( port ); //The port

    //cout<<"==BIND TO PORT=="<<port<<endl;

    if ( bind ( mySocket, (SOCKADDR*) &myAddress, sizeof( myAddress) ) == SOCKET_ERROR ) //If there is a problem
    {
        cerr<<"ServerSocket: Failed to connect\n"; //Error
        system("pause"); //Pause the program
        WSACleanup(); //Clean up
        exit(14); //Exit
    }
}

void ClientSocket::ConnectToServer( const char *ipAddress, int port ) //ConnectToServer (for our ClientSocket class)
{
    myAddress.sin_family = AF_INET; //sin_family is always supposed to be AF_INET
    myAddress.sin_addr.s_addr = inet_addr( ipAddress ); //IP it connects to
    myAddress.sin_port = htons( port ); //The port

    //cout<<"==CONNECTED=="<<endl;

    if ( connect( mySocket, (SOCKADDR*) &myAddress, sizeof( myAddress ) ) == SOCKET_ERROR ) //If there is a problem
    {
        cerr<<"ClientSocket: Failed to connect\n"; //Error
        system("pause"); //Pause the program
        WSACleanup(); //Clean up
        exit(13); //Exit
    }
}


Main.cpp


#include <iostream> //Input/output stream
#include <string> //For using strings
#include "Socket.h" //Our socket.h file

using namespace std; //Standard namespace

int main() //Our main function
{
    int choice; //Variable to hold the users choice
    int port = 666; //Port number
    //char *ipAddress = "127.0.0.1";   - If we wanted to set the IP to always be 127.0.0.1
    string ipAddress; //Our ipAddress string
    bool done = false; //Used for main loops
    char recMessage[STRLEN]; //Receive message array
    char sendMessage[STRLEN]; //Send message array 
    cout<<"1) Host server"<<endl; //Our host option
    cout<<"2) Join server"<<endl; //Our join option
    cout<<"3) Quit"<<endl; //Our quit option
    cin>>choice; //Wait for the user to input a choice
	if ( choice == 3 ){ //If its 3 (our exit option)
        exit(0); //Exit
	}
    else if ( choice == 2 ) //If its our join option
    {
        //Client
        cout<<"Enter an IP address (127.0.0.1 is the loopback address)"<<endl; //Prompt the user to enter an IP
        cin >> ipAddress; //Wait for the user to input an IP
        ClientSocket sockClient; //Create a sockClient variable (using the ClientSock class)
        cout<<"==TRYING TO CONNECT=="<<endl; //Tell the user we are trying to connect
        sockClient.ConnectToServer( ipAddress.c_str(), port ); //Connect
        //Connected
        while ( !done ) //While they are still talking
        { 
            sockClient.GetAndSendMessage(); //GetAndSendMessage
            //cout<<"==WAIT=="<<endl;
            sockClient.RecvData( recMessage, STRLEN ); //Receive data
            cout<<"> "<<recMessage<<endl; 
            if ( strcmp( recMessage, "end" ) == 0 ||strcmp( sendMessage, "end" ) == 0 ) //If anyone types "end"
            {
                done = true; //Break out of the loop
            }
        }
        sockClient.CloseConnection(); //Close the connection
    }
    else if ( choice == 1 ) //Our host option
    {
        //SERVER
        ServerSocket sockServer; //sockServer variable (from our ServerSocket class)
        cout<<"==HOSTING=="<<endl; //Tell the user we are hosting
        sockServer.StartHosting( port ); //StartHosting
        //Connected
        while ( !done ) //While they are still talking
        {
            //cout<<"==WAIT=="<<endl;
            sockServer.RecvData( recMessage, STRLEN ); //Receive data
            cout<<"> "<<recMessage<<endl;
            sockServer.GetAndSendMessage(); //GetAndSendMessage
            if ( strcmp( recMessage, "end" ) == 0 || strcmp( sendMessage, "end" ) == 0 ) //If anyone types "end"
            {
                done = true; //Break out of the loop
            }
        }
    }
}


This C++ tutorial was written by


Back to C++

Advertisement: