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;
}