12. Sockets
Well, now you've got what it takes to write some fairly kewl programs that work with files on your computer, and can ease many common tasks. BUT WAIT! What about working across networks, and creating some kewl client server programs? Well, if you've ever used a chat program or web browser you might have thought what it would be like to create your own. That is
what this section is about. This is called 'Socket Programming' and I think this is the best part of programming. ( Of course this is comming from a hacker ;) )
Okay, lets jump into this. First thing you should do is login as root and go to the '/usr/include/sys' directory. Then open the 'socket.h' file in your favorite text editor. Now scroll down to where you see...
struct osockaddr
{
unsigned short int sa_family;
unsigned char sa_data[14];
};
Now this is kinda cryptic, so lets create another struct. Just below
the osockaddr struct type this:
struct SockAddr
{
short int Family;
unsigned short int Port;
struct in_addr Address;
unsigned char Zero[8];
};
Now that you have both of these structs socket programming is fairly simple. Now there are many tutorials on socket programming so I'm just going over one aspect. Here we will write a Simple TCP Stream client and server. This is enough to get the general idea, and write fairly good programs, TCP Stream programs are the most created on the net because it is the most reliable method of data transfer.
tcpclient.c
-------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
/* Yes there are allot of header files but you will need them all! */
#define PORT 6666 /* This will be the port that your client will
connect to on the remote machine. If this
client wants to connect to Telnet then you
would use port 23, FTP port 21, and the list
goes on, make sure if you write a server then
you make the ports in the 4 digit range so
not to interfer with other network programs. */
#define MAXDATASIZE 100 /* This is the maximum size of information
that can be recieved at a time from the
server. */
int main(int argc, char *argv[])
{
int sock_fd, numbytes;
/* Creates a socket file descriptor for data exchange,
numbytes holds the number of bytes recieved from
the server. */
char buffer[MAXDATASIZE];
/* This holds all the information sent from the server. */
struct hostent *he;
/* This structure is used to get the IP address from a hostname. */
struct SockAdder Their;
/* This is the structure you created to hold information
about the stream you will be using. */
if(argc != 2)
{
/* This checks to make sure that the right
amount of arguements were passed on the
command line. */
fprintf(stderr, "usage: client hostname\n";
exit (1);
}
if((he=gethostbyname(argv[1])) == NULL)
{
herror("gethostbyname");
exit (1);
}
/* socket() -
This is always the first part of a connection.
You will notice that the connections will be built by if
statements. The above if statement should be used if you
think the user might type in a domain name like...
"www.yahoo.com" instead of an IP address. Then to create
an internet connection using the TCP protocol type this
next if statement. */
if((sock_fd=socket(AF_INET,SOCK_STREAM,0)) == -1)
{
perror("socket");
exit (1);
}
/*
This is the information that will be used to make a connection.
just leave it like this everytime, I do it this way to make it
more readable. */
Their.Family = AF_INET;
/* This is the socket address family, This text only covers
AF_INET connections so if you want more info type 'man socket'
at your linux prompt. */
Their.Port = htons(PORT);
/* This is the port your client will be connecting to. For the
most part this will always be the same. Just always remember to
define your port at the beginning of your program. Or if you want
a user specified port you could use...
unsigned int PORT = argv[3];
just after main(). */
Their.Address = *((struct in_addr *)he->h_addr);
/* This is the address of the person your connecting to
for the most part this will stay the same too. */
bzero(&(Their.Zero), 8);
/* Don't ask just add it here everytime! */
if(connect(sock_fd, (struct sockaddr *)&Their, sizeof(struct sockaddr)) == -1)
{
/* This creates a full 'handshake' connection to
the remote host. ALL CLIENT PROGRAMS WILL HAVE THIS! */
perror("connect");
exit (1);
}
if((numbytes=recv(sock_fd,buffer,MAXDATASIZE,0)) == -1)
{
/* I will explain this a bit. recv() gets information
from the server through your file descriptor(sock_fd)
and places as much as it can, determined by MAXDATASIZE,
into 'buffer'. Then the number of bytes placed into
'buffer' is placed into 'numbytes'. */
perror("recv");
exit (1);
}
buffer[numbytes] = '\0';
/* A lacking carrage return is placed at the end of the buffer. */
printf("> %s", buffer);
/* prints all information in the buffer to the screen. */
close(sock_fd);
/* closes the connection */
return (0);
}
-------------------------------------------------
Now I have commented this enought that you should be able to figure it out on your own. I do recomend reading a guide on Networking to better understand the lingo, but other than that you should do fine.
Here we go, the client program is VERY easy but a bit more goes into server programs. Servers have to bind to a port, recieve connection, fork off a sub-proccess and handle sending and recieving data. I know it sounds like alot but I'm sure you'll have no problems following the comments. In this section I will OverComment for you! ;)
tcpserver.c
-------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
/* Yes, even more includes but once again you'll need them all! */
#define PORT 6666
/* This is the port the server will be listening on. */
#define BACKLOG 10
/* This is how many connections your server will accept at once. */
int main()
{
int sock_fd, new_fd;
/* Two file discriptors are created. Servers have to have
both. One will be used for when your server forks to the
client. */
struct SockAddr My;
struct SockAddr Their;
/* Creates a local socket and a remote socket. The remote
socket holds the person connectings information and the
local one holds your servers connection information. */
int Size;
/* This will hold the size of SockAddr for use with accept(). */
if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
{
/* creates your local socket, just like in the client */
perror("socket");
exit (1);
}
My.Family = AF_INET;
My.Port = htons(PORT);
/* The above two set up your SA_family and your local port
just as in the client. */
My.Address.s_addr = INADDR_ANY;
/* This sets the address to your current IP. For servers
this is what you always want. */
bzero(&(My.Zero), 8);
/* Once again, don't ask just do it. ;) */
if (bind(sock_fd, (struct sockaddr *)&My, sizeof(struct sockaddr)) == -1)
{
/* This binds your server to your local port.
This will keep other programs from trying
to use the same port. */
perror("bind");
exit (1);
}
if (listen(sock_fd, BACKLOG) == -1)
{
/* This statement tells your computer to sit and
wait and listen for up to 10, the amount of
BACKLOG, connections. */
perror("listen");
exit(1);
}
while (1)
{
/* while(1) is called a forever loop, meaning that
it will just loop over constantly until return()
is called with an int. */
Size = sizeof(struct SockAddr);
/* Stores the size of struct SockAddr to Size for
use with accept. */
if ((new_fd = accept(sock_fd, (struct sockaddr *)&Their, &Size)) -1)
{
/* The above is called when a connection
is made, it uses your other file descriptor
'new_fd' to recieve their socket information. */
perror("accept");
printf("> %s has connected!\n", inet_ntoa(Their.Address));
/* Prints the IP address of the connecting person on the
screen. Thats about all you get on the server side.
This is good if you want to get someones IP for some
reason ;) just create this client and have the connect
to you on the 'PORT' port and it will display their
IP on screen. */
if (!fork())
{
/* Don't worry about that just use it.
I'm the kinda person that if you always
have to do it, then I'm not gonna take
time to explain it. Just do it. */
if(send(new_fd, "Hello Client!\n", 14, 0) == -1)
{
/* sends the string Hello Client! to
the client. For more advance servers
you would put a function there and
have all the operations placed in the
function. */
perror("send");
}
close(new_fd);
/* This closes the file descriptor. */
exit(0);
}
close(new_fd);
while(waitpid(-1,NULL,WNOHANG) > 0);
/* Once again just do it. */
}
}
------------------------------------
Okay that is enough for this tutorial. If you really want to get serious about C socket programming for linux go to...
http://www.programmersheaven.com/
or http://www.google.com/linux?q=Socket+Programming&hl=en
I forgot to tell you. Most of the stuff in this tutorial will work for you Windows Programmers, except for the Socket Programming. Windows have their own Socket API and I suggest looking for information at...
http://www.google.com/microsoft?q=Socket+Programming&hl=en
for more information. Well I hate to say it, but this is all I'm doing on C. I know it's not the best tutorial in the world, but it does get you started. After all I'm more or less a C++ coder but there is the occasional job the C is need. I by no means think C++ is better than C, it's just my preferred language, I mean ASM is much better than C or C++ but there is no way in HELL I would write all my programs in it! So for now I'm out, LaTeRz!
|