Introduction to the Socket API Introduction to the Socket API Tom Kelliher, CS43 Mar. 19, 1996 Background reading: W. R. Stevens, UNIX Network Programming, Chapter 6 (on reserve). A Client-Server Dialogue Sockets API socket() #include
#include
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();
bind() #include
#include
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: Pointer to a struct sockaddr_in for Internet sockets. Cast to 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();
listen() #include
#include
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);
accept() #include
#include
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: Pointer to a struct sockaddr_in for Internet sockets. Cast to 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));
connect() #include
#include
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: Pointer to a struct sockaddr_in for Internet sockets. Cast to 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));
read() #include
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();
write() #include
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(). close() #include
int close(int sd);
Close socket descriptor sd. Returns -1 on failure, otherwise 0. Byte Ordering Calls #include
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
#include
#include
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. Example Server Program /**********************************************************************
* 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
#include
#include
#include
#include
#include
#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);
}
Example Client Program /**********************************************************************
* 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
#include
#include
#include
#include
#include
#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);
}
Thomas P. Kelliher Mon Mar 18 22:35:52 EST 1996 Tom Kelliher