5 - Sockets and forking.
===============================================================================
In this section there will be alot of new things, socket coding is the code
that makes an application able to connect to another computer or accept
connections from other computers, this is how the internet works.
A socket is an imaginary socket in the computer that opens a port
to send or receav something from the internet or a local network.
I will first make the example code and then I will explain it,
so let's go right to the first example of socket coding:
//----------------------------------------------------------
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main ( int argc, char **argv )
{
struct sockaddr_in address;
struct hostent *hp;
int port, sock;
char buffer[1024];
if ( argc < 3 ) {
printf ( "Usage: %s
exit ( -1 );
}
if ( ( sock = socket (AF_INET, SOCK_STREAM, 0 ) ) == -1 ) {
fprintf ( stderr, "Error\n" );
exit ( -1 );
}
printf ( "Resolving hostname...\n" );
if ( ( hp = gethostbyname ( argv[1] ) ) == NULL ) {
fprintf ( stderr, "Could not resolve %s.\n", argv[1] );
exit ( -1 );
}
port = atoi ( argv[2] );
printf ( "Connecting to host.\n" );
address.sin_family = AF_INET;
address.sin_port = htons ( port );
address.sin_addr.s_addr = *( u_long * ) hp->h_addr;
if ( connect ( sock, ( struct sockaddr * ) &address, sizeof ( struct sockaddr ) ) == -1) {
printf ( "Connction refused from host\n" );
exit ( -1 );
}
printf ( "Sending string to host.\n" );
sprintf ( buffer, "Something\r\n" );
send ( sock, buffer, strlen ( buffer ), 0 );
printf ( "Closing connection to host.\n" );
close ( sock );
return 0;
}
//----------------------------------------------------------
So now, what does all this mean ?
First we include the needed header files:
#include
#include
#include
#include
#include
#include
#include
#include
#include
Then we make and open the main function and then we make the 2 following
structs:
struct sockaddr_in address;
struct hostent *hp;
Then we have the "if the argument counter is less then 3" thing..
Then we have this:
if ( ( sock = socket (AF_INET, SOCK_STREAM, 0 ) ) == -1 )
AF_INET is the domain, SOCK_STREAM is the type and 0 is the protocol,
AF_INET or PF_INET means a normal IP as the domain.
SOCK_STREAM means a two-way connectionbased byte stream.
and 0 means no protocol.
For more info on this, look in the manual page for socket (man 2 socket).
From there to "hp = gethostbyname ( argv[1] )" should be undestandeble.
gethostbyname() will resolve a hostname, for more info on this function
look in the manual page.
Next new things:
address.sin_family = AF_INET;
address.sin_port = htons ( port );
address.sin_addr.s_addr = *( u_long * ) hp->h_addr;
These are declarations of family port and address, you may ask yourself
"where does the names "sin_family" etc. come from ?
They are defined in netinet/in.h.
The next new thing is:
if ( connect ( sock, ( struct sockaddr * ) &address, sizeof ( struct sockaddr ) ) == -1)
First the "( sock," comes from:
if ( ( sock = socket (AF_INET, SOCK_STREAM, 0 ) ) == -1 ) {
And is the socket with which we connect to the other computer.
Then we have a cast, ( struct sockaddr * ), then we have the address and then
bit lenth of the address.
This may be confusing, but that's the way connect() wants it to work.
Next new thing is:
send ( sock, buffer, strlen ( buffer ), 0 );
This will send the buffer to the socket.
send works like this:
int send ( int socket, const void *msg, int len, unsigned int flags );
This tells us that, send is an int.
That it wants a sock (which also is an int), a text message (or a pointer to
one), the string lenth of the message to send, and any flags, which here
is 0 because we aint using any flags.
And then we close the sock and return successful.
-------------------------------------------------------------------------------
You seriously need to play around some with this and read the source several
times to understand it better, you may and should also read this
tutorial more then one time to understand everything, and consult it
when you code things.
-------------------------------------------------------------------------------
Now let's make another example of socket coding and learn to fork() at the
same time.
A fork detaches a whole program or a bit of it from the main program so that
it can be in the background, so that you may log out from the shell but the
process is still running, this is how a daemon / server works.
Now let's make an example of a daemon.
//----------------------------------------------------------
#include
#include
#include
#include
#include
#include
#include
#include
#include
int main ( void )
{
pid_t pid;
int sock, send_sock, i;
struct sockaddr_in addr, info;
setsid ( );
umask ( 0 );
signal ( SIGCHLD, SIG_IGN );
if ( fork ( ) ) return 0;
sock = socket ( AF_INET, SOCK_STREAM, 0 );
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl ( INADDR_ANY );
addr.sin_port = htons ( 50 );
bind ( sock, ( struct sockaddr *) &addr, sizeof ( addr ) );
i = sizeof ( info );
listen ( sock, 1 );
for (;;) {
i = sizeof ( info );
send_sock = accept ( sock, ( struct sockaddr * ) &info, &i );
pid = fork ( );
if ( pid ) {
close ( send_sock );
continue;
}
else {
send ( send_sock, "sh-2.03# ", 9, 0 );
sleep ( 4 );
send (send_sock, "\nNah, just kidding :P\n", 24, 0);
sleep ( 3 );
close ( send_sock );
return 0;
}
}
return 0;
}
//----------------------------------------------------------
So now what does this mean ?
First we include the header files and create a main function.
Then we declare pid as pid_t, so that we have a pointer to the pid
(process ID), then we make a struct with pointers addr and info.
Then we use setsid(), (man 2 setsid for info on it).
And after that we use umask(), this is used to set the file premission,
you may "man 2 umask", if you wanna know exectly what it does.
The we use signal to ignore the sigchld, man 2 signal for more info on why
it acts the way it acts.
The we say:
if ( fork ( ) ) return 0;
This means that if the process if forked it should return successfully.
Then we create a socket.
And after that declare the ports etc. in the struct.
Then we use bind:
bind ( sock, ( struct sockaddr *) &addr, sizeof ( addr ) );
sock is the socket, and then there is the cast, the address and the
bit size of the address.
Then we say that i is the bit size of info.
i = sizeof ( info );
And then we say for it to listen on the sock we used bind to open.
listen ( sock, 1 );
The 1 in listen is the backlog (look in the manyal page for this).
Then we make a loop that may go on for ever that says:
That i is the bit size of info, again .... I had some problems with the
code when it wasnt there.
i = sizeof ( info );
And then we say that send_sock is like this:
send_sock = accept ( sock, ( struct sockaddr * ) &info, &i );
this means that send_sock is the accept, and so it's the incoming socket
connection.
Then we make sure that the pid is forked and so put to the background,
pid = fork ( );
If there is a pid, then close the socket and continue.
.....
if ( pid ) {
close ( send_sock );
continue;
}
.....
else do this (if it works out the way it's made to do:
.....
send ( send_sock, "sh-2.03# ", 9, 0 );
sleep ( 4 );
send (send_sock, "\nNah, just kidding :P\n", 24, 0);
sleep ( 3 );
close ( send_sock );
.....
This means send "sh-2.03# " to the user at the other end, and then
wait for 4 seconds and after that send "Nah, just kidding :P",
wait for another 3 seconds and then close the socket.
As we did:
addr.sin_port = htons ( 50 );
This daemon will be listening on port 50 ...
And since we are using raw socks, this will have to be started as user root.
Now I know I'm not explaining this as easy as I did with other codes, but
I'm doing that for a reason, to make you think and to get more "aha"
experiances.
So that you learn some by your self, else you're likly to just forget
about it as soon as you read it .... so play around with the code until
you understand it, and dont be afraid of the manual pages.
See how much of the code you can change etc.
-------------------------------------------------------------------------------
Here is a little code with the forking part alone:
//----------------------------------------------------------
#include "check_tmp.h"
/* taken from Stevens APUE */
int become_daemon(void){
pid_t pid;
if ( (pid = fork()) < 0)
return(-1);
else if (pid != 0)
exit(0); /* death to the parental unit */
setsid();
/* we're going to fork again for annoying SYSVr4 to insure that
we have no controlling terminal */
if ( (pid = fork()) < 0)
return(-1);
else if (pid != 0)
exit(0); /* death to the parental unit */
chdir("/");
umask(0);
return(0);
}
//----------------------------------------------------------
This code is taken litterly from another source, and I will not change or
explain it, look at it as a little test, of how well you know C by now ....
Figure out exectly what it's doing and why, use the manual pages.
(No it wont compile as it is, because there is no main function and the header
files are missing.)
-------------------------------------------------------------------------------
Actually there's not much more to say about forking and raw sockets at
this point ... there is ALOT more to sockets, but nothing you need to know
at this point since this is not a socket coding tutorial.
So off to the next section.
No comments:
Post a Comment