This is just a short tutorial on how to use Winsock2 with C++. It has blocking sockets (as opposed to non-blocking or asynchronous), so server and client have to take turns chatting. ;P
*EDIT* Oh yeah, first thing you want to go is go to the linker and add libws2_32.a
For DevC++, go to project options > parameters > Add Library or Object.
Go to the DevC++ program folder, into the libs folder, and libws2_32.a should be in there.
It's similar for Code::Blocks, I think both IDEs have this file with it by default.
I'd imagine Visual Studio is also similar, but I don't use that.
I'm not that good at explaining things, though. @_@
I prefer reading clear code than reading words when I'm learning, though. D:
Basics steps of a server and clientThe server will Bind to a port, begin Listening, and then Accept connection if received.
The client will attempt to Connect to an IP address and port given
Both will send and receive data, and have commands to disconnect.
Socket Code...For the cruddy little wrapper I wrote, I have the basic socket class and then ServerSocket and ClientSocket inherit from it.
//Socket.h
#pragma once
#include <iostream>
#include "WinSock2.h"
using namespace std;
const int STRLEN = 256;
class Socket
{
protected:
WSADATA wsaData;
SOCKET mySocket;
SOCKET myBackup;
SOCKET acceptSocket;
sockaddr_in myAddress;
public:
Socket();
~Socket();
bool SendData( char* );
bool RecvData( char*, int );
void CloseConnection();
void GetAndSendMessage();
};
WSADATA stores info about Winsock, like version...
I pretty much just use it in the winsock startup.
//Socket.cpp
#include "Socket.h"
Socket::Socket()
{
if( WSAStartup( MAKEWORD(2, 2), &wsaData ) != NO_ERROR )
{
cerr<<"Socket Initialization: Error with WSAStartup\n";
system("pause");
WSACleanup();
exit(10);
}
//Create a socket
mySocket = socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
if ( mySocket == INVALID_SOCKET )
{
cerr<<"Socket Initialization: Error creating socket"<<endl;
system("pause");
WSACleanup();
exit(11);
}
myBackup = mySocket;
}
This is the socket initialization.
WSAStartup's parameters are the version number, and a reference to our wsaData object.
If there's an error initializing the socket then we'll clean up and exit.
After that we actually create the socket we'll use the socket() function
to initialize.
AF_INET means "Address Family",
SOCK_STREAM means connection-oriented, rather than connectionless I believe.
IPPROTO_TCP is the IP protocol we're using.
Socket::~Socket()
{
WSACleanup();
}
bool Socket::SendData( char *buffer )
{
send( mySocket, buffer, strlen( buffer ), 0 );
return true;
}
bool Socket::RecvData( char *buffer, int size )
{
int i = recv( mySocket, buffer, size, 0 );
buffer = '\0';
return true;
}
void Socket::CloseConnection()
{
//cout<<"CLOSE CONNECTION"<<endl;
closesocket( mySocket );
mySocket = myBackup;
}
void Socket::GetAndSendMessage()
{
char message[STRLEN];
cin.ignore();//without this, it gets the return char from the last cin and ignores the following one!
cout<<"Send > ";
cin.get( message, STRLEN );
SendData( message );
}
I think these are pretty basic. I wrote GetAndSendMessage to interface
with the program rather than the program actually calling SendData straight out.
This way, it's a bit cleaner.
I have cin.ignore(); because sometimes cin.get will grab the return character
from a past cin.
So we create a character array (STRLEN is 256), get a line of input, then call SendData.
SendData uses winsock's send() function.
Parameters are a socket that sends it, the buffer (the message), the length of the message, and
the 0 means
class ServerSocket : public Socket
{
public:
void Listen();
void Bind( int port );
void StartHosting( int port );
};
class ClientSocket : public Socket
{
public:
void ConnectToServer( const char *ipAddress, int port );
};
These are my inherited classes
void ServerSocket::StartHosting( int port )
{
Bind( port );
Listen();
}
Calls my bind function, and then listen
void ServerSocket::Bind( int port )
{
myAddress.sin_family = AF_INET;
myAddress.sin_addr.s_addr = inet_addr( "0.0.0.0" );
myAddress.sin_port = htons( port );
//cout<<"BIND TO PORT "<<port<<endl;
if ( bind ( mySocket, (SOCKADDR*) &myAddress, sizeof( myAddress) ) == SOCKET_ERROR )
{
cerr<<"ServerSocket: Failed to connect\n";
system("pause");
WSACleanup();
exit(14);
}
}
void ServerSocket::Listen()
{
//cout<<"LISTEN FOR CLIENT..."<<endl;
if ( listen ( mySocket, 1 ) == SOCKET_ERROR )
{
cerr<<"ServerSocket: Error listening on socket\n";
system("pause");
WSACleanup();
exit(15);
}
//cout<<"ACCEPT CONNECTION..."<<endl;
acceptSocket = accept( myBackup, NULL, NULL );
while ( acceptSocket == SOCKET_ERROR )
{
acceptSocket = accept( myBackup, NULL, NULL );
}
mySocket = acceptSocket;
}
For the server, when we bind we will bind the port, but
the sin_addr doesn't really have to be anything since the
server doesn't mess with one.
Just from what I've read, sin_family is "always supposed to be AF_INET".
We then bind mySocket to the sddress, and if there's an error, we
clean up and exit.
Then we use winsock's listen() function, and if it doesn't return
an error, we keep trying to accept a client until it's successful.
void ClientSocket::ConnectToServer( const char *ipAddress, int port )
{
myAddress.sin_family = AF_INET;
myAddress.sin_addr.s_addr = inet_addr( ipAddress );
myAddress.sin_port = htons( port );
//cout<<"CONNECTED"<<endl;
if ( connect( mySocket, (SOCKADDR*) &myAddress, sizeof( myAddress ) ) == SOCKET_ERROR )
{
cerr<<"ClientSocket: Failed to connect\n";
system("pause");
WSACleanup();
exit(13);
}
}
The Client's connect function will set up the IP address and port,
and attempt to connect. It passes myAddress, so the connect
function itself handles the IP and port.
Working .exe, DevC++ project file and source can be downloaded here:
http://moosader.deviantart.com/art/C-Winsock-Sample-Program-97696619