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 .

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

newFD).exit(1). to force the newFD to have a specific number: returnCode = dup2(oldFD. if (returnCode < 0) {perror(“dup”). .dup() and dup2() newFD = dup( oldFD). 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). if (newFD < 0) {perror(“dup”). if newFD is open.} • • • • In both cases.exit(1).} • Or.

.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). /* fd[0] for reading.const char* mode). 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]. fd[1] for writing */ • Getting a stream from an FD: FILE* fdopen(int filedes.

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

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

pipeFP = popen(“/usr/bin/ls *.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.c”. const char* type). • Warning: popen() can return NULL too! . • Example: FILE* pipeFP.”r”).

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 . or even on the same machine • A socket is like the end point of a pipe – in fact.What are sockets? • Sockets are an extension of pipes. with the advantages that processes don’t need to be related.

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. and SOCK_RAW … we’ll focus on SOCK_STREAM .

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

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

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

int backlog) • After calling listen.Set up queue (Server Side) int listen (int sockfd. 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 .

socklen_t * addrlen).Establish the connection (Server Side) int accept(int sockfd. • 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 . struct sockaddr* cliaddr.

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

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

• 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 .

Sign up to vote on this title
UsefulNot useful