Translate

Thursday, March 27, 2014

Basic Server/Client Application - Network Programming

   Today, I’d like to get into programming sockets, and help explain a little about network programming.  While network programming is a lot to understand, it is easy to understand once you get the hang of the basic functions needed to get a client-server connection established. First let’s go through some basic terminology of networking.

Sockets: There are object inside of the source code that create an interface with the network, and act as a connection to the transport layer of the IP(Internet Protocol).

Client: The local application a user starts is called the application.

Server: The application on the end that receives messages from the client’s application instance.

Protocol: A protocol is a system of rules for the exchange of data across a network. There are many protocols, made up of layers for each protocol used by an application. For this tutorial, we will be utilizing the UDP protocol for passing a basic message. There is also the TCP protocol, which came first and serves a different purpose, but with the same principal; to enable the transfer of data.

Port: A number, specifying the location for the protocol to be used on the transport layer (i.e. 80 is for HTTP, which is used to browse the web.)

Internet Address: A 12 character number, which describes the location of the physical computer used for an application; Necessary for finding and establishing a connection to the client’s and to connect to the server.

   Ok, now that we have an understanding of the terms for a network application, let’s get into a basic networking application. Both files are quite similar, so lets start with defining the methods used:

struct sockaddr_in sockServer, sockClient;
WSADATA wsa;
SOCKET s, c;
int slen = sizeof(sockClient) , recv_len;
char buf[BUFLEN];

   The first few lines in main() set some variables and data structures to be used in the application. The first two are structures, called sockaddr_in. These are socket address interface object, and they store the socket information for our application, for the client and the server. The second of the two variables are the SOCKET variables, which act an the interface objects to the transport layer of the IP suite. The next variable is the WSADATA variable; this is a winsock specific variable, and we will need to initialize this in order to get the application to work on windows correctly. There is also two integers for measuring the length of the socket (slen) and of the data incoming (recv_len). The final variable, buf[], is the buffer for our data; an array of characters, with BUFLEN as a defined value constant for determining the length of the message.

if(WSAStartup(MAKEWORD(2,2),&wsa) != 0)
       {
           printf("Failed. Error Code : %d",WSAGetLastError());
       }

   This if loop will setup WinSock for our application with the wsa variable, and return a error message if it can't initialize properly.

s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

   This is where the server socket is initialized. AF_INET tells this socket that it will be used over the internet. SOCK_DGRAM tells this socket to send data as a datagram, and the third parameter, IPPROTO_UDP will set the protocol used to UDP (User Datagram Protocol).

sockServer.sin_family = AF_INET;
sockServer.sin_port = htons(SERVERPORT);
sockServer.sin_addr.s_addr = inet_addr("127.0.0.1");

   These lines of code will initialize the sockaddr_in object with information about the computer being connected to. The first line is AF_INET, which tells the structure to use the IP suite. The second one assigned a port number - this instance has the user assigned a port number to a specific number, but normally this area is used for port numbers used for protocol numbers like HTTP (80) or UDP (67). And inet_addr assigns a numerical address to the structure, to identify the specific computer to connect to. In this case, the number "127.0.0.1" is used to indicate the local host, meaning that same computer used for the server is also the same address for the client; we will be using this for the tutorial to keep this simple, and I will go into this later on.

bind(s , (struct sockaddr*)&sockServer, sizeof(sockServer) );

  Before the main loop our application utilizes, we call bind, to bind the socket 's' to the address structure 'sock'server'. This means calling the sizeof() method for the structure as well.

recv_len = recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &sockClient, &slen);

   This line of code start with the variable; that variable will get the number of bytes received from the incoming data. That value is returned from the recv_from() method.

   The recv_from() method receives data from another computer; this is the method to test for incoming data. The first parameter in the recv_from method is the bound socket, for our current application. The second and third parameters are the buffer for the data and the buffer size, respectively. The fourth parameter is for setting flags ('0' in this case). The fifth parameter take a sockaddr_in object, passing the address information from the source into the object. And the sixth parameter takes the size in bytes of the fifth parameter's structure.

sendto(c, buf, recv_len, 0, (struct sockaddr*) &sockClient, slen);

   This method finally sends the data out to another computer; the sendto() method. The parameters in this method are identical to the recv_from arguments; the main difference is that all the parameters are inputs, meaning all the objects in the sendto() method are being utilized, and no data is sent out from this function to any of the objects passed as arguments, unlike recv_from().

closesocket(c);

   Finally, we call closesocket() to close the socket in use. This takes a single parameter, the socket. It is pretty simple to understand.

I’ve included the source code for both the client and the server instances below for you to run; just remember to run the server application first before using the client. I hope this tutorial was helpful in illuminating exactly how an application works over the internet. It serves to show exactly how an application works from a windows stand point.

   To compile, simply put both files in a directory, navigate to that directory with cd, then, assuming you have GCC, type g++ -Wall server.c –o serverAppName.exe –lWs2_32. Please remember the –l and Ws2_32, as these are the libraries for Windows to work with sockets. Without them the application will not work.
Have a fantastic day! :D
Server Code:
#include <stdio.h> //printf
#include <string.h> //memset
#include <stdlib.h> //exit(0)

#ifdef WIN32
#include <winsock2.h>
#include <winsock.h>
#else
#include <arpa/inet.h>
#include <sys/socket.h>
#endif

#define BUFLEN 512  //Max length of buffer
#define SERVERPORT 1111   //The port on which to listen for incoming data
#define CLIENTPORT 1112

int main(int argc, char **argv)
{
    struct sockaddr_in sockServer, sockClient;
       WSADATA wsa;
    SOCKET s, c;
       int slen = sizeof(sockClient) , recv_len;
    char buf[BUFLEN];
      
       if(WSAStartup(MAKEWORD(2,2),&wsa) != 0)
       {
           printf("Failed. Error Code : %d",WSAGetLastError());
       }
    
    //create a UDP socket
    s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    
    // zero out the structure
    memset((char *) &sockServer, 0, sizeof(sockServer));
    
       //Assign IP address and port number for the server
    sockServer.sin_family = AF_INET;
    sockServer.sin_port = htons(SERVERPORT);
    sockServer.sin_addr.s_addr = inet_addr("127.0.0.1"); //Localhost
    
    //bind socket to the port number
    bind(s , (struct sockaddr*)&sockServer, sizeof(sockServer) );
    
    //keep listening for data
    while(1)
    {
              //Send message and clear standard buffers
        printf("Waiting for data...");
        fflush(stdout);
              fflush(stdin);
        
        //try to receive some data, this is a blocking call
        recv_len = recvfrom(s, buf, BUFLEN, 0, (struct sockaddr *) &sockClient, &slen);
             
              //print details of the client/peer and the data received
              printf("Received packet from %s:%d\n", inet_ntoa(sockClient.sin_addr), ntohs(sockClient.sin_port));
              buf[recv_len];
              printf("Data: %s\n" , buf);
                    
              //Create client socket for server side connection.
              c = socket(AF_INET, SOCK_DGRAM, 0);
             
              sockClient.sin_family = AF_INET;
              sockClient.sin_port = htons(CLIENTPORT);
              sockClient.sin_addr.s_addr = inet_addr("127.0.0.1");
        
        //now reply the client with the same data
        sendto(c, buf, recv_len, 0, (struct sockaddr*) &sockClient, slen);
             
              //Close the socket connection the server to the client
              closesocket(c);
    }
      
       //Close the server socket and close the application
       closesocket(s);
    WSACleanup();
      
    return 0;
}

Client Code:
#include<stdio.h> //printf
#include<string.h> //memset
#include<stdlib.h> //exit(0)

#ifdef WIN32
#include <winsock2.h>
#include <winsock.h>
#else
#include <arpa/inet.h>
#include<sys/socket.h>
#endif

#define BUFLEN 512  //Max length of buffer
#define SERVERPORT 1111   //The port on which to listen for incoming data
#define CLIENTPORT 1112

int main(int argc, char **argv)
{
    struct sockaddr_in sockHost, sockServer;
    SOCKET s, c;
       WSADATA wsa;
       int slen = sizeof(sockServer) , recv_len;
    char buf[BUFLEN];
    
       if(WSAStartup(MAKEWORD(2,2),&wsa) != 0)
       {
           printf("Failed. Error Code : %d",WSAGetLastError());
       }
      
    //create a UDP socket
    c = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    
    // zero out the structure
    memset((char *) &sockHost, 0, sizeof(sockHost));
    
    sockHost.sin_family = AF_INET;
    sockHost.sin_port = htons(CLIENTPORT);
    sockHost.sin_addr.s_addr = inet_addr("127.0.0.1"); //Localhost
    
    //bind socket to port
    bind(c , (struct sockaddr*)&sockHost, sizeof(sockHost) );
    
    //keep listening for data
    while(1)
    {
              //Get input from the user, and put the data into the buffer
        printf("Input data...");
        fflush(stdin);
              fgets(buf, 512, stdin);
              printf("Client: %s", buf);
             
              //Create the client-side socket for the server
              s = socket(AF_INET, SOCK_DGRAM, 0);
              sockServer.sin_family = AF_INET;
              sockServer.sin_port = htons(SERVERPORT);
              sockServer.sin_addr.s_addr = inet_addr("127.0.0.1");
             
        //now reply the server with the input data
        sendto(s, buf, BUFLEN, 0, (struct sockaddr*) &sockServer, slen);
             
              closesocket(s);
             
        //receive some data, this is a blocking call
        recv_len = recvfrom(c, buf, BUFLEN, 0, (struct sockaddr *) &sockServer, &slen);
        
              printf("Server: %s", buf);
    }

    closesocket(c);
       WSACleanup();
    return 0;

}

No comments:

Post a Comment