You are on page 1of 9

TCP/IP Sockets in C: Practical Guide for Programmers

Michael J. Donahoo Kenneth L. Calvert Morgan Kaufmann Publisher $14.95 Paperback

Computer Chat
!

How do we make computers talk?

How are they interconnected?


Internet Protocol (IP)

Internet Protocol (IP)


! !

IP Address
! ! ! !

Datagram (packet) protocol Best-effort service


! ! ! !

Loss Reordering Duplication Delay

32-bit identifier (IPv4, IPv6=128 bits) Dotted-quad: 192.118.56.25 www.mkp.com -> 167.208.101.28 Identifies a host interface (not a host)

Host-to-host delivery
192.18.22.13 209.134.16.123

Transport Protocols
Best-effort not sufficient!
! !

Ports
Identifying the ultimate destination
! ! !

Add services on top of IP User Datagram Protocol (UDP)


! !

Data checksum Best-effort Data checksum Reliable byte-stream delivery Flow and congestion control

IP addresses identify hosts Host has many applications Ports (16-bit identifier) 1-65,535
Application Port WWW 80 E-mail 25 Telnet 23

Transmission Control Protocol (TCP)


! ! !

192.18.22.13

Socket
How does one speak TCP/IP?
! !
!

Sockets
Identified by protocol and local/remote address/port Applications may refer to many sockets Sockets accessed by many applications

Sockets provides interface to TCP/IP Generic interface for many protocols

TCP/IP Sockets
!

Specifying Addresses
!

Family TCP UDP


!

Type SOCK_STREAM SOCK_DGRAM

Protocol IPPROTO_TCP

Generic
!

mySock = socket(family, type, protocol); TCP/IP-specific sockets


PF_INET

struct sockaddr { unsigned short sa_family; /* Address family (e.g., AF_INET) */ char sa_data[14]; /* Protocol-specific address information */ }; struct sockaddr_in { unsigned short sin_family; /* Internet protocol (AF_INET) */ unsigned short sin_port; /* Port (16-bits) */ struct in_addr sin_addr; /* Internet address (32-bits) */ char sin_zero[8]; /* Not used */ }; struct in_addr { unsigned long s_addr; /* Internet address (32-bits) */ };

Socket reference
!

File (socket) descriptor in UNIX

Clients and Servers


!

IP Specific

IPPROTO_UDP

TCP Client/Server Interaction


Server starts by getting ready to receive client connections
Server Create a TCP socket Assign a port to socket Set socket to listen Repeatedly: a. Accept new connection b. Communicate c. Close the connection

Client: Initiates the connection


Client: Bob Hi. Im Bob. Hi, Bob. Im Jane
1. 2.

Server: Jane

Nice to meet you, Jane.


!

3. 4.

Client Create a TCP socket Establish connection Communicate Close the connection

1. 2. 3. 4.

Server: Passively waits to respond

TCP Client/Server Interaction


/* Create socket for incoming connections */ if ((servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) DieWithError("socket() failed");

TCP Client/Server Interaction


echoServAddr.sin_family = AF_INET; /* Internet address family */ echoServAddr.sin_addr.s_addr = htonl(INADDR_ANY);/* Any incoming interface */ echoServAddr.sin_port = htons(echoServPort); /* Local port */ if (bind(servSock, (struct sockaddr *) &echoServAddr, sizeof(echoServAddr)) < 0) DieWithError("bind() failed");

1. 2. 3. 4.

Client Create a TCP socket Establish connection Communicate Close the connection

1. 2. 3. 4.

Server Create a TCP socket Bind socket to a port Set socket to listen Repeatedly: a. Accept new connection b. Communicate c. Close the connection

1. 2. 3. 4.

Client Create a TCP socket Establish connection Communicate Close the connection

1. 2. 3. 4.

Server Create a TCP socket Bind socket to a port Set socket to listen Repeatedly: a. Accept new connection b. Communicate c. Close the connection

TCP Client/Server Interaction


/* Mark the socket so it will listen for incoming connections */ if (listen(servSock, MAXPENDING) < 0) DieWithError("listen() failed");

TCP Client/Server Interaction


for (;;) /* Run forever */ { clntLen = sizeof(echoClntAddr); if ((clntSock=accept(servSock,(struct sockaddr *)&echoClntAddr,&clntLen)) < 0) DieWithError("accept() failed");

1. 2. 3. 4.

Client Create a TCP socket Establish connection Communicate Close the connection

1. 2. 3. 4.

Server Create a TCP socket Bind socket to a port Set socket to listen Repeatedly: a. Accept new connection b. Communicate c. Close the connection

1. 2. 3. 4.

Client Create a TCP socket Establish connection Communicate Close the connection

1. 2. 3. 4.

Server Create a TCP socket Bind socket to a port Set socket to listen Repeatedly: a. Accept new connection b. Communicate c. Close the connection

TCP Client/Server Interaction


Server is now blocked waiting for connection from a client

TCP Client/Server Interaction


Later, a client decides to talk to the server

1. 2. 3. 4.

Client Create a TCP socket Establish connection Communicate Close the connection

1. 2. 3. 4.

Server Create a TCP socket Bind socket to a port Set socket to listen Repeatedly: a. Accept new connection b. Communicate c. Close the connection

1. 2. 3. 4.

Client Create a TCP socket Establish connection Communicate Close the connection

1. 2. 3. 4.

Server Create a TCP socket Bind socket to a port Set socket to listen Repeatedly: a. Accept new connection b. Communicate c. Close the connection

TCP Client/Server Interaction


/* Create a reliable, stream socket using TCP */ if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) DieWithError("socket() failed");

TCP Client/Server Interaction


echoServAddr.sin_family = AF_INET; /* Internet address family */ echoServAddr.sin_addr.s_addr = inet_addr(servIP); /* Server IP address */ echoServAddr.sin_port = htons(echoServPort); /* Server port */ if (connect(sock, (struct sockaddr *) &echoServAddr, sizeof(echoServAddr)) < 0) DieWithError("connect() failed");

1. 2. 3. 4.

Client Create a TCP socket Establish connection Communicate Close the connection

1. 2. 3. 4.

Server Create a TCP socket Bind socket to a port Set socket to listen Repeatedly: a. Accept new connection b. Communicate c. Close the connection

1. 2. 3. 4.

Client Create a TCP socket Establish connection Communicate Close the connection

1. 2. 3. 4.

Server Create a TCP socket Bind socket to a port Set socket to listen Repeatedly: a. Accept new connection b. Communicate c. Close the connection

TCP Client/Server Interaction


echoStringLen = strlen(echoString); /* Determine input length */ /* Send the string to the server */ if (send(sock, echoString, echoStringLen, 0) != echoStringLen) DieWithError("send() sent a different number of bytes than expected");

TCP Client/Server Interaction


if ((clntSock=accept(servSock,(struct sockaddr *)&echoClntAddr,&clntLen)) < 0) DieWithError("accept() failed");

1. 2. 3. 4.

Client Create a TCP socket Establish connection Communicate Close the connection

1. 2. 3. 4.

Server Create a TCP socket Bind socket to a port Set socket to listen Repeatedly: a. Accept new connection b. Communicate c. Close the connection

1. 2. 3. 4.

Client Create a TCP socket Establish connection Communicate Close the connection

1. 2. 3. 4.

Server Create a TCP socket Bind socket to a port Set socket to listen Repeatedly: a. Accept new connection b. Communicate c. Close the connection

TCP Client/Server Interaction


/* Receive message from client */ if ((recvMsgSize = recv(clntSocket, echoBuffer, RCVBUFSIZE, 0)) < 0) DieWithError("recv() failed");

TCP Client/Server Interaction


close(sock); close(clntSocket)

1. 2. 3. 4.

Client Create a TCP socket Establish connection Communicate Close the connection

1. 2. 3. 4.

Server Create a TCP socket Bind socket to a port Set socket to listen Repeatedly: a. Accept new connection b. Communicate c. Close the connection

1. 2. 3. 4.

Client Create a TCP socket Establish connection Communicate Close the connection

1. 2. 3. 4.

Server Create a TCP socket Bind socket to a port Set socket to listen Repeatedly: a. Accept new connection b. Communicate c. Close the connection

TCP Tidbits
! !

Closing a Connection
! !

Client knows server address and port No correlation between send() and recv()

close() used to delimit communication Analogous to EOF

Client
send(Hello Bob)

Server
recv() -> Hello recv() -> Bob send(Hi ) send(Jane)

Client
send(string) while (not received entire string) recv(buffer) send(buffer) close(socket)

Server
recv(buffer) while(client has not closed connection) send(buffer) recv(buffer)

recv() -> Hi Jane

close(client socket)

TCP/IP Byte Transport Constructing Messages


beyond simple strings
!

TCP/IP protocols transports bytes


Application
byte stream Here are some bytes. I dont know what they mean.

Application
byte stream Ill pass these to the app. It knows what to do.

TCP/IP

TCP/IP

Application protocol provides semantics

Application Protocol
! !

Primitive Types
!

Encode information in bytes Sender and receiver must agree on semantics Data encoding
! !

String
! !

Character encoding: ASCII, Unicode, UTF Delimit: length vs. termination character
77 M 0 o 111 111 0 m 109 109 0 \n 10

Primitive types: strings, integers, and etc. Composed types: message with fields
3

77

Primitive Types
!

Primitive Types
!

Integer
!

Integer
!

Strings of character encoded decimal digits


55 7
!

Native representation
0 0 92 246 23,798 4-byte twos-complement integer

49 1

57 9

57 9 1. 2. 1. 2.

56 8

55 7

48 0

10 \n

Little-Endian

Advantage: Disadvantage:

Human readable Arbitrary size Inefficient Arithmetic manipulation

Big-Endian
!

246

92

Network byte order (Big-Endian)


! !

Use for multi-byte, binary data exchange htonl(), htons(), ntohl(), ntohs()

Message Composition
!

Beware the bytes of padding -- Julius Caesar, Shakespeare


! !

Message composed of fields


!

Fixed-length fields integer short short

Architecture alignment restrictions Compiler pads structs to accommodate

Variable-length fields i k e 1 2 \n

struct tst { short x; int y; short z; };


! !

[pad]

[pad]

Problem: Alignment restrictions vary Solution: 1) Rearrange struct members 2) Serialize struct by-member

TCPEchoClient.c #include #include #include #include #include #include <stdio.h> <sys/socket.h> <arpa/inet.h> <stdlib.h> <string.h> <unistd.h> /* /* /* /* /* /* for for for for for for printf() and fprintf() */ socket(), connect(), send(), and recv() */ sockaddr_in and inet_addr() */ atoi() */ memset() */ close() */ servIP = argv[1]; echoString = argv[2]; /* First arg: server IP address (dotted quad) */ /* Second arg: string to echo */

if (argc == 4) echoServPort = atoi(argv[3]); /* Use given port, if any */ else echoServPort = 7; /* 7 is the well-known port for the echo service */ /* Create a reliable, stream socket using TCP */ if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) DieWithError("socket() failed"); /* Construct the server address structure */ memset(&echoServAddr, 0, sizeof(echoServAddr)); echoServAddr.sin_family = AF_INET; echoServAddr.sin_addr.s_addr = inet_addr(servIP); echoServAddr.sin_port = htons(echoServPort); /* /* /* /* Zero out structure */ Internet address family */ Server IP address */ Server port */

#define RCVBUFSIZE 32

/* Size of receive buffer */ /* Error handling function */

void DieWithError(char *errorMessage); int main(int argc, char *argv[]) { int sock; struct sockaddr_in echoServAddr; unsigned short echoServPort; char *servIP; char *echoString; char echoBuffer[RCVBUFSIZE]; unsigned int echoStringLen; int bytesRcvd, totalBytesRcvd;

/* /* /* /* /* /* /* /*

Socket descriptor */ Echo server address */ Echo server port */ Server IP address (dotted quad) */ String to send to echo server */ Buffer for echo string */ Length of string to echo */ Bytes read in single recv() and total bytes read */

/* Establish the connection to the echo server */ if (connect(sock, (struct sockaddr *) &echoServAddr, sizeof(echoServAddr)) < 0) DieWithError("connect() failed"); echoStringLen = strlen(echoString); /* Determine input length */

if ((argc < 3) || (argc > 4)) /* Test for correct number of arguments */ { fprintf(stderr, "Usage: %s <Server IP> <Echo Word> [<Echo Port>]\n", argv[0]); exit(1); }

/* Send the string to the server */ if (send(sock, echoString, echoStringLen, 0) != echoStringLen) DieWithError("send() sent a different number of bytes than expected");

TCPEchoServer.c /* receive the same string back from the server */ totalBytesRcvd = 0; printf("Received: "); /* Setup to print the echoed string */ while (totalBytesRcvd < echoStringLen) { /* Receive up to the buffer size (minus 1 to leave space for a null terminator) bytes from the sender */ if ((bytesRcvd = recv(sock, echoBuffer, RCVBUFSIZE - 1, 0)) <= 0) DieWithError("recv() failed or connection closed prematurely"); totalBytesRcvd += bytesRcvd; /* Keep tally of total bytes */ echoBuffer[bytesRcvd] = '\0'; /* Terminate the string! */ printf(echoBuffer); /* Print the echo buffer */ } printf("\n"); close(sock); exit(0); } void DieWithError(char *errorMessage) { perror(errorMessage); exit(1); } /* Print a final linefeed */ #include #include #include #include #include #include <stdio.h> <sys/socket.h> <arpa/inet.h> <stdlib.h> <string.h> <unistd.h> /* /* /* /* /* /* for for for for for for printf() and fprintf() */ socket(), bind(), and connect() */ sockaddr_in and inet_ntoa() */ atoi() */ memset() */ close() */

#define MAXPENDING 5

/* Maximum outstanding connection requests */ /* Error handling function */ /* TCP client handling function */

void DieWithError(char *errorMessage); void HandleTCPClient(int clntSocket); int main(int argc, char *argv[]) { int servSock; int clntSock; struct sockaddr_in echoServAddr; struct sockaddr_in echoClntAddr; unsigned short echoServPort; unsigned int clntLen;

/* /* /* /* /* /*

Socket descriptor for server */ Socket descriptor for client */ Local address */ Client address */ Server port */ Length of client address data structure */

if (argc != 2) /* Test for correct number of arguments */ { fprintf(stderr, "Usage: %s <Server Port>\n", argv[0]); exit(1); } echoServPort = atoi(argv[1]); /* First arg: local port */

/* Create socket for incoming connections */ if ((servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) DieWithError("socket() failed"); /* Construct local address structure */ memset(&echoServAddr, 0, sizeof(echoServAddr)); echoServAddr.sin_family = AF_INET; echoServAddr.sin_addr.s_addr = htonl(INADDR_ANY); echoServAddr.sin_port = htons(echoServPort); /* /* /* /* Zero out structure */ Internet address family */ Any incoming interface */ Local port */

for (;;) /* Run forever */ { /* Set the size of the in-out parameter */ clntLen = sizeof(echoClntAddr); /* Wait for a client to connect */ if ((clntSock = accept(servSock, (struct sockaddr *) &echoClntAddr, &clntLen)) < 0) DieWithError("accept() failed"); /* clntSock is connected to a client! */ printf("Handling client %s\n", inet_ntoa(echoClntAddr.sin_addr));

/* Bind to the local address */ if (bind(servSock, (struct sockaddr *) &echoServAddr, sizeof(echoServAddr)) < 0) DieWithError("bind() failed"); /* Mark the socket so it will listen for incoming connections */ if (listen(servSock, MAXPENDING) < 0) DieWithError("listen() failed"); }

HandleTCPClient(clntSock); } /* NOT REACHED */

Transport Protocols
! ! !

Transport Protocols
!

Best-effort, host-to-host insufficient Transport protocols add services to IP Ports make end-to-end protocols
Application Port WWW 80 E-mail 25 Telnet 23

User Datagram Protocol (UDP)


! !

Adds ports and data checksum to IP Best-effort

Transmission Control Protocol (TCP)


! ! ! !

Adds ports and data checksum to IP Reliable byte-stream delivery Flow and congestion control Etc.

192.18.22.13

Socket Protocols
!

TCP Client
1. 2.

Protocol Family
!

PF_INET Internet Protocol Family SOCK_STREAM Reliable byte-stream SOCK_DGRAM Best-effort datagram IPPROTO_TCP IPPROTO_UDP

Create a TCP socket using socket() Establish a connection to the server using connect() Communicate using send() and recv() Close the connection with close()

Protocol Type
! !

3.

Protocol
! !

4.

TCP Server
1. 2.

Protocols
! !

3.

4.

Create a TCP socket using socket() Assign a port number to the socket with bind() Tell the system to allow connections to be made to that port, using listen() Repeatedly do the following:
a.

We want to make computers talk Protocol: Agreement on exchange


Bob Hi. Im Bob. Hi, Bob. Im Jane Jane

b.

c.

Call accept() to get a new socket for each client connection. Communicate with the client via that new socket, using send() and recv() Close the client connection using close()

Nice to meet you, Jane.

TCPEchoServer-Fork.c #include "TCPEchoServer.h" #include <sys/wait.h> /* TCP echo server includes */ /* for waitpid() */

int main(int argc, char *argv[]) { int servSock; int clntSock; unsigned short echoServPort; pid_t processID; unsigned int childProcCount = 0;

/* /* /* /* /*

Socket descriptor for server */ Socket descriptor for client */ Server port */ Process ID from fork() */ Number of child processes */

for (;;) /* Run forever */ { clntSock = AcceptTCPConnection(servSock); /* Fork child process and report any errors */ if ((processID = fork()) < 0) DieWithError("fork() failed"); else if (processID == 0) /* If this is the child process */ { close(servSock); /* Child closes parent socket */ HandleTCPClient(clntSock); exit(0); } printf("with child process: %d\n", (int) processID); close(clntSock); /* Parent closes child socket descriptor */ childProcCount++; /* Increment number of outstanding child processes */ while (childProcCount) /* Clean up all zombies */ { processID = waitpid((pid_t) -1, NULL, WNOHANG); /* Non-blocking wait */ if (processID < 0) /* waitpid() error? */ DieWithError("waitpid() failed"); else if (processID == 0) /* No zombie to wait on */ break; else childProcCount--; /* Cleaned up after a child */ } } } /* Child process terminates */

if (argc != 2) /* Test for correct number of arguments */ { fprintf(stderr, "Usage: %s <Server Port>\n", argv[0]); exit(1); } echoServPort = atoi(argv[1]); /* First arg: local port */

servSock = CreateTCPServerSocket(echoServPort);

#define MAXPENDING 5

/* Maximum outstanding connection requests */ /* Error handling function */ #include <stdio.h> /* for printf() */ #include <sys/socket.h> /* for accept() */ #include <arpa/inet.h> /* for sockaddr_in and inet_ntoa() */ void DieWithError(char *errorMessage); /* Error handling function */

void DieWithError(char *errorMessage);

int CreateTCPServerSocket(unsigned short port) { int sock; /* socket to create */ struct sockaddr_in echoServAddr; /* Local address */ /* Create socket for incoming connections */ if ((sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) DieWithError("socket() failed"); /* Construct local address structure */ memset(&echoServAddr, 0, sizeof(echoServAddr)); echoServAddr.sin_family = AF_INET; echoServAddr.sin_addr.s_addr = htonl(INADDR_ANY); echoServAddr.sin_port = htons(port); /* /* /* /* Zero out structure */ Internet address family */ Any incoming interface */ Local port */

int AcceptTCPConnection( int servSock ) { int clntSock; /* Socket descriptor for client */ struct sockaddr_in echoClntAddr; /* Client address */ unsigned int clntLen; /* Length of client address data structure */ /* Set the size of the in-out parameter */ clntLen = sizeof(echoClntAddr); /* Wait for a client to connect */ if ((clntSock = accept(servSock, (struct sockaddr *) &echoClntAddr, &clntLen)) < 0) DieWithError("accept() failed"); /* clntSock is connected to a client! */ printf("Handling client %s\n", inet_ntoa(echoClntAddr.sin_addr)); return clntSock; }

/* Bind to the local address */ if (bind(sock, (struct sockaddr *) &echoServAddr, sizeof(echoServAddr)) < 0) DieWithError("bind() failed"); /* Mark the socket so it will listen for incoming connections */ if (listen(sock, MAXPENDING) < 0) DieWithError("listen() failed"); return sock; }

Resources/References
http://cs.ecs.baylor.edu/~donahoo/practical/C Sockets http://www.ecst.csuchico.edu/~beej/guide
! !

Network programming Unix Interprocess communication

You might also like