Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411423 Posts in 69363 Topics- by 58416 Members - Latest Member: JamesAGreen

April 19, 2024, 07:08:46 AM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsCommunityTownhallForum IssuesArchived subforums (read only)TutorialsWinsock and C++ Super Basic Chat Program
Pages: [1]
Print
Author Topic: Winsock and C++ Super Basic Chat Program  (Read 45574 times)
Moosader
Level 0
***


View Profile WWW
« on: October 10, 2008, 10:53:48 AM »

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. Tongue



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 client
The 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.

Quote from: Socket.h
//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.

Quote from: Socket.cpp

//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.

Quote from: Socket.cpp

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

Quote from: Socket.h

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

Quote from: Socket.cpp

void ServerSocket::StartHosting( int port )
{
     Bind( port );
     Listen();
}

Calls my bind function, and then listen

Quote from: Socket.cpp

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.

Quote from: Socket.cpp


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
« Last Edit: March 19, 2009, 09:09:12 PM by Derek » Logged

Ivan
Owl Country
Level 10
*


alright, let's see what we can see


View Profile
« Reply #1 on: October 10, 2008, 11:03:31 AM »

Very nice!! I remember learning Winsock in the 90s and there were pretty much 0 resources on it anywhere.

Good job!  Beer!
Logged

http://polycode.org/ - Free, cross-platform, open-source engine.
increpare
Guest
« Reply #2 on: October 10, 2008, 03:32:24 PM »

My first attempt at networking involved putting an executable in a shared folder, and have it communicate by reading/writing a file in the app directory.  It crashed not infrequently, but it worked and was, as such, a fantastic success ;P
Logged
Cymon
Level 9
****


Computer Kid


View Profile WWW
« Reply #3 on: October 11, 2008, 08:19:23 AM »

Oh wow. Now it won't work in Linux will it? What would it take to rewrite the code to be cross platform compatible?

Still, I think I'm going to link to this.
Logged

Cymon's Games, free source code, tutorials, and a new game every week!
Follow me on twitter
Gnarf
Guest
« Reply #4 on: October 11, 2008, 08:37:40 AM »

You might want to take a look at Berkley sockets or something. And maybe something like this.

My first attempt at networking involved putting an executable in a shared folder, and have it communicate by reading/writing a file in the app directory.  It crashed not infrequently, but it worked and was, as such, a fantastic success ;P
I made some split-screen stuff with The Games Factory with a similar approach once. It's not a very good approach Smiley
Logged
Moosader
Level 0
***


View Profile WWW
« Reply #5 on: October 11, 2008, 11:42:31 AM »

Oh wow. Now it won't work in Linux will it? What would it take to rewrite the code to be cross platform compatible?

Still, I think I'm going to link to this.

I think, from what I've read, a lot of the functions are really similar like send() and recv().
In BeeJ's tutorial (http://www.beej.us/guide/bgnet/output/html/singlepage/bgnet.html),

Quote
This is what you'll have to do (unless you install Cygwin!): first, ignore pretty much all of the system header files I mention in here. All you need to include is:

#include <winsock.h>

Wait! You also have to make a call to WSAStartup() before doing anything else with the sockets library. The code to do that looks something like this:

#include <winsock.h>

{
    WSADATA wsaData;   // if this doesn't work
    //WSAData wsaData; // then try this instead

    if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) {
        fprintf(stderr, "WSAStartup failed.\n");
        exit(1);
    }

You also have to tell your compiler to link in the Winsock library, usually called wsock32.lib or winsock32.lib or some-such. Under VC++, this can be done through the Project menu, under Settings.... Click the Link tab, and look for the box titled "Object/library modules". Add "wsock32.lib" to that list.

Or so I hear.

Finally, you need to call WSACleanup() when you're all through with the sockets library. See your online help for details.

Once you do that, the rest of the examples in this tutorial should generally apply, with a few exceptions. For one thing, you can't use close() to close a socket—you need to use closesocket(), instead. Also, select() only works with socket descriptors, not file descriptors (like 0 for stdin).


I'm having trouble getting SDL_Net to work, otherwise I would be learning that, but I'm under quite a bit of a time limit and only have time to play with what works right now.
Logged

Pages: [1]
Print
Jump to:  

Theme orange-lt created by panic