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
}
}
}
}
Back to C++

