Systems Programming III (Pipes and Socket Programming

)
Iqbal Mohomed CSC 209 – Summer 2004 Week 8

Inter-Process Communication (IPC)
• Data exchange techniques between processes:
– Message passing: files, pipes, sockets – Shared-memory model

Some Limitations • Limitations of files for inter-process data exchange: – Slow! • Limitations of pipes: – two processes must be running on the same machine – two processes communicating must be “related” • Sockets overcome these limitations and are widely used in Internet software .

but there’s a more efficient way. FILE* fp = fopen(“/tmp/junk”. “r”). using dup() or dup2() . • One could exchange two entries in the fd table by closing and reopening both streams.Pipes and File • A fork’d child inherits file descriptors from its parent • Its possible to alter these using fclose() and fopen(): fclose(stdin).

. if (returnCode < 0) {perror(“dup”).} • • • • In both cases. newFD).} • Or. if newFD is open. oldFD and newFD now refer to the same file For dup2. it is first automatically closed Note that dup() and dup2() refer to FDs and not streams A useful system call to convert a stream to a FD is: int fileno( FILE*fp). to force the newFD to have a specific number: returnCode = dup2(oldFD.exit(1).dup() and dup2() newFD = dup( oldFD). if (newFD < 0) {perror(“dup”).exit(1).

. /* fd[0] for reading. fd[1] for writing */ • Getting a stream from an FD: FILE* fdopen(int filedes. typically want the stdout of one process to be connected to stdin of another process … this is where dup2() becomes useful! • Usage: int fd[2].const char* mode).pipe() • The pipe() system call creates an internal system buffer and two file descriptors: one for reading and one for writing • With a pipe. pipe(fd).

close(fd[0]). pipe(fd).pipe()/dup2() example /* Equivalent to "sort < file1 | uniq*/ #include<stdio.(char*) 0). close(fd[0]). } .(char*)0).exit(3).fileno(stdout)). execl("/usr/bin/uniq".close(fd[1]). dup2(fileno(fp)."r"). } return 0.exit(2).fileno(stdin)). }else{ dup2(fd[0]."uniq".close(fd[1])."sort". fclose(fp). execl("/bin/sort". if (fork() == 0) { dup2(fd[1]. FILE *fp = fopen("file1". fileno(stdin)).h> int main() { int fd[2].

out Hello. my name is Marc. is is my my name name werewolf:~/iqtesty% sort < file1|uniq OR werewolf:~/iqtesty% . Iqbal. Marc. Marc. Hello. Iqbal.werewolf:~/iqtesty% cat file1 Hello. is my name Output . my name is Iqbal. werewolf:~/iqtesty% sort<file1 Hello. Hello./a.

• Warning: popen() can return NULL too! . pipeFP = popen(“/usr/bin/ls *.”r”).c”. • Example: FILE* pipeFP.popen() and pclose() • popen() simplifies the sequence of: – – – – Generating a pipe Forking a child process Duplicating file descriptors Passing command execution via an exec() • Usage: FILE* popen(const char* command. const char* type).

or even on the same machine • A socket is like the end point of a pipe – in fact. the UNIX kernel implements pipes as a pair of sockets • Two (or more) sockets must be connected before they can be used to transfer data .What are sockets? • Sockets are an extension of pipes. with the advantages that processes don’t need to be related.

and SOCK_RAW … we’ll focus on SOCK_STREAM .More on Sockets • Two main categories of socket types: – The UNIX domain: both processes on same machine – The INET domain: processes on different machines • Three main types of sockets: SOCK_STREAM. SOCK_DGRAM.

Establish a queue for connections listen() 4. Communicate! read() write() CLIENT 1. Extract connection from queue Established! accept() 5. Create a socket socket() 2.Connection-Oriented Paradigm SERVER 1. Create a socket socket() 2. Communicate! read() write() . Initiate a connection connect() 3. Assign a name to the socket bind() 3.

int protocol). SOCK_DGRAM.Creating the Socket int socket(int family. SOCK_RAW • Protocol: – Set to 0 except for RAW sockets • Returns a socket descriptor . • Family specifies the protocol family: – PF_INET – IPv4 – PF_LOCAL – UNIX Domain • Type: – SOCK_STREAM. int type.

/*PF_INET*/ u_short sin_port. struct in_addr sin_addr.Bind to a name (Server Side) int bind(int sockfd. } • sin_addr can be set to INADDR_ANY to communicate with any host . char sin_zero[8]. socklen_t addrlen). • sockfd – returned by socket • INET Address struct sockaddr_in{ short sin_family. const struct sockaddr *servaddr.

Set up queue (Server Side) int listen (int sockfd. int backlog) • After calling listen. a socket is ready to accept connections • Prepares a queue in the kernel where partially completed connections wait to be accepted • backlog is the maximum number of partially completed connections that the kernel should queue .

struct sockaddr* cliaddr. • Blocks waiting for a connection (from the queue) • Returns a new descriptor which refers to the TCP connection with the client • sockfd is the listening socket • cliaddr is the address of the client • Reads and writes on the connection will use the socket returned by accept . socklen_t * addrlen).Establish the connection (Server Side) int accept(int sockfd.

const struct sockaddr *servaddr. • The kernel will choose a dynamic port and source IP address • Returns 0 on success and –1 on failure.Establish the connection (Client Side) int connect(int sockfd. socklen_t addrlen). setting errno .

• • AF_INET/PF_INET versus AF_UNIX/PF_UNIX Client needs to know the machine name and port of the server: struct hostent *host. sizeof (serv_adr)). 0. . VERSUS #define NAME “my_sock” static struct sockaddr_un serv_adr.sun_family = AF_UNIX. host = gethostbyname(“werewolf.sun_path.sin_family = AF_INET.NAME). the socket name is a machine name and port number: static struct sockaddr_in serv_adr. serv_adr.INET vs UNIX Domain • The main difference is the bind()/connect command … in the UNIX domain.sin_addr.s_addr = htonl(INADDR_ANY). the socket name is a filename. serv_adr. strcpy(serv_adr. but in the INET domain. memset(&serv_adr. serv_adr.sin_port = htons(6789).cdf”). serv_adr.

• There are functions provided to do this: – – – – unsigned long htonl(unsigned long) unsigned short htons(unsigned short) unsigned long ntohl(unsigned long) unsigned short ntohs(unsigned short) . and Sun Sparc CPUs are big-endian • To communicate between machines with unknown or different “endian-ness” we convert numbers to network byte order (big-endian) before we send them.Network byte order • Intel CPUs are usually little-endian.

65535 .Port Numbers • Well-known ports: 0 – 1023 – – – – 80: web 22: ssh 23: telnet 21: ftp • Registered ports: 1024 – 49151 • Private ports: 49152 .