Unix, C, and C++
Function Reference
Internet
See notes on: General Scheme of Internet Communications
Port Numbers
IP Numbers
Byte Order
Finding IP addresses
and samples: Sample Internet (IP+TCP) Server Program
Sample Internet (IP+TCP) Client Program
Sample IP+UDP Communication Program
Warning: When a function's description says include <sys/types.h>
then <sys/socket.h> it is important that the #includes appear in
that order in your program. The file sys/socket.h makes use of definitions
that appear in sys/types.h but is too stupid to include it for itself.
Putting those two includes in the wrong order will cause a large number of
meaningless error messages from the compiler.
int gethostname(char * name, int maxlen)
Purpose: Find the computer's name.
include: <unistd.h>
The name of the computer (e.g. rabbit.eng.miami.edu) is stored in
the string name, up to a maximum of maxlen characters.
returns <0 for error, 0 for success.
int gethostid(void)
include: <unistd.h>
returns An integer that is supposed to uniquely identify the
computer, usually its IP number
note: valid results can be negative.
WARNING: This function can not be relied upon; it relies on optional system set-up
procedures that are often not performed. On many systems it will return zero.
For reliable results, use gethostname then gethostbyname instead
hostent * gethostbyname(char * name)
Purpose: Find any computer's IP address
include: <netdb.h>
name = computer name, such as "rabbit", "rabbit.eng.miami.edu", or "129.171.33.6"
returns NULL in case of error (use herror, not perror, etc to display), or
a pointer to a hostent struct. The fields of a hostent are:
char * h_name the official name
char * * h_aliases list of alternate names, NULL at end
int h_addr_type what kind of address: expect AF_INET
int h_length bytes in each address: expect 4
char * h_addr primary address - not a string
char * * h_addr_list all addresses, not strings, NULL at end
usually h_addr is the only interesting field. It is not a string, but a list
of bytes giving the address, e.g. { 129, 171, 33, 6 }
h_addr, and all h_addr_list[i], are usually type-cast to (in_addr *) before use.
If the input string, name, is in numeric form, that numeric form is returned as
h_name, h_addr, and h_addr_list[0]; no aliases are provided, no 'reverse
translation' is performed.
IP addresses are provided in Network Byte Order, so ntohl may be required.
See Simple Sample Program, Full Sample Program
int socket(int domain, int type, int protocol)
Purpose: Create a file-like object capable of internet communications
include: <sys/types.h> then <sys/socket.h>
domain = AF_INET if you want to use IP, there are many others.
type = SOCK_STREAM if you want TCP, or SOCK_DGRAM if you want UDP, there are a few others
protocol = 0, except in almost unheard-of circumstances
returns <0 for error, or an integer file decriptor exactly as returned by open.
Note using UDP the socket is now ready for use with send and recvfrom;
using TCP a connection to another computer must first be made with connect or accept
See Sample Server Program or Sample Client Program
int bind(int socket, sockaddr * description, int size)
include: <sys/types.h> then <sys/socket.h>
socket = a socket as created by socket
description = a pointer to a sockaddr_in, but typecast as a pointer to a sockaddr.
size = the size in bytes of a sockaddr_in
returns <0 for error, positive otherwise
Purpose: (for servers only) After a socket has been created, and a sockaddr_in has
been set up to describe the service to be establised, this sets up the socket to be a
server socket on the appropriate port.
See Sample Server Program
int listen(int socket, int length)
include: <sys/types.h> then <sys/socket.h>
socket = a socket as created by socket and set up with bind as a server port
length = queue length for pending connections, typically 3
returns <0 for error, positive otherwise
Purpose: (for servers only) After a socket has been created and bound to a port, this
sets up a queue of pending client connections so that it is ready to listen for them.
See Sample Server Program
int accept(int socket, sockaddr * description, unsinged int * size)
include: <sys/types.h> then <sys/socket.h>
socket = a socket as created by socket and set up with bind and listen
description = a pointer to an uninitialised sockaddr_in, typecast as a pointer to a sockaddr.
length = the address of an unsigned int variable
returns <0 for error, otherwise an int describing a file exactly as returned by open
on which read and write operations directly to the client may be performed.
Purpose: (for servers only) After a socket has been created, bound to a port, and
listened on, this waits for a client to request a connection.
See Sample Server Program
int connect(int socket, sockaddr * description, int size)
include: <sys/types.h> then <sys/socket.h>
socket = a socket as created by socket
description = a pointer to a sockaddr_in, but typecast as a pointer to a sockaddr.
size = the size in bytes of a sockaddr_in
returns <0 for error, otherwise an int describing a file exactly as returned by open
on which read and write operations directly to the server may be performed.
Purpose: (for clients only) After a socket has been created, and a sockaddr_in has
been set up to describe the server to be connected to, this attempts to make
the connection
See Sample Client Program
struct sockaddr
{ char sa_len; // total size of the struct in bytes
char sa_family; // usually AF_INET
char sa_data[LENGTH]; }; // just a place-holder
include: <sys/socket.h>
Purpose: the functions bind and connect use a sockaddr to specify the kind of connection wanted.
Actually, programs never create a sockaddr, but instead make a sockaddr_in which contains
all the information that is needed, and typecast as a sockaddr before passing to the function.
See Sample Server Program or Sample Client Program
struct sockaddr_in
{ char sin_len; // total size of the struct in bytes
char sin_family; // usually AF_INET
short int sin_port; // port number, in network format, use htons
in_addr sin_addr; // for clients: IP address of server in network format, use htonl
// for servers: usually htonl(INADDR_ANY) to accept connections from anywhere.
char sin_zero[8]; }; // unused.
include: <netinet/in.h>
See Sample Server Program or Sample Client Program
struct in_addr
{ int s_addr; }; // the IP address, network format, use htonl
include: <netinet/in.h>
Purpose: Used to represent an IP address in a sockaddr_in. An absolute folly, as it is exactly
equivalent to an int.
int sendto(int socket, char *data, int numbytes, int flags, sockaddr *destination, int dstsize)
include: <sys/types.h> then <sys/socket.h>
Purpose: Send data. This function is for UDP sockets, not TCP ones.
socket = a socket as created by socket
data = pointer to data to be transmitted (can be any type)
numbytes = number of bytes to be transmitted
flags = normally 0, but can be MSG_EOR to indicate that this is a complete transmission
(like "over" on a walkie-talkie), or MSG_EOF (like "out").
destination = a pointer to a sockaddr_in, but typecast as a pointer to a sockaddr.
dstsize = the size in bytes of a sockaddr_in
returns <0 for error, otherwise the number of bytes successfully sent.
See Sample UDP Program
int recvfrom(int socket, char *data, int maxnumbytes, int flags, sockaddr *source, int srcsize)
include: <sys/types.h> then <sys/socket.h>
Purpose: wait for and receive data. This function is for UDP sockets, not TCP ones.
socket = a socket as created by socket
data = pointer to memory to receive data (can be any type)
max numbytes = maximum number of bytes to be accepted in a single read
flags = normally 0, but can be MSG_PEEK to look at a message while still leaving it
in the buffer so that it can be read again later.
source = a pointer to a sockaddr_in, but typecast as a pointer to a sockaddr.
The IP address and port number of the sender will be put in here.
May be NULL if you don't care who the sender is.
srcsize = the size in bytes of a sockaddr_in
returns <0 for error, otherwise the number of bytes actually received.
See Sample UDP Program
char * inet_ntoa(in_addr a)
include: <netinet/in.h> and <arpa/inet.h>
Purpose: Convert an IP address in the internal format as retrieved by gethostbyname
(i.e. a big-endian byte list) into a string suitable for printing.
Result format example: "129.171.33.6"
See Sample Program for use
int htonl(int ip)
include: <sys/param.h>
Purpose: converts an IP address in the computers integer format to the standard network format
note: valid results can be negative.
See Sample Program for use
int ntohl(int ip)
include: <sys/param.h>
Purpose: converts an IP address in the standard network format to the computers integer format
note: valid results can be negative.
See Sample Program for use
int htons(int pn)
include: <sys/param.h>
Purpose: converts a port number in the computers integer format to the standard network format
int ntohs(int pn)
include: <sys/param.h>
Purpose: converts a port number in the standard network format to the computers integer format
General Scheme of Internet Communications
First make the choice of protocol: TCP or UDP:
-
TCP Establishes a virtual connection between two communicating systems, A and B. A connection
must be made between A and B before they can communicate in any way. Once the connection has been
made, only messages sent by A will be received by B, and only messages sent by B will be received
by A; it is a private channel. TCP ensures that no message goes undelivered; it informs both
systems if the connection is lost, and informs one system as soon as the other closes the connection.
Because of all this, the TCP protocol is fairly heavy-weight: a lot of network traffic is generated
for very small messages. TCP applications need one port for each remote system they are communicating with.
TCP tends to enforce a client-server setup.
-
UDP is a very light-weight protocol, generating very little traffic beyond that absolutely
required to transmit the messages. It does not establish connections. Once a message has been
transmitted, there is no follow up; if it is not received, or even if the intended recipient does
not exist, you will not be informed. UDP applications need only one port; with it they can transmit
to and receive from any other UDP application. UDP applications must implement their own message
confirmation protocol; it is the nature of the internet that sent messages often do not arrive;
TCP handles this automatically, invisibly resending any lost messages. UDP applications tend
towards a peer-to-peer setup.
If TCP is chosen, of the communicating applications one must act as a server and the other as a client.
The server sets up a port and listens to it for incoming connections. The client must know the
server's port number; once the server is running, the client makes a connection to it, then
two-way communications can begin.
Stages for a TCP server (see Server Sample Program):
- Create an Internet Stream socket
- Create a sockaddr_in object stating the port number and the kind of connections to be accepted
- Bind the socket to its port
- Tell the socket to listen for incoming connections
- Wait for and accept an incoming connection request. The result of accept is
a unix-level file descriptor; reads and writes on it are direct communications
with the client
- Optionally fork a new subprocess to deal with this client. This leaves the original
process free to immediately accept another client connection by looping back to the accept step
- Optionally create a C FILE* to allow higher-level i/o functions
to be used
- Communicate with the client
- Close the connection to the client
- If communications are being carried out by a subprocess, then exit to terminate it
- If further connections are to be accepted, loop back to the accept step
- To terminate the server, close the original socket
Stages for a TCP client (see Client Sample Program):
- Look up the server's IP address
- Create an Internet Stream socket
- Create a sockaddr_in object stating the server's IP address and port number
- Connect the socket to the server
- The socket behaves as
a unix-level file descriptor; reads and writes on it are direct communications
with the server
- Optionally create a C FILE* to allow higher-level i/o functions
to be used
- Communicate with the server
- Close the socket to sever the connection.
If UDP is chosen, both communicating applications
behave in the same way. In fact it is common to sue exactly the same program running in exactly
the same way for both sides of the communications.
Each sets itself up on a particular port; it can use that port to transmit to any UDP application
anywhere, and it can receive data from any UDP application on that same port.
Stages for a UDP application (see UDP Sample Program):
- Create an Internet Datagram socket
- Create a sockaddr_in object stating the port number that should be listened to, and the kind of connections to be accepted
- Bind the socket to its port
- Either wait for an incoming message or send an outgoing message, repeatedly.
To send an outgoing message:
- Look up the IP address of the other system
- Create a object stating the IP address and port number for the remote application
- Use sendto to send the message
- To wait for and receive and incoming message:
- Create an empty sockaddr_in object to receive the IP and port numbers of the sender
- Provide an array (or other area of memory) to receive data into
- Call recvfrom
- To terminate communications, close the socket
UDP ports and TCP ports are not the same things. A program can be using TCP port number 1234, and another
program can be using UDP port number 1234 on the same computer at the same time without any problems.
Port Numbers
There is no convenient human-understandable
way to refer to internet-enabled applications running on a computer like there is for files.
Files have names that can be very descriptive, and have protections and a record of ownership
so they can not be subverted for illicit uses. There is nothing like that for internet
connections.
A program wishing to receive data over the network (usually a server) must attach itself to
an internet port. Other programs wishing to communicate with it must know which port it is
attached to. Ports are referred to solely by 16-bit numbers (range 1 to 65535, or 1 to 32767
on some computers). If a program is to communicate with another over the internet, it must
know two things: the IP address, (which at least has a memorable human-oriented form, like
rabbit.eng.miami.edu) and the port (which is just a meaningless number).
To prevent unconquerable confusion, well-known services always attach themselves to
particular well-known port numbers (e.g. web servers use port 80, the mail server uses
port 25, and ftp uses port 21). For new or non-famous programs, there are of course
no well-known port numbers, and the port number must be treated as part of the address;
it is just as vital as the IP address. Common practice is to attach the port number
to the IP address with a colon, for example 129.171.33.6:1234 or rabbit.eng.miami.edu:1234.
There is a significant security problem too. At any time that the mail server is not
running, another program could attach itself to port 25, and it would receive all
incoming mail destined for that computer. The same goes for port 80: another program
could pretend to be the web server and provide false information (even falser than
the information normally found on the web, that is). The only slight attempt to
fix this problem is that on some systems (unixes particularly), all port numbers
less than 1024 are accessible only by system (root) processes. So a normal user
can't take over one of the well-known port numbers. But there is nothing you can
do to protect the port number that your new or non-famous application has decided
to use. This is why all secure applications must use some form of encryption on
all data sent over the internet, even if they use TCP with "secure" sockets.
UDP and TCP ports are independent: one application can use TCP port 1234, and
another can use UDP port 1234 at the same time without problems. However,
well-known port numbers are usually kept consistent. For example, DNS runs
over both TCP and UDP, and uses both port 53s.
Well-Known Port Numbers from RFC923
Most computers do not run most of these services, some because they are not useful,
but many because they are believed to constitute security risks.
5 RJE Remote Job Entry
7 ECHO Echo
9 DISCARD Discard - Ignore all incoming data
11 USERS List Active Users
13 DAYTIME Respond with time of day
15 NETSTAT Who is up or NETSTAT
17 QUOTE Quote of the Day
19 CHARGEN Character Generator
20 FTP File Transfer [Default Data]
21 FTP File Transfer [Control]
23 TELNET Telnet
25 SMTP Simple Mail Transfer
27 NSW-FE NSW User System FE
29 MSG-ICP MSG ICP
31 MSG-AUTH MSG Authentication
35 any printer server
37 TIME Time
39 RLP Resource Location Protocol
41 GRAPHICS Graphics
42 NAMESERVER Host Name Server
43 NICNAME Who Is
44 MPM-FLAGS MPM FLAGS Protocol
45 MPM Message Processing Module [recv]
46 MPM MPM [default send]
47 NI-FTP NI FTP
49 LOGIN Login Host Protocol
51 LA-MAINT IMP Logical Address Maintenance
53 DOMAIN Domain Name Server
55 ISI-GL ISI Graphics Language
57 any private terminal access
59 any private file service
61 NI-MAIL NI MAIL
63 VIA-FTP VIA Systems - FTP
69 TFTP Trivial File Transfer
71 NETRJS Remote Job Service
72 NETRJS Remote Job Service
73 NETRJS Remote Job Service
74 NETRJS Remote Job Service
75 any private dial out service
77 any private RJE service
79 FINGER Finger
81 HOSTS2 Name Server
83 HOSTS2-NS MIT ML Device
85 MIT-ML-DEV MIT ML Device
87 MIT-ML-DEV Any Private Terminal Link
89 SU-MIT-TG SU/MIT Telnet Gateway
91 MIT-DOV MIT Dover Spooler
93 DCP Device Control Protocol
95 SUPDUP SUPDUP
97 SWIFT-RVF Swift Remote Vitural File Protocol
99 METAGRAM Metagram Relay
101 HOSTNAME NIC Host Name Server
105 CSNET-NS Mailbox Name Nameserver
107 RTELNET Remote Telnet Service
109 POP Post Office Protocol
111 SUNRPC SUN Remote Procedure Call
113 AUTH Authentication Service
115 SFTP Simple File Transfer Protocol
117 UUCP-PATH UUCP Path Service
243 SUR-MEAS Survey Measurement
245 LINK LINK
IP Numbers
IP addresses are
simply 32-bit numbers allocated by a central authority to
uniquely identify any internet-accessible computer. 32-bit
numbers allow for 4,294,967,296 different addresses (but due
to allocation inefficiencies there are far fewer than this to
go around), and recently it was decided that that is not enough
for the whole world. IP6 is on the horizon, under which
IP addresses will be 48-bit numbers, but for now 32 bits is
all we have to deal with.
Large numbers are hard for people to deal with accurately,
so just as in all sensibly regulated human affairs (miles, feet,
and inches, or stones, pounds, and ounces, or pounds, shillings,
and pence) IP numbers are split into reasonably-sized components.
The notation 129.171.33.6 really represents a base-256 number,
equal to 129*256^3 + 171*256^2 + 33*256 + 6.
The more human-oriented form of IP addresses, e.g. rabbit.eng.miami.edu
is directly translated into the corresponding IP number by a service
called DNS (Domain Name Service), which programs can access through
the gethostbyname system function.
The verbal form should be used whenever possible, as it is much less
likely to change than the numeric form.
The numeric form A.B.C.D is actually two numbers concatenated.
rabbit.eng.miami.edu refers to the host (i.e. computer) called
rabbit in the domain called eng.miami.edu. The corresponding
numeric form is 129.171.33.6, in which 129.171 represents
miami.edu, and 33.6 represents rabbit.eng. Large domains are
often split up into sub-domains (that is how 129.171 manages
to represent miami.edu rather then eng.miami.edu; eng.miami.edu
is a subdomain in miami.edu). sub-domains are not required to
be represented by a consistent part of the IP number: 129.171.33
is not the designation for eng.miami.edu.
The division between domain and host in an IP number is easy
to determine. Given the IP number A.B.C.D:
- If A is between 0 and 127 (inclusive), it is a "Class A" network.
A itself is the network or domain number, and B.C.D, interpreted
as B*256^2 + C*256 + D is the host number (or sub-domain and host).
- If A is between 128 and 191 (inclusive), it is a "Class B" network.
A.B is the network number, interpreted as (A-128)*256+B, and C*256+D is the host number.
- If A is between 192 and 223 (inclusive), it is a "Class C" network.
A.B.C is the network number, interpreted as (A-192)*256^2+B*256+c, and
D is the host number.
- If A is between 224 and 255 (inclusive), it is a "Class D" or
"Multicast" network. A.B.C.D is the network number, interpreted as
(A-224)*256^3 + B*256^2 + c*256 + D. Hosts within a class D network
do not have their own unique numbers; everything sent to one of them
is sent to all of them. As far as I am aware, there are no class D
networks.
- Generally, in A.B.C.D, none of A, B, C, or D may be either 0
or 255. 0 is used to represent "the same number as I've got here",
so on rabbit (129.171.33.6), 0.0.123.111 means 129.171.123.111.
255 is sued to represent a "wild card", like * in a filename, so
129.171.255.255 represents anything in thr miami.edu domain.
- The special address 127.0.0.1 always represents the "Local Host",
which means "this computer".
Byte Order
Different
computers store their integers in different ways. Some
(including Intel-based PCs and Vaxes) make the rational choice of
storing the least significant bytes first (at the lowest addresses),
which is called the Little Endian order. Some make the less
rational choice of storing the most significant byte first: Big
Endian, while a very few are completely irrational and follow
something like the firing order of a four cylinder car engine. The
following C++ program shows how byte-order can be seen:
#include <stdio.h>
#include <sys/param.h>
union converter
{ struct { unsigned char a, b, c, d; } b;
int i; };
void main(void)
{ converter c;
printf("&int=%u; &byte-a=%u; &byte-d=%u\n", &c.i, &c.b.a, &c.b.d);
c.i=0x11223344;
printf("%08X = %02x-%02X-%02X-%02X\n", c.i, c.b.a, c.b.b, c.b.c, c.b.d); }
The
union type called converter arranges for one int and four bytes
to share the same memory, so using a variable c of type converter,
storing an integer in c.i allows the individual bytes of that integer
to be seen in c.b.a, c.b.b, c.b.c, and c.b.d. The program first prints
the addresses of c.i, c.b.a, and c.b.d to confirm that the compiler has not
reordered things, and byte a is indeed the first byte and d is the last.
Then it stores a hexadecimal constant in c.i (hexadecimal makes the
individual bytes obvious), you can see that the most significant byte
is 0x11, and the least significant byte is 0x44. When this program is
run on an intel-86-based computer, you should see this output:
11223344 = 44-33-22-11, confirming that the least significant
byte is stored first.
Byte
order is rarely relevant unless two computers of different types
have to communicate, and that is what the internet is all about. Integers
that describe network addresses must be in a single consistent format
regardless of the kind of computer currently in use. The network format
for integers happens to be Big Endian. Programs that deal with IP addresses
have to be able to convert from one form to the other. The functions
htonl and ntohl convert 32-bit values (i.e. IP addresses);
htons and ntohs convert 16-bit values (i.e. port numbers).
In those four names, 'h' stands for 'host' (the computer you're using),
'to' is 'to', 'n' stands for 'network', 's' is for 'short', and 'l' is
for 'long', so htonl converts a 32-bit int from the computer's format
to the network format.
An extended version of
the above program shows the function htonl is action.
On most computers ntohl and htonl are indentical.
#include <stdio.h>
#include <sys/param.h>
union converter
{ struct { unsigned char a, b, c, d; } b;
int i; };
void main(void)
{ converter c;
printf("&int=%u; &byte-a=%u; &byte-d=%u\n", &c.i, &c.b.a, &c.b.d);
c.i=0x11223344;
printf("%08X = %02x-%02X-%02X-%02X\n", c.i, c.b.a, c.b.b, c.b.c, c.b.d);
c.i=htonl(c.i);
printf("htonl\n");
printf("%08X = %02x-%02X-%02X-%02X\n", c.i, c.b.a, c.b.b, c.b.c, c.b.d);
On a PC or Vax, the output
should be 11223344 = 44-33-22-11; htonl; 44332211 = 11-22-33-44.
On a Big-endian computer,
it should be 11223344 = 11-22-33-44; htonl; 11223344 = 11-22-33-44.
Of course, under IP6 when IP addresses are 6-byte values, everything
will become more complicated.
(The terms Big-Endian and Little Endian come from Jonathan Swift's 1726
satire Gulliver's Travels, in which the terms named the opposing
sides in a theological war. One side believed that boiled eggs should
be eaten little-side up, and the other side believed the opposite.)
gethostbyname, inet_ntoa: Simple Sample Program (C++)
This shows only the most useful aspects of gethostbyname; it shows
how to find the IP address (in a form suitable for making connections) associated with any
reasonable form of a computer's name, such as "rabbit.eng.miami.edu", "rabbit", or "129.171.33.6".
#include <stdio.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
void main(void)
{ hostent * record = gethostbyname("rabbit.eng.miami.edu");
if (record==NULL)
{ herror("gethostbyname failed");
exit(1); }
in_addr * address=(in_addr * )record->h_addr;
printf("IP Address: %s\n", inet_ntoa(* address)); }
gethostbyname: Full Sample Program (C++)
This shows everything that can be done with the result of gethostbyname. Most of
this is not very useful, a more realistic sample is given above.
#include <stdio.h>
#include <netdb.h>
#include <unistd.h> // only needed for definition of gethostname for part 1
#include <netinet/in.h> // only needed for section 8
#include <arpa/inet.h> // only needed for section 8
#include <sys/socket.h> // only needed for definition of AF_INET for part 5
#include <sys/param.h> // only needed for definition of MAXHOSTNAMELEN for part 1
void main(int argc, char *argv[])
{
// Part 1 (optional) get the name that is to be looked up
char hostname[MAXHOSTNAMELEN];
if (argc==2)
strcpy(hostname, argv[1]);
else
gethostname(hostname, MAXHOSTNAMELEN);
// Part 2 (required)
hostent * record = gethostbyname(hostname);
if (record==NULL)
{ herror("gethostbyname failed");
exit(1); }
// Part 3 (optional) The official name may not be the same as the parameter
printf("Official name: %s\n", record->h_name);
// Part 4 (optional) Some names have a list of alternative forms
for (int i=0; 1; i+=1)
{ if (record->h_aliases[i]==NULL) break;
printf("Alternate name: %s\n", record->h_aliases[i]); }
// Part 5 (optional) Make sure it really is an internet address
if (record->h_addrtype==AF_INET)
printf("Internet Address\n");
else
printf("Other (non-internet) Address\n");
// Part 6 (optional) Find out what size the address is (4 bytes), may be come 6 one day
int len=record->h_length;
printf("Address length = %d\n", record->h_length);
// Part 7 (optional) Illustrates the internal format of an IP address
char * addr=record->h_addr;
printf("Main Address: ");
printf("%d", (unsigned char)addr[0]);
for (int i=1; i<len; i+=1)
printf(".%d", (unsigned char)addr[i]);
printf("\n");
// Part 8 (useful)
in_addr * address=(in_addr * )record->h_addr;
printf("Main Address: %s\n", inet_ntoa(* address));
// Part 9 (optional) Illustrates obtaining IP as an int
int ip = ntohl(address->s_addr);
printf("Main Address: %d.%d.%d.%d\n", (ip>>24)&0xFF, (ip>>16)&0xFF, (ip>>8)&0xFF, ip&0xFF);
// Part 10 (optional) Print any secondary IP addresses
// note loop starts at 1 because h_addr = h_addr_list[0]
for (int i=1; 1; i+=1)
{ in_addr * address = (in_addr *)record->h_addr_list[i];
if (address==NULL) break;
printf("Address: %s\n", inet_ntoa(* address)); } }
Sample Internet (IP+TCP) Server Program (C++)
This server waits for a client to connect to it, then sends a single "hello..." line to
the client, and closes the connection. The communications in this program are all one-way,
the server never pays any attention to input from the client, but that is a trivial
thing to change.
It expects to be given a port number on the command line. Remember that numbers below 1024
are reserved on many systems. When the server is running on computer X, port Y, it can
be tested with the command "telnet X Y", or using the sample client.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>
extern int errno;
void main(int argc, char *argv[])
{
// get port number to serve from command line
if (argc!=2)
{ fprintf(stderr, "no port number\n"); exit(1); }
int port_number = atol(argv[1]);
// Step 1 create a socket
int main_socket = socket(AF_INET, SOCK_STREAM, 0);
if (main_socket<0) { perror("socket creation"); exit(1); }
// Step 2 create a sockaddr_in to describe the service
sockaddr_in server_info;
server_info.sin_len = sizeof(server_info);
server_info.sin_family = AF_INET;
server_info.sin_addr.s_addr = htonl(INADDR_ANY);
server_info.sin_port = htons(port_number);
// Step 3 use bind to set the socket according to the sockaddr_in
int r1 = bind(main_socket, (sockaddr *) &server_info, sizeof(server_info));
if (r1<0) { perror("port in use"); exit(1); }
// Step 4 use listen to turn the server on
int r2 = listen(main_socket, 3);
if (r2<0) { perror("listen"); exit(1); }
printf("Listening on port %d\n", port_number);
// Servers usually accept multiple clients, so a loop is used
int session_number=0;
while (1)
{
// Step 5 set up another sockaddr_in to receive information on the client
sockaddr_in client_info;
unsigned int client_info_size = sizeof(client_info);
// Step 6 use accept to wait for and accept the next client
int session_socket = accept(main_socket, (sockaddr *) &client_info, &client_info_size);
if (session_socket<0)
{ if (errno==EINTR) continue; // EINTR is not a problem
perror("accept");
usleep(100000);
// Other errors are probably a reason to stop, but for a robust server
// just give the error a chance to go away and try again
continue; }
session_number+=1;
// At this point, a heavy-duty server would use fork to create a sub-process, and
// let the sub-process deal with the client, so we can wait for other clients.
// accept filled in the provided sockaddr_in with information on the client
char * who_is_it = inet_ntoa(client_info.sin_addr);
printf("[connection %d accepted from %s]\n", session_number, who_is_it);
// Step 7 (Optional) Create normal files for communication with the client
// Or just use read and write directly on the session_socket itself
FILE * r_connection=fdopen(session_socket, "r");
FILE * w_connection=fdopen(session_socket, "w");
// Step 8 Deal with the client
fprintf(w_connection, "Hello, %s\r\n", who_is_it);
// Step 9 When finished send all lingering transmissions and close the connection.
fflush(w_connection);
fclose(w_connection);
fclose(r_connection);
close(session_socket); } }
Sample Internet (IP+TCP) Client Program (C++)
This client expects an computer name and port number on the command line; the
basic version (without step 6) connects
to the indicated server, then just prints whatever the server sends. Communications
are entirely one-way: the client never transmits anything, so it can only usefully
connect to servers that never listen. The best
way to test it is with the Sample Server
If the two extra lines shown as the optional step 6 are included in the program,
it acts as a text-only web browser. After connecting to the server, it sends a
simple HTTP request for its main index.html page, and prints out whatever
is received. This version doesn't need any special server to test it, any
available web server will do, e.g. "a.out rabbit.eng.miami.edu 80"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
void main(int argc, char *argv[])
{
// get server IP address and port number of server from command line
if (argc!=3)
{ fprintf(stderr, "no address and port\n"); exit(1); }
char * server = argv[1];
int port_number = atol(argv[2]);
// Step 1 Look up server to get numeric IP address
struct hostent * record = gethostbyname(server);
if (record==NULL) { herror("gethostbyname failed"); exit(1); }
struct in_addr * addressptr = (in_addr *) record->h_addr;
// Step 2 create a socket
int main_socket = socket(AF_INET, SOCK_STREAM, 0);
if (main_socket<0) { perror("socket creation"); exit(1); }
// Step 3 create a sockaddr_in to describe the server
struct sockaddr_in server_info;
server_info.sin_len = sizeof(server_info);
server_info.sin_family = AF_INET;
server_info.sin_addr = * addressptr;
server_info.sin_port = htons(port_number);
// Step 4 connect
int r = connect(main_socket, (struct sockaddr *) &server_info, sizeof(server_info));
if (r<0) { perror("connect"); exit(1); }
printf("Connected to %s\n", inet_ntoa(* addressptr));
// Step 5 (Optional) Create normal files for communication with the client
// Or just use read and write directly on the session_socket itself
FILE * r_connection=fdopen(main_socket, "r");
FILE * w_connection=fdopen(main_socket, "w");
// Step 6 (Optional, for web servers only)
// Only include the following two lines when connecting to a web server
fprintf(w_connection, "GET /index.html HTTP/1.0\r\n\r\n");
fflush(w_connection);
// Step 7 Deal with the client
char line[1000];
for (int i=1; 1; i+=1)
{ char *s=fgets(line, 999, r_connection);
if (s==NULL)
{ printf("Disconnected\n");
break; }
printf("Line %d: %s", i, line); }
// Step 8 When finished send all lingering transmissions and close the connection
fflush(w_connection);
fclose(w_connection);
fclose(r_connection);
close(main_socket); }
Sample IP+UDP Communications Program (C++)
This client expects three things on the command line: the port it should listen on,
the remote computer it should send to, and the port on that remote computer.
The program immediately sends a string to the indicated computer+port, then waits
for a single message from anywhere, then sends a second string to the original
computer+port again, before exitting. Because UDP just shouts into the wind, and
does not guarantee delivery, you should not expect to see all the messages.
For example, in one rabbit session type "a.out 1234 rabbit.eng.miami.edu 1235";
The program immediately sends "hello you" to port 1235 (where nothing is listening yet)
then waits for incoming messages. Next, in another session, type the similar but different
"a.out 1235 rabbit.eng.miami.edu 1234"; here the program will immediately send
"hello you" to port 1234, where it is received by the first session and printed. It then
waits for incoming messages on port 1235. The first session then sends its second message
"hello again" to port 1235 and exits. The second session receives and prints "hello again",
then proceeds to send "hello again" to port 1234 where nothing is listening before exitting.
So although four messages are sent, only two are received and printed, one saying "hello you"
and the other "hello again".
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
void main(int argc, char *argv[])
{
// get port to run on, and IP+port to communicate with from command line
if (argc!=4)
{ fprintf(stderr, "requires localport remoteip remoteport\n"); exit(1); }
int port_number = atol(argv[1]);
char * remote = argv[2];
int remote_port = atol(argv[3]);
// Step 1 Look up server to get numeric IP address
struct hostent * record = gethostbyname(remote);
if (record==NULL) { herror("gethostbyname failed"); exit(1); }
struct in_addr * addressptr = (in_addr *) record->h_addr;
// Step 2 Create a socket
int main_socket = socket(AF_INET, SOCK_DGRAM, 0);
if (main_socket<0) { perror("socket creation"); exit(1); }
// Step 3 Create a sockaddr_in to describe the local port
struct sockaddr_in local_info;
local_info.sin_len = sizeof(local_info);
local_info.sin_family = AF_INET;
local_info.sin_addr.s_addr = htonl(INADDR_ANY);
local_info.sin_port = htons(port_number);
// Step 4 Bind the socket to the port
int r = bind(main_socket, (struct sockaddr *) &local_info, sizeof(local_info));
if (r<0) { perror("bind"); exit(1); }
printf("ready\n");
// Step 5 Create a sockaddr_in to describe the remote application
struct sockaddr_in remote_info;
remote_info.sin_len = sizeof(remote_info);
remote_info.sin_family = AF_INET;
remote_info.sin_addr = *addressptr;
remote_info.sin_port = htons(remote_port);
// Step 6 Send a message
char *message1="Hello you\n";
r=sendto(main_socket, message1, strlen(message1), MSG_EOR,
(struct sockaddr *) &remote_info, sizeof(remote_info));
if (r<0) { perror("sendto"); exit(1); }
printf("sent %d characters of %s\n", r, message1);
// Step 7 Wait for a message to be received
char buffer[100];
struct sockaddr_in incoming_info;
unsigned int socklen = sizeof(incoming_info);
r=recvfrom(main_socket, &buffer, sizeof(buffer)-1, 0,
(struct sockaddr *) &incoming_info, &socklen);
if (r<0) { perror("recvfrom"); exit(1); }
buffer[r]=0;
printf("from %s port %d, %d characters: %s\n",
inet_ntoa(incoming_info.sin_addr),
ntohs(incoming_info.sin_port),
r,
buffer);
// Duplicate for step 6, but with a different message
char *message2="Hello again\n";
r=sendto(main_socket, message2, strlen(message2), MSG_EOR,
(struct sockaddr *) &remote_info, sizeof(remote_info));
if (r<0) { perror("sendto"); exit(1); }
printf("sent %d characters of %s\n", r, message2);
// Step 8 Close the socket and exit
close(main_socket); }