programmers resources
  http://www.intel-assembler.it/  (c)2017 intel-assembler.it   info@intel-assembler.it
 
Search :  
Lingua Italiana    English Language   
Index
 
just an empty assembly space
just an arrow Intel Platform
just an arrow Article & Guides
just an arrow Download Software


23/01/2009 Featured Article: How to remove Buzus Virus (permalink)




:::3095286:::
Bottone Scambio Directory Pubblicitaonline.it
Home Page | Articles & Guides | Download | Intel Platform | Contacts

Google
 


Bookmark and Share
Download 
Tell a friend



An Introduction to Socket Programming in C

Fundamental concepts for Unix TCP/IP applications

(by reg quinton - university of west.ontario)

E' una guida C che sintetizza: i servizi,netstat,hostname ed ip,strutture C,porte,socket ed fd,client connect,buffers,server accept ed argomenti correlati.
This article is online from 4280 days and has been seen 2214 times


An Introduction
to
Socket Programming

Last Edit
March 23, 1992

Reg Quinton <reggers@julian.uwo.ca>
Computing and Communications Services
The University of Western Ontario
London, Ontario N6A 5B7
Canada

1.Socket Programming

This course is directed at Unix application programmers who
want to develop client/server applications in the TCP/IP
domain.

Fundamental concepts are covered including network address-
ing, well known services, sockets and ports. Sample appli-
cations are examined with a view to developing similar
applications that serve other contexts.

This course requires an understanding of C programming and
an appreciation of the programming environment (ie. compil-
ers, loaders, libraries, Makefiles and the RCS revision con-
trol system).

BEWARE: If C code scares you, then you'll get some con-
cepts but you might be in the wrong course.

Our example is the UWO whois(l) service -- client and server
sources available in:

julian:~ftp/pub/unix/networking/rwhois

2. Existing Services

On a Unix machine there are usually lots of TCP/IP services
installed and running (tons on julian!).

[1:17pm julian] netstat -a
Active Internet connections (including servers)
Proto Recv-Q Send-Q Local Address Foreign Address (state)
tcp 0 0 julian.2717 vnet.ibm.com.smtp ESTABLISHED
tcp 0 0 julian.smtp uacsc2.albany.ed.55049 TIME_WAIT
tcp 0 13 julian.nntp watserv1.waterlo.3507 ESTABLISHED
tcp 0 0 julian.nntp gleep.csd.uwo.ca.3413 ESTABLISHED
tcp 0 0 julian.telnet uwonet-server2.c.55316 ESTABLISHED
tcp 0 0 julian.login no8sun.csd.uwo.c.1023 ESTABLISHED
tcp 0 0 julian.2634 Xstn15.gaul.csd..6000 ESTABLISHED
etc...
tcp 0 0 *.printer *.* LISTEN
tcp 0 0 *.smtp *.* LISTEN
tcp 0 0 *.waisj *.* LISTEN
tcp 0 0 *.account *.* LISTEN
tcp 0 0 *.whois *.* LISTEN
tcp 0 0 *.nntp *.* LISTEN
etc...
udp 0 0 *.ntp *.*
udp 0 0 *.syslog *.*
udp 0 0 *.xdmcp *.*

2.1. Netstat Observations

Inter Process Communication, IPC, is between host.port pairs
(or host.service). A process pair uses the connection --
client and server applications.

Two protocols on IP -- TCP (Transmission Control Protocol)
and UDP (User Datagram Prototocol). We'll be looking in more
detail at TCP services and will not look at UDP at all.

TCP services are connection orientated (like a stream, pipe
or tty) while UDP services are connectionless (more like
telegrams or letters).

We recognize many of the services -- SMTP (Simple Mail
Transfer Protocol, E-mail), NNTP (Network News Transfer Pro-
tocol service, Usenet News), NTP (Network Time Protocol),
and SYSLOG is the BSD service implemented by /etc/syslogd.

The netstat display shows many TCP services as ESTABLISHED
(there is a connection between client.port and server.port)
and others in a LISTEN state (a server application is lis-
tening at a port for client connections).

3. Host names and IP numbers

Hosts have names (eg. julian.uwo.ca) but IP addressing is by
number (eg. [129.100.2.12]). In the old days name/number
translations were tabled in /etc/hosts.

[10:25am suncon] page /etc/hosts
# Sun Host Database
#
127.0.0.1 localhost
129.100.1.75 suncon.ccs.uwo.ca

These days name to number translations are implemented by
the Domain Name Service (or DNS) -- see named(8).

[10:26am suncon] page /etc/resolv.conf
nameserver 129.100.2.12
nameserver 129.100.7.100
nameserver 129.100.2.13
domain ccs.uwo.ca
[10:26am suncon] nslookup whohost
Server: julian.uwo.ca
Address: 129.100.2.12

Name: julian.uwo.ca
Address: 129.100.2.12
Aliases: whohost.uwo.ca

3.1. Programming Calls

Programmers don't scan /etc/hosts nor do they communicate
with the DNS. The C library routines gethostbyname(3) and
gethostbyaddr(3) each return a pointer to an object with the
following structure:

struct hostent {
char *h_name; /* official name of host */
char **h_aliases; /* alias list */
int h_addrtype; /* host address type */
int h_length; /* length of address */
char **h_addr_list; /* list of addresses */
};
#define h_addr h_addr_list[0]
/* backward compatibility */

The structure h_addr_list is a list of IP numbers (recall
that a machine might have several interfaces, each will have
a number).

Good programmers would try to connect to each address listed
in turn (eg. some versions of ftp). Lazy programmers (like
me) just use h_addr -- the first address listed.

Client applications connect to a host.port (cf. netstat out-
put) for a service.

Proto Recv-Q Send-Q Local Address Foreign Address (state)
tcp 0 0 julian.2717 vnet.ibm.com.smtp ESTABLISHED
tcp 0 13 julian.nntp watserv1.waterlo.3507 ESTABLISHED

The connection is usually prefaced by translating a hostname
name into an IP number (but if you knew the IP number you
could carefully skip that step).

int tcpopen(host,service)
char *service, *host;
{
struct hostent *hp;
etc...
if ((hp=gethostbyname(host)) == NULL)
error...

Carefully because the IP address is a structure of 4 octets.
Watch out for byte ordering.

An unsigned long isn't the same octet sequence on all
machines. See htonl(3) and ntohl(3) for host to net conver-
sions.

4. Services and Ports

Services have names (eg. smtp the Simple Mail Transfer Pro-
tocol). Ports have numbers (eg. smtp is a service on port
25). The mapping from service names to port numbers is
listed in /etc/services.

[1:22pm julian] page /etc/services
# $Author: reggers $
# $Date: 1992/02/13 15:58:44 $
#
# Network services, Internet style
etc...
ftp 21/tcp
telnet 23/tcp
smtp 25/tcp mail
whois 43/tcp nicname
domain 53/tcp nameserver
domain 53/udp nameserver
tftp 69/udp
finger 79/tcp
nntp 119/tcp readnews untp
ntp 123/udp
snmp 161/udp
xdmcp 177/udp xdm
etc...

4.1. Programming Calls

The C library routines getservbyname(3), and getservby-
port(3) each return a pointer to an object with the follow-
ing structure containing the broken-out fields of a line in
/etc/services.

struct servent {
char *s_name; /* official name of service */
char **s_aliases; /* alias list */
int s_port; /* port service resides at */
char *s_proto; /* protocol to use */
};

Client applications connect to a service port. Usually this
is prefaced by translating a service name (eg. smtp) into
the port number (but if you knew the port number you could
carefully skip that step).

int tcpopen(host,service)
char *service, *host;
{
struct servent *sp;
etc...
if ((sp=getservbyname(service,"tcp")) == NULL)
error...

5. Socket Addressing

A Socket Address is a host.port pair (communication is
between host.port pairs). The structure is sockaddr_in, the
address family is AF_INET:

int tcpopen(host,service)
char *service, *host;
{ int unit;
struct sockaddr_in sin;
struct servent *sp;
struct hostent *hp;
etc...
if ((sp=getservbyname(service,"tcp")) == NULL)
error...
if ((hp=gethostbyname(host)) == NULL)
error...
bzero((char *)&sin, sizeof(sin));
sin.sin_family=AF_INET;
bcopy(hp->h_addr,(char *)&sin.sin_addr,hp->h_length);
sin.sin_port=sp->s_port;
etc...

The code is filling in the port and host address in the
Socket Address structure -- the address of the remote
host.port where we want to connect.

There's a generic Socket Address structure, a sockaddr, used
for communication in arbitrary domains.

/* from: /usr/include/sys/socket.h */

struct sockaddr {
u_short sa_family; /* address family */
char sa_data[14]; /* up to 14 bytes of direct address */
};

The sockaddr_in structure is for Internet Socket Addresses.
An instance of the generic socket address.

/* from: /usr/include/netinet/in.h */

struct sockaddr_in {
short sin_family; /* AF_INET */
u_short sin_port; /* service port */
struct in_addr sin_addr; /* host number */
char sin_zero[8];
};

The family defines the interpretation of the data. In other
domains addressing will be different -- services in the UNIX
domain are names (eg. /dev/printer).

6. File Descriptors and Sockets

6.1. File Descriptors

File Descriptors are the fundamental I/O object. You read(2)
and write(2) to file descriptors.

int cc, fd, nbytes;
char *buf;

cc = read(fd, buf, nbytes);
cc = write(fd, buf, nbytes)

The read attempts to read nbytes of data from the object
referenced by the file descriptor fd into the buffer pointed to
by buf. The write does a write to the file descriptor
from the buffer. Unix I/O is a byte stream.

File descriptors are numbers used for I/O. Usually the
result of open(2) and creat(2) calls.

All Unix applications run with stdin as file descriptor 0,
stdout as 1, and stderr as 3. But stdin is a FILE (see
stdio(3)) not a file descriptor. If you want a stdio stream
on a file descriptor use fdopen(3).

6.2. Sockets

A Socket is a Unix file descriptor created by the socket(2)
call -- you don't open(2) or creat(2) a socket. Cf. pipe(2)
creates file descriptors.

int s, domain, type, protocol;
s = socket(domain, type, protocol)
etc...
cc = read(s, buf, nbytes);

The domain parameter specifies a communications domain (or
address family). For IP use AFINET.

The type parameter specifies the semantics of communication.
For TCP/IP use SOCKSTREAM (for UDP/IP use SOCKDGRAM).

A SOCKSTREAM is a sequenced, reliable, two-way connection
based byte stream. If a data cannot be successfully trans-
mitted within a reasonable length of time the connection is
considered broken and I/O calls will indicate an error.

The protocol specifies a particular protocol to be used with
the socket -- for TCP/IP use 0. But see /etc/protocols to
get really confused.

7. Client Connect

A client application creates a socket(2) and connect(2) to a
service.

int tcpopen(host,service)
char *service, *host;
{ int unit;
struct sockaddr_in sin;
struct servent *sp;
struct hostent *hp;

if ((sp=getservbyname(service,"tcp")) == NULL)
error...
if ((hp=gethostbyname(host)) == NULL)
error...
bzero((char *)&sin, sizeof(sin))
etc...
if ((unit=socket(AF_INET,SOCK_STREAM,0)) < 0)
error...
if (connect(unit,&sin,sizeof(sin)) < 0)
error...
return(unit);
}

The result returned is a file descriptor.

7.1. Client Communication

Having connected a socket to a server to establish a file
descriptor communication is with the usual Unix I/O calls.

Many programmers turn file descriptors into stdio(3) streams
so they can use fputs, fgets, fprintf, etc. -- use
fdopen(3).

main(argc,argv)
int argc;
char *argv[];
{
int unit,i;
char buf[BUFSIZ];
FILE *sockin,*sockout;

if ((unit=tcpopen(WHOHOST,WHOPORT)) < 0)
error...
sockin=fdopen(unit,"r");
sockout=fdopen(unit,"w");
etc...
fprintf(sockout,"WHOIS %s\n",argv[i]);
etc...
while (fgets(buf,BUFSIZ,sockin)) etc...

7.2. Stdio Buffers

Stdio streams have powerful manipulation tools (eg. fscanf
is amazing). But beware, streams are buffered!

This means a well placed fflush(3) is often required to
flush a buffer to the peer.

fprintf(sockout,"WHOIS %s\n",argv[i]);
fflush(sockout);

while (fgets(buf,BUFSIZ,sockin)) etc...

Many client/server protocols are client driven -- the client
sends a command and expects an answer.

The server won't see the command if the client doesn't flush
the output. Likewise, the client won't see the answer if the
server doesn't flush it's output.

Watch out for client and server blocking -- both waiting for
input from the other.

8. Server Applications

A system offers a service by having an application running
that is listening at the service port for a connection. If
there is no application listening at the service port then
the machine doesn't offer that service.

The SMTP service is provided by an application listening on
port 25. On Unix systems this is usually the sendmail appli-
cation which is started at boot time.

[2:20pm julian] ps -agx | grep sendmail
419 ? SW 0:03 /usr/lib/sendmail -bd -q15m
18438 ? IW 0:01 /usr/lib/sendmail -bd -q15m

[2:28pm julian] netstat -a | grep smtp
tcp 0 0 julian.3155 acad3.alaska.edu.smtp SYN_SENT
tcp 0 0 *.smtp *.* LISTEN

In the example we have a process listening to the smtp port
(for inbound mail) and another process talking to the smtp
port on acad3.alaska.edu (ie. sending mail to that system).

8.1. Server Bind

A Server uses bind(2) to establish the local host.port
assignment. Only required for servers -- applications which
accept(2) connections from a host.port.

struct servent *sp;
struct sockaddr_in sin;

if ((sp=getservbyname(service,"tcp")) == NULL)
error...
sin.sin_family=AF_INET;
sin.sin_port=sp->s_port;
sin.sin_addr.s_addr=htonl(INADDR_ANY);

if ((s=socket(AF_INET,SOCK_STREAM,0)) < 0)
error...
if (bind(s, &sin, sizeof(sin)) < 0)
error...

htonl converts a long to the right sequence (given different
byte ordering on different machines). The IP address
INADDR_ANY means all interfaces.

Client applications usually aren't concerned about the local
host.port assignment (the connect(2) does a bind for the
local address). But rcp, rlogin, etc. do connect from
reserved port numbers.

8.2. Listen and Accept

To accept connections, a socket is first created with
socket(2), a queue for incoming connections is specified
with listen(2) and then the connections are accepted
with accept(2).

struct servent *sp;
struct sockaddr_in sin,from;

if ((sp=getservbyname(service,"tcp")) == NULL) error...
sin.sin_family=etc...
if ((s=socket(AF_INET,SOCK_STREAM,0)) < 0) error...
if (bind(s, &sin, sizeof(sin)) < 0) error...

if (listen(s,QUELEN) < 0) error...
for (;;) {
if ((g=accept(f,&from,&len)) < 0) error...
if (!fork()) {
child handles request...
exit(0);
}
close(g);
}

This is the programming schema used by utilities like send-
mail and lpd -- they create their socket and listen for con-
nections.

9. Inetd Services

Not all services are started at boot time by running a
server application. Eg. you won't usually see a process
running for the finger service like you do for the smtp ser-
vice. Many are handled by the InterNet Daemon inetd(8). This
is a generic service configured by the file /etc/inetd.conf.

[2:35pm julian] page /etc/inetd.conf
# $Author: reggers $
# $Date: 1992/02/13 15:58:44 $
#
# Internet server configuration database
ftp stream tcp nowait root /usr/etc/ftpd ftpd
telnet stream tcp nowait root /usr/etc/telnetd telnetd
shell stream tcp nowait root /usr/etc/rshd rshd
login stream tcp nowait root /usr/etc/rlogind rlogind
exec stream tcp nowait root /usr/etc/rexecd rexecd
uucpd stream tcp nowait root /usr/etc/uucpd uucpd
finger stream tcp nowait nobody /usr/etc/fingerd fingerd
etc...
nntp stream tcp nowait root /usr/lib/newsbin/nntpd nntpd
whois stream tcp nowait nobody /usr/ccs/lib/directory/rwhoisd rwhoisd
tn3270 stream tcp nowait nobody /usr/ccs/bin/tn3270 tn3270
account stream tcp nowait nobody /usr/ccs/bin/accountd accountd

9.1. Inetd Comments

For each service listed in /etc/inetd.conf the inetd process
(this process is started at boot time) executes the socket,
bind, listen and accept calls as discussed above. Inetd also
handles many of the daemon issues (signal handling, set pro-
cess group and controlling tty).

Inetd spawns the appropriate application (with fork(2) and
exec(2)) when a client connects.

The application is started with stdin and stdout connected
to the remote port. Any input/output on stdin/stdout are
sent/received by the client.

This means, any application written to use stdin/stdout can
be a server application. Writing a server application
should be fairly simple.

9.2. Whois Daemon

On julian we have an entry in /etc/inetd.conf for the UWO
whois service:

[3:25pm julian] grep whois /etc/inetd.conf
whois stream tcp nowait nobody -
/usr/ccs/lib/directory/rwhoisd rwhoisd

This is the UWO whois service (as listed in /etc/services),
on a TCP/IP stream, ran as user nobody, the program to run
is listed, and the command line to the program.

Note that this is not the standard /usr/ucb/whois service
that talks to nic.ddn.mil. The UWO whois service talks to a
different server and implements a different protocol.

The program conducts a protocol on stdin/stdout (which is
usually connected by a TCP/IP socket to a client applica-
tion).

10. Running the Daemon

You can run the whois daemon (on the server) to see what it
does:

[3:27pm julian] /usr/ccs/lib/directory/rwhoisd
220 Directory Service $Revision: 1.1 $ ready at julian.uwo.ca
help .... my command
350 I don't know much but I can understand
HELP, QUIT, WHOIS
.
whois quinton .... my command
350 Matches on quinton follow:
Reg.Quinton:
Reg Quinton <reggers@julian.uwo.ca>
CCS,programmer,NSC,214,661-2151,6026,#11930

reggers:
Reg Quinton <reggers@julian.uwo.ca>
CCS,programmer,NSC,214,661-2151,6026,#11930

.
quit .... my command
220 Quit accepted, terminating session
[3:30pm julian]

The program is command driven -- you give commands on stdin,
it produces results on stdout.

10.1. The Code

The program is easy enough: read a line, switch on command
and do command.

printf("220 Directory Service %s ready at %s\n", VERSION, name);
fflush(stdout);

while (fgets(string,BUFSIZ,stdin))
{ if (isprefix(string,"HELP")) printf(HELPMSG);
else if (isprefix(string,"QUIT"))
{ printf("220 Quit accepted, terminating session\n");
fflush(stdout); sleep(3); exit(0);
}
else if (isprefix(string,"WHOIS "))
{ sscanf(string,"%*s%*[ ]%[^ \r\n]",name);
printf("350 Matches on %s follow:\n",name);
fflush(stdout);
sprintf(string,"%s '%s'",GREP,name);
system(string); printf(".\n");
}
else printf("550 command makes no sense\n");
fflush(stdout);
}
printf("550 Oops... you've stopped talking\n");

The protocol is line based. This works well with stdio
streams. Also easy to test from a terminal. Compare with
line based protocols for NNTP and SMTP.

10.2. Connecting to the Server

You can make a telnet(1) connection to the server:

[3:47pm suncon] grep whois /etc/services
whois 43/tcp nicname
[3:47pm suncon] telnet julian 43
Trying 129.100.2.12 ... Connected to julian.uwo.ca.
Escape character is '^]'.
220 Directory Service $Revision: 1.1 $ ready at julian.uwo.ca
help .... my command
350 I don't know much but I can understand
HELP, QUIT, WHOIS
.
whois quinton .... my command
350 Matches on quinton follow:
Reg.Quinton:
Reg Quinton <reggers@julian.uwo.ca>
CCS,programmer,NSC,214,661-2151,6026,#11930

reggers:
Reg Quinton <reggers@julian.uwo.ca>
CCS,programmer,NSC,214,661-2151,6026,#11930

.
quit .... my command
220 Quit accepted, terminating session
Connection closed by foreign host.
[3:48pm suncon]

10.3. Whois Client

The whois client makes a TCP/IP connection to the server and
conducts the kind of protocol you would type if you where to
make a connection by hand:

[7:30am zebra] whois quinton
Reg.Quinton:
Reg Quinton <reggers@julian.uwo.ca>
CCS,programmer,NSC,214,661-2151,6026,#11930

reggers:
Reg Quinton <reggers@julian.uwo.ca>
CCS,programmer,NSC,214,661-2151,6026,#11930

[7:30am zebra]

The client sends the command "WHOIS quinton", the server
sends back the answer and the client displays the answer to
the user. When finished the client sends "QUIT".

The server response codes assist in the parsing of the
results.

The client code is complicated (a bit) by the piping through
a pager.

11. Final Comments

The whois example uses a line based protocol. The strategy
is common but by no means universal. For example, the lpd
protocols use octets (ie. single characters) for the com-
mands.

Inetd servers are the simplest to implement. However, this
may not be optimal. Especially if the server has to do a lot
of work first (eg. loading in a big data base).

Stand alone servers have to deal with many daemon issues --
they should ignore most signals, set a unique process group
and get rid of the controlling terminal.

Daemons like nntp could (in theory) handle many clients from
a single daemon using interrupt driven I/O. As currently
implemented we have an nntp daemon for each client.

You'll note that Socket programmers use alarm(3), setjmp(3),
and signal(3) calls. The intent is to prevent a process
(client or server) from hanging in a wait for I/O state.

11.1. Note Well

The best way to code a client/server program is to reuse
code from an existing service. There's lots of public domain
examples to work from -- nntp, lpd, sendmail, and even our
whois service.

A simple solution that works is much better than a fancy
solution that doesn't -- KISS. Protocols have to be simple!

Presentation issues, ie. the display for the user, should
not effect the protocol or server. Again, protocols have to
be simple!

Don't ever assume the client or server applications are well
behaved!

12. Suggested Reading

Introductory 4.3BSD Interprocess Communication, by Stuart
Sechrest, (in) UNIX Programmer's Supplementary Documents,
Vol1, 4.3 Berkeley Software Distribution, PS1:7.

Advanced 4.3BSD Interprocess Communication, by Samuel J.
Leffler et al, (in) UNIX Programmer's Supplementary Docu-
ments, Vol1, 4.3 Berkeley Software Distribution, PS1:8.

Introduction to the Internet Protocols, Computer Science
Facilities Group, Rutgers. (on ~ftp/nic).

Networking with BSD-style Sockets, by John Romkey, (in) Unix
World, July-Aug. 1989.

How to Write Unix Daemons, by Dave Lennert, (in) Unix World,
Dec. 1988.

A Socket-Based Interprocess Communications Tutorial, Chpt.
10 of SunOS Network Programming Guide.

An Advanced Socket-Based Interprocess Communications Tuto-
rial, Chpt. 11 of SunOS Network Programming Guide.


Top
Download 
Tell a friend
Bookmark and Share


 Tags: socket


webmaster jes
writers rguru, tech-g, aiguru, drAx

site optimized for IE/Firefox/Chrome with 1024x768 resolution

Valid HTML 4.01 Transitional


ALL TRADEMARKS ® ARE PROPERTY OF LEGITTIMATE OWNERS.
© ALL RIGHTS RESERVED.

hosting&web - www.accademia3.it

grossocactus
find rguru on
http://www.twitter.com/sicurezza3/
... send an email ...
Your name

Destination email

Message

captcha! Code