
Socket Programming and TCP Connections
Learn about Socket Programming, TCP Connections, and the Sockets API in this comprehensive guide. Explore how sockets facilitate inter-process communication, different socket families like AF_UNIX and AF_INET, socket types such as Stream and Datagram, and the role of TCP connections in networking applications. Discover how to use the Socket API for TCP, with insights into client-side programming with sockets.
Download Presentation

Please find below an Image/Link to download the presentation.
The content on the website is provided AS IS for your information and personal use only. It may not be sold, licensed, or shared on other websites without obtaining consent from the author. If you encounter any issues during the download, it is possible that the publisher has removed the file from their server.
You are allowed to download the files provided on this website for personal or commercial use, subject to the condition that they are used lawfully. All files are the property of their respective owners.
The content on the website is provided AS IS for your information and personal use only. It may not be sold, licensed, or shared on other websites without obtaining consent from the author.
E N D
Presentation Transcript
CS252: Systems Programming Ninghui Li Based on slides by Prof. Gustavo Rodriguez-Rivera Topic 16: Socket Programming & Project 5
Sockets API They were introduced by UNIX BSD (Berkeley Standard Distribution). Sockets are a form of IPC (Inter-Process Communications) Sockets families UNIX (AF_UNIX) Between processes on the same host IPv4 (AF_INET) Using IPv4 to communicate IPv6 (IF_INET6)
Sockets API Sockets with IPv4 provide a standard API for programming using TCP/IP. A program that uses sockets can be easily ported to other OS s that implement sockets. Socket types Stream (think online chatting session, TCP): reliable delivery (know delivery status), connection-oriented (existence of a peer socket), no message boundary Datagram (think SMS messages, UDP): no reliable delivery, not connection-oriented, has message boundary
TCP Connections A connection is defined uniquely in the entire Internet by five values: <protocol, src-ip-addr, src-port, dest-ip-addr, dest-port> Example: A runs an HTTP server in port 80 B connects to A s HTTP server using source port 5000 The connection is <TCP, IPB, 5000, IPA, 80> C connects also to A s HTTP server using source port 8000 The connection is <TCP, IPC, 8000, IPA, 80> Another browser in B using port 6000 connects to A The connection is <TCP, IPB, 6000, IPA, 80> Another browser in C using port 5000 connects to A The connection is <TCP, IPC, 5000, IPA, 80> The OS uses these values to know what data corresponds to what application/socket.
Sockets API Most networking applications are written using sockets: web browser/server, FTP client/server, mail, p2p file sharing, finger, DNS etc. Many protocols take the form of a conversation between client and server using ASCII Text. E.g., you can telnet to a WWW server, a mail server, etc.
Programming With Sockets Client Side int cs =socket(AF_INET, SOCK_STREAM, proto) connect (cs, addr, sizeof(addr)) write(cs, buf, len) read(cs, buf, len); close(cs)
Programming With Sockets Server Side int masterSocket = socket(AF_INET, SOCK_STREAM, 0); int error = bind( masterSocket, (struct sockaddr *)&serverIPAddress, sizeof(serverIPAddress) ); error = listen( masterSocket, QueueLength); while ( 1 ) { int slaveSocket = accept( masterSocket, (struct sockaddr*)&clientIPAddress, (socklen_t*)&alen); read(slaveSocket, buf, len); write(slaveSocket, buf, len); close(slaveSocket); }
Socket Creation in C: socket int s = socket(domain, type, protocol); where s: socket descriptor, an integer (like a file descriptor) domain: integer, communication domain e.g., AF_INET (IPv4 protocol) type: communication type SOCK_STREAM: reliable, 2-way, connection-based service SOCK_DGRAM: unreliable, connectionless protocol: e.g., TCP or UDP use IPPROTO_TCP or IPPROTO_UDP, or getproctobyname( tcp ) to send/receive TCP or UDP packets
Client create socket and connect to remote host The client connects to the remote host The connect function is used by a client program to establish communication with a remote entity int status = connect(sock, &servaddr, addrlen); where status: return value, 0 if successful connect, -1 otherwise sock: client s socket to be used in connection servaddr: server s address structure addrlen: size (in bytes) of the servaddr structure connect is blocking Example code: if(connect(hSocket, (struct sockaddr*) &Address, sizeof(Address)) == -1) { printf("\nCould not connect to host\n"); }
Socket Addresses #include <netinet/in.h> // All pointers to socket address structures are often cast to pointers // to this type before use in various functions and system calls: struct sockaddr { unsigned short sa_family; // address family, AF_xxx char sa_data[14]; // 14 bytes of protocol address };
Socket Addresses // IPv4 AF_INET sockets: struct sockaddr_in { short sin_family; // e.g. AF_INET, AF_INET6 unsigned short sin_port; // e.g. htons(3490) struct in_addr sin_addr; // see struct in_addr, below char sin_zero[8]; // zero this if you want to }; struct in_addr { unsigned long s_addr; // 32 bit unsigned integer };
Getting Internet Addresses #include <netdb.h> struct hostent *gethostbyname(const char *name); 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] /* for backward compatibility */
Getting Internet Addresses in_addr_t inet_addr(const char* string) returns the 32-bit IP address that corresponds to the A.B.C.D format string. The IP address is in network-byte order. If no error occurs, inet_addr returns an unsigned long value containing a suitable binary representation of the Internet address given. If the string in the cp parameter does not contain a legitimate Internet address, for example if a portion of an "a.b.c.d" address exceeds 255, then inet_addr returns the value INADDR_NONE.
Getting Protocol Name #include <netdb.h> struct protoent *getprotobyname(const char *name); struct protoent { char *p_name; /* official protocol name */ char **p_aliases; /* alias list */ int p_proto; /* protocol number */ } E.g. struct protoent *ptrp = getprotobyname("tcp"); int sock = socket(PF_INET, SOCK_STREAM, ptrp->p_proto);
Byte Ordering Network uses big endian Intel CPU uses little endian
Translation between Network and Host Byte Ordering in_addr_t htonl(in_addr_t hostLong) in_addr_t htons(in_port_t hostShort) in_addr_t ntohl(in_addr_t networkLong) in_addr_t ntohs(in_port_t networkShort) each of these functions performs a conversion between a host-format number and a network- format number.
Using read/write and/or send/recv ssize_t send(intsockfd, const void *buf, size_tlen, intflags); ssize_t write(int fd, const void *buf, size_t count); send( ) with flags==0 equivalent to write( ) ssize_t recv(intsockfd, void *buf, size_tlen, intflags); ssize_t read(int fd, void *buf, size_t count); recv( ) with flags==0 equivalent to read( )
Client for Daytime Server //------------------------------------------------------------------------ // Program: client // // Purpose: allocate a socket, connect to a server, and print all output // // Syntax: client host port // // host - name of a computer on which server is executing // port - protocol port number server is using // //------------------------------------------------------------------------ #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <netdb.h> #include <stdio.h>
Client for Daytime Server #include <string.h> #include <unistd.h> #include <stdlib.h> void printUsage() { printf( "Usage: client <host> <port> <name>\n"); printf( "\n"); printf( " host: host where the server is running.\n"); printf( " port: port that the server is using.\n"); printf( "\n"); printf( "Examples:\n"); printf( "\n"); printf( " client localhost 422422\n"); printf( " client lore 1345\n"); printf( "\n"); }
Client for Daytime Server int main(int argc, char **argv) { // Check command-line argument for protocol port and extract // host and port number if ( argc < 4 ) { printUsage(); exit(1); } char * host = argv[1]; int port = atoi(argv[2]); // Extract port number char * name = argv[3]; // Extract host name // Initialize socket address structure struct sockaddr_in socketAddress; // Clear sockaddr structure memset((char *)&socketAddress,0,sizeof(socketAddress));
Client for Daytime Server socketAddress.sin_family = AF_INET; // Set family to Internet if (port > 0) { // Test for port legal value socketAddress.sin_port = htons((u_short)port); } else { fprintf(stderr,"bad port number %s\n", argv[2]); exit(1); } // Get host table entry for this host struct hostent *ptrh = gethostbyname(host); if ( ptrh == NULL ) { fprintf(stderr, "invalid host: %s\n", host); perror("gethostbyname"); exit(1); } // Copy the host ip address to socket address structure memcpy(&socketAddress.sin_addr, ptrh->h_addr, ptrh->h_length);
Client for Daytime Server // Get TCP transport protocol entry struct protoent *ptrp = getprotobyname("tcp"); if ( ptrp == NULL ) { fprintf(stderr, "cannot map \"tcp\" to protocol number"); perror("getprotobyname"); exit(1); } // Create a tcp socket int sock = socket(PF_INET, SOCK_STREAM, ptrp->p_proto); if (sock < 0) { fprintf(stderr, "socket creation failed\n"); perror("socket"); exit(1); }
Client for Daytime Server // Connect the socket to the specified server if (connect(sock, (struct sockaddr *)&socketAddress, sizeof(socketAddress)) < 0) { fprintf(stderr,"connect failed\n"); perror("connect"); exit(1); } // In this application we don't need to send anything. // For your HTTP client you will need to send the request // as specified in the handout using send(). int m = read(sock,buffer,100); buffer[m]=0; printf("buffer=%s\n", buffer); write(sock, name, strlen(name)); write(sock,"\r\n",2);
Client for Daytime Server // Receive reply // Data received char buf[1000]; int n = recv(sock, buf, sizeof(buf), 0); while (n > 0) { // Write n characters to stdout write(1,buf,n); // Continue receiving more n = recv(sock, buf, sizeof(buf), 0); } close(sock); // Close the socket. // Terminate the client program gracefully. exit(0); }
The bind function associates a port for use by the socket int status = bind(sock, &addrport, size) where status: return status, 0 if successful, -1 otherwise sock: socket being used addrport: address structure This C structure has four parts to it (next slide) size: the size (in bytes) of the addrport structure Bind is non-blocking: returns immediately
Connection setup: listen & accept The listen function prepares a bound socket to accept incoming connections int status = listen(sock, queuelen) where status: return value, 0 if listening, -1 if error sock: socket being used queuelen: number of active participants that can wait for a connection listen is non-blocking: returns immediately Example code: if (listen(hServerSocket, 1) == -1) { printf("\nCould not listen\n"); return -1; }
Connection setup: listen & accept Use the accept function to accept a connection request from a remote host The function returns a socket corresponding to the accepted connection int s = accept(sock, &cliaddr, &addrlen) where s: new socket used for data-transfer sock: original socket being listened on (e.g., server) cliaddr: address structure of the active participant (e.g., client) The accept function updates/returns the sockaddr structure with the client's address addrlen: size (in bytes) of the client sockaddr structure The accept function updates/returns this value accept is blocking: waits for connection before returning Example code: hSocket = accept(hServerSocket, (struct sockaddr *) &Address, (socklen_t *) &nAddressSize); /* socklen_t is socket address length type, defined in sys/socket.h; in our example code it is being cast from a pointer to an integer */
Sending / Receiving Data Send data int count = send(int sock, const void * msg, int len, unsigned int falgs); Where: count: number of bytes transmitted (-1 if error) sock: socket being used buf: buffer to be transmitted len: length of buffer (in bytes) to transmit flags: special options, usually just 0 Receive data int count = recv(int sock, void *buf, int len, unsigned int flags); Where: count: number of bytes received (-1 if error) sock: socket being used buf: stores received bytes len: length of buf flags: special options, usually just 0
Example (Client/Server) // write a message to the server n = send(sock,buffer,strlen(buffer),0); // after the client executed a write(), it will read n = recv(newsock,buffer,255,0); // do some processing // do some processing // send the result to the client // read a message from the server n = recv(sock,buffer,255,0); n = send(newsock,resp_msg,strlen(resp_msg),0); SERVER CLIENT
close When finished using a socket, the socket should be closed: status = close(s); status: return value, 0 if successful, -1 if error s: the file descriptor (socket being closed)
Daytime Server const char * usage = " \n" "daytime-server: \n" " \n" "Simple server program that shows how to use socket calls \n" "in the server side. \n" " \n" "To use it in one window type: \n" " \n" " daytime-server <port> \n" " \n" "Where 1024 < port < 65536. \n" " \n" "In another window type: \n" " \n" " telnet <host> <port> \n" " \n" "where <host> is the name of the machine where daytime-server \n" "is running. <port> is the port number you used when you run \n" "daytime-server. \n" " \n" "Then type your name and return. You will get a greeting and \n" "the time of the day. \n" " \n";
Daytime Server #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <stdio.h> #include <time.h> int QueueLength = 5;
Daytime Server int main( int argc, char ** argv ) { // Print usage if not enough arguments if ( argc < 2 ) { fprintf( stderr, "%s", usage ); exit( -1 ); } // Get the port from the arguments int port = atoi( argv[1] ); // Set the IP address and port for this server struct sockaddr_in serverIPAddress; memset( &serverIPAddress, 0, sizeof(serverIPAddress) ); serverIPAddress.sin_family = AF_INET; serverIPAddress.sin_addr.s_addr = INADDR_ANY; serverIPAddress.sin_port = htons((u_short) port);
Daytime Server // Allocate a socket int masterSocket = socket(PF_INET, SOCK_STREAM, 0); if ( masterSocket < 0) { perror("socket"); exit( -1 ); } // Set socket options to reuse port. Otherwise we will // have to wait about 2 minutes before reusing the same port number int optval = 1; int err = setsockopt(masterSocket, SOL_SOCKET, SO_REUSEADDR, (char *) &optval, sizeof( int ) ); // Bind the socket to the IP address and port int error = bind( masterSocket, (struct sockaddr *)&serverIPAddress, sizeof(serverIPAddress) ); if ( error ) { perror("bind"); exit( -1 ); }
Daytime Server // Put socket in listening mode and set the // size of the queue of unprocessed connections error = listen( masterSocket, QueueLength); if ( error ) { perror("listen"); exit( -1 ); } while ( 1 ) { // Accept incoming connections struct sockaddr_in clientIPAddress; int alen = sizeof( clientIPAddress ); int slaveSocket = accept( masterSocket, (struct sockaddr *)&clientIPAddress, (socklen_t*)&alen);
Daytime Server if ( slaveSocket < 0 ) { perror( "accept" ); exit( -1 ); } // Process request. processTimeRequest( slaveSocket ); // Close socket close( slaveSocket ); } }
Daytime Server void processTimeRequest( int fd ) { // Buffer used to store the name received from the client const int MaxName = 1024; char name[ MaxName + 1 ]; int nameLength = 0; int n; // Send prompt const char * prompt = "\nType your name:"; write( fd, prompt, strlen( prompt ) ); // Currently character read unsigned char newChar; // Last character read unsigned char lastChar = 0;
Daytime Server // // The client should send <name><cr><lf> // Read the name of the client character by character until a // <CR><LF> is found. // while ( nameLength < MaxName && ( n = read( fd, &newChar, sizeof(newChar) ) ) > 0 ) { if ( lastChar == '\015' && newChar == '\012' ) { // Discard previous <CR> from name nameLength--; break; } name[ nameLength ] = newChar; nameLength++; lastChar = newChar; } // Add null character at the end of the string name[ nameLength ] = 0; printf( "name=%s\n", name );
Daytime Server // Get time of day time_t now; time(&now); char *timeString = ctime(&now); // Send name and greetings const char * hi = "\nHi "; const char * timeIs = " the time is:\n"; write( fd, hi, strlen( hi ) ); write( fd, name, strlen( name ) ); write( fd, timeIs, strlen( timeIs ) ); // Send the time of day write(fd, timeString, strlen(timeString)); // Send last newline const char * newline="\n"; write(fd, newline, strlen(newline)); }
Types of Server Concurrency Iterative Server Fork Process After Request Create New Thread After Request Pool of Threads Pool of Processes
Iterative Server void iterativeServer( int masterSocket) { while (1) { int slaveSocket =accept(masterSocket, &sockInfo, &alen); if (slaveSocket >= 0) { dispatchHTTP(slaveSocket); } } } Note: We assume that dispatchHTTP itself closes slaveSocket.
Fork Process After Request void forkServer( int masterSocket) { while (1) { int slaveSocket = accept(masterSocket, &sockInfo, &alen); if (slaveSocket >= 0) { int ret = fork(); ` if (ret == 0) { dispatchHTTP(slaveSocket); exit(0); } close(slaveSocket); } } }
Create Thread After Request void createThreadForEachRequest(int masterSocket) { while (1) { int slaveSocket = accept(masterSocket, &sockInfo, &alen); if (slaveSocket >= 0) { // When the thread ends resources are recycled pthread_attr_t attr; pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); pthread_create(&thread, &attr, dispatchHTTP, (void *) slaveSocket); } } }
Pool of Threads void poolOfThreads( int masterSocket ) { for (int i=0; i<4; i++) { pthread_create(&thread[i], NULL, loopthread, masterSocket); } loopthread (masterSocket); } void *loopthread (int masterSocket) { while (1) { int slaveSocket = accept(masterSocket, &sockInfo, &alen); if (slaveSocket >= 0) { dispatchHTTP(slaveSocket); } } }
Pool of Processes void poolOfProcesses( int masterSocket ) { for (int i=0; i<4; i++) { int pid = fork(); if (pid ==0) { loopthread (masterSocket); } } loopthread (masterSocket); } void *loopthread (int masterSocket) { while (1) { int slaveSocket = accept(masterSocket, &sockInfo, &alen); if (slaveSocket >= 0) { dispatchHTTP(slaveSocket); } } }
Notes: In Pool of Threads and Pool of processes, sometimes the OS does not allow multiple threads/processes to call accept() on the same masterSocket. In other cases it allows it but with some overhead. To get around it, you can add a mutex_lock/mutex_unlock around the accept call. mutex_lock(&mutex); int slaveSocket = accept(masterSocket, &sockInfo, 0); mutex_unlock(&mutex); In the pool of processes, the mutex will have to be created in shared memory.
Review Client/server call sequence for socket communication Types of server communication