Tom Kelliher, CS43
Mar. 19, 1996
Background reading: W. R. Stevens, UNIX Network Programming, Chapter 6 (on reserve).
#include <sys/types.h> #include <sys/socket.h> int socket(int domain, int type, int protocol);
Used to create an unamed socket. Must bind or connect.
Returns a socket descriptor (small positive int). Returns -1 on error.
domain:
AF_UNIX
--- UNIX domain protocols.
AF_INET
--- Internet domain protocols.
type:
SOCK_STREAM
--- TCP protocol.
SOCK_DGRAM
--- UDP protocol.
SOCK_RAW
--- IP protocol.
protocol: set to 0.
Typical call:
int sock; if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) die();
#include <sys/types.h> #include <sys/socket.h> int bind(int sd, const struct sockaddr *name, int namelen);
Assigns a name (port, etc.) to a socket.
Returns 0 if successful, otherwise -1 and look in errno.
sd: socket descriptor.
name:
struct sockaddr_in
for Internet sockets.
struct sockaddr *
.
/* * Socket address, internet style. */ struct sockaddr_in { u_char sin_len; u_char sin_family; /* AF_INET */ u_short sin_port; /* 16 bit port number */ struct in_addr sin_addr; /* IP address */ char sin_zero[8]; };For a server:
sin_port
--- desired listen port. Gt 5000.
sin_addr
--- another struct. Where we're willing to accept
connections from.
namelen: sizeof name structure.
Typical call:
struct sockaddr_in server; bzero((char *) &server, sizeof(server)); server.sin_family = AF_INET; server.sin_addr.s_addr = INADDR_ANY; server.sin_port = htons(SERVER_PORT); if (bind(sock, (struct sockaddr *) &server, sizeof(server))) die();
#include <sys/types.h> #include <sys/socket.h> int listen(int sd, int backlog);
Announce willingness to accept connections to socket and a maximum queue length.
Returns 0 on success, otherwise -1.
sd: socket descriptor.
backlog: max size of queue. Maximum is 5.
Typical call:
int sock; listen(sock, 5);
#include <sys/types.h> #include <sys/socket.h> int accept(int sd, struct sockaddr *addr, int *addrlen);
Extract first connection request on queue; blocking if queue is empty.
Returns -1 on error, otherwise a non-negative descriptor to use for socket I/O ( read() or write()).
sd: socket descriptor.
addr:
struct sockaddr_in
for Internet sockets.
struct sockaddr *
.
sin_port
--- client port.
sin_addr
--- client IP address.
addrlen: addressof sizeof addr structure.
Typical call:
struct sockaddr_in client; int msgsock, sock, clientLen; clientLen = sizeof(client); if ((msgsock = accept(sock, (struct sockaddr *) &client, &clientLen)) == -1) die(); else { /* Print information about the client. */ if (clientLen != sizeof(client)) die(); printf("Client IP: %s\n", inet_ntoa(client.sin_addr)); printf("Client Port: %hu\n", ntohs(client.sin_port));
#include <sys/types.h> #include <sys/socket.h> int connect(int sd, const struct sockaddr *name, int namelen);
Connect socket to another socket, opening a socket pair communication channel.
Returns 0 on success, -1 otherwise. Check errno.
sd: socket descriptor.
name:
struct sockaddr_in
for Internet sockets.
struct sockaddr *
.
sin_port
--- server port.
sin_addr
--- server IP address.
namelen: sizeof name structure.
Will automatically do a bind and assign an ephemeral port. Use getsockname() to discover the port number.
Typical call:
struct sockaddr_in server; struct hostent* hp; bzero((char *) &server, sizeof(server)); server.sin_family = AF_INET; if ((hp = gethostbyname(argv[1])) == NULL) { sprintf(buf, "%s: unknown host\n", argv[1]); die(buf); } bcopy(hp->h_addr, &server.sin_addr, hp->h_length); server.sin_port = htons((u_short) SERVER_PORT); /* Try to connect */ if (connect(sock, (struct sockaddr *) &server, sizeof(server)) < 0) pdie("Connecting stream socket"); /* Determine what port client's using. */ clientLen = sizeof(client); if (getsockname(sock, (struct sockaddr *) &client, &clientLen)) pdie("Getting socket name"); if (clientLen != sizeof(client)) die("getsockname() overwrote name structure"); printf("Client socket has port %hu\n", ntohs(client.sin_port));
#include <unistd.h> ssize_t read(int sd, void* buf, size_t nbytes);
Read at most nbytes bytes into buf from sd.
Returns -1 on error, 0 on EOF (connection closed), or the actual number of bytes read.
sd: socket descriptor.
buf: addressof char buffer.
nbytes: sizeof buf.
Typical call:
char buf[BUFFER_SIZE]; int rval, msgsock; bzero(buf, BUFFER_SIZE); if ((rval = read(msgsock, buf, BUFFER_SIZE)) < 0) die();
#include <unistd.h> ssize_t write(int sd, const void *buf, size_t nbytes);
Attempts to write nbytes from buf to sd.
Returns -1 on error or the number of bytes actually written.
Arguments similar to read().
#include <unistd.h> int close(int sd);
Close socket descriptor sd.
Returns -1 on failure, otherwise 0.
#include <sys/param.h> u_long htonl(u_long hostlong); u_short htons(u_short hostshort); u_long ntohl(u_long netlong); u_short ntohs(u_short netshort);
Convert 16- and 32-bit integers between native format (host) and network format.
#include <sys/types.h> #include <netinet/in.h> #include <arpa/inet.h> unsigned long inet_addr(const char *ip); char* inet_ntoa(struct in_addr in);
inet_addr()
converts a char string IP address to its 32-bit network
byte-order integer equivalent.
inet_ntoa
does the opposite.
/********************************************************************** * server.c --- Demonstrate a simple iterative server. * Tom Kelliher * * This program demonstrates a simple iterative server. The server * opens a TCP connection on port SERVER_PORT and begins accepting * connections from anywhere. It sits in an endless loop, so one must * send an INTR to terminate it. * * The server reads a message from the client, printing it to stdout. * Then, the server sends a simple message back to the client. **********************************************************************/ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <stdio.h> #include <stdlib.h> #define DATA "Danger Will Roger . . ." #define TRUE 1 #define SERVER_PORT 5001 #define BUFFER_SIZE 1024 /* prototypes */ void die(const char *); void pdie(const char *); /********************************************************************** * main **********************************************************************/ int main(void) { int sock; /* fd for main socket */ int msgsock; /* fd from accept return */ struct sockaddr_in server; /* socket struct for server connection */ struct sockaddr_in client; /* socket struct for client connection */ int clientLen; /* returned length of client from accept() */ int rval; /* return value from read() */ char buf[BUFFER_SIZE]; /* receive buffer */ /* Open a socket, not bound yet. Type is Internet TCP. */ if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) pdie("Opening stream socket"); /* Prepare to bind. Permit Internet connections from any client to our SERVER_PORT. */ bzero((char *) &server, sizeof(server)); server.sin_family = AF_INET; server.sin_addr.s_addr = INADDR_ANY; server.sin_port = htons(SERVER_PORT); if (bind(sock, (struct sockaddr *) &server, sizeof(server))) pdie("Binding stream socket"); printf("Socket has port %hu\n", ntohs(server.sin_port)); /* Set the listen queue to 5, the maximum. */ listen(sock, 5); /* Loop, waiting for client connections. */ /* This is an interactive server. */ while (TRUE) { clientLen = sizeof(client); if ((msgsock = accept(sock, (struct sockaddr *) &client, &clientLen)) == -1) pdie("Accept"); else { /* Print information about the client. */ if (clientLen != sizeof(client)) pdie("Accept overwrote sockaddr structure."); printf("Client IP: %s\n", inet_ntoa(client.sin_addr)); printf("Client Port: %hu\n", ntohs(client.sin_port)); do { /* Read from client until it's closed the connection. */ /* Prepare read buffer and read. */ bzero(buf, sizeof(buf)); if ((rval = read(msgsock, buf, BUFFER_SIZE)) < 0) pdie("Reading stream message"); if (rval == 0) /* Client has closed the connection */ fprintf(stderr, "Ending connection\n"); else printf("S: %s\n", buf); /* Write back to client. */ if (write(msgsock, DATA, sizeof(DATA)) < 0) pdie("Writing on stream socket"); } while (rval != 0); } /* else */ close(msgsock); } exit(0); } /********************************************************************** * pdie --- Call perror() to figure out what's going on and die. **********************************************************************/ void pdie(const char *mesg) { perror(mesg); exit(1); } /********************************************************************** * die --- Print a message and die. **********************************************************************/ void die(const char *mesg) { fputs(mesg, stderr); fputc('\n', stderr); exit(1); }
/********************************************************************** * client.c --- Demonstrate a simple client. * Tom Kelliher * * This program will connect to a simple iterative server and exchange * messages. The single command line argument is the server's hostname. * The server is expected to be accepting connection requests from * SERVER_PORT. * * The same message is sent three times over separate connections, * demonstrating that different ephemeral ports are used for each * connection. **********************************************************************/ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <stdio.h> #include <stdlib.h> #define DATA "The sea is calm tonight, the tide is full . . ." #define SERVER_PORT 5001 #define BUFFER_SIZE 1024 /* prototypes */ void die(const char *); void pdie(const char *); /********************************************************************** * main **********************************************************************/ int main(int argc, char *argv[]) { int sock; /* fd for socket connection */ struct sockaddr_in server; /* Socket info. for server */ struct sockaddr_in client; /* Socket info. about us */ int clientLen; /* Length of client socket struct. */ struct hostent *hp; /* Return value from gethostbyname() */ char buf[BUFFER_SIZE]; /* Received data buffer */ int i; /* loop counter */ if (argc != 2) die("Usage: client hostname"); /* Open 3 sockets and send same message each time. */ for (i = 0; i < 3; ++i) { /* Open a socket --- not bound yet. */ /* Internet TCP type. */ if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) pdie("Opening stream socket"); /* Prepare to connect to server. */ bzero((char *) &server, sizeof(server)); server.sin_family = AF_INET; if ((hp = gethostbyname(argv[1])) == NULL) { sprintf(buf, "%s: unknown host\n", argv[1]); die(buf); } bcopy(hp->h_addr, &server.sin_addr, hp->h_length); server.sin_port = htons((u_short) SERVER_PORT); /* Try to connect */ if (connect(sock, (struct sockaddr *) &server, sizeof(server)) < 0) pdie("Connecting stream socket"); /* Determine what port client's using. */ clientLen = sizeof(client); if (getsockname(sock, (struct sockaddr *) &client, &clientLen)) pdie("Getting socket name"); if (clientLen != sizeof(client)) die("getsockname() overwrote name structure"); printf("Client socket has port %hu\n", ntohs(client.sin_port)); /* Write out message. */ if (write(sock, DATA, sizeof(DATA)) < 0) pdie("Writing on stream socket"); /* Prepare our buffer for a read and then read. */ bzero(buf, sizeof(buf)); if (read(sock, buf, BUFFER_SIZE) < 0) pdie("Reading stream message"); printf("C: %s\n", buf); /* Close this connection. */ close(sock); } exit(0); } /********************************************************************** * pdie --- Call perror() to figure out what's going on and die. **********************************************************************/ void pdie(const char *mesg) { perror(mesg); exit(1); } /********************************************************************** * die --- Print a message and die. **********************************************************************/ void die(const char *mesg) { fputs(mesg, stderr); fputc('\n', stderr); exit(1); }