Inter- Communication (IPC) • Data exchange techniques between Systems Programming III processes: (Pipes and Socket Programming) – Message passing: files, pipes, sockets – Shared-memory model Iqbal Mohomed CSC 209 – Summer 2004 Week 8

Some Limitations Pipes and File

• Limitations of files for inter-process data • A fork’d child inherits file descriptors from its exchange: parent –Slow! • Its possible to alter these using fclose() and • Limitations of pipes: fopen(): fclose(stdin); – two processes must be running on the same machine FILE* fp = fopen(“/tmp/junk”, “r”); – two processes communicating must be “related” • One could exchange two entries in the fd table by • Sockets overcome these limitations and are widely closing and reopening both streams, but there’s a used in more efficient way, using dup() or dup2()

dup() and dup2() pipe()

newFD = dup( oldFD); • The pipe() system call creates an internal system if (newFD < 0) {perror(“dup”);exit(1);} buffer and two file descriptors: one for reading • Or, to force the newFD to have a specific number: and one for writing returnCode = dup2(oldFD, newFD); • With a pipe, typically want the stdout of one if (returnCode < 0) {perror(“dup”);exit(1);} process to be connected to stdin of another process • In both cases, oldFD and newFD now refer to the same file … this is where dup2() becomes useful! • For dup2, if newFD is open, it is first automatically closed • Usage: • Note that dup() and dup2() refer to FDs and not streams int fd[2]; • A useful system call to convert a stream to a FD is: pipe(fd); /* fd[0] for reading; fd[1] for writing */ int fileno( FILE*fp); • Getting a stream from an FD: FILE* fdopen(int filedes,const char* mode);

1 werewolf:~/iqtesty% cat file1 Hello, my name is pipe()/dup2() example Iqbal. Output Hello, /* Equivalent to "sort < file1 | uniq*/ my #include name int main() is { Marc. int fd[2]; werewolf:~/iqtesty% sort

popen() and pclose() What are sockets?

• popen() simplifies the sequence of: • Sockets are an extension of pipes, with the – Generating a pipe advantages that processes don’t need to be – Forking a child process related, or even on the same machine – Duplicating file descriptors – Passing command execution via an exec() • A socket is like the end point of a pipe – in • Usage: fact, the kernel implements pipes as a FILE* popen(const char* command, const char* type); pair of sockets • Example: • Two (or more) sockets must be connected FILE* pipeFP; pipeFP = popen(“/usr/bin/ls *.c”,”r”); before they can be used to transfer data • Warning: popen() can return NULL too!

More on Sockets Connection-Oriented Paradigm

• Two main categories of socket types: SERVER CLIENT 1. Create a socket 1. Create a socket – The UNIX domain: both processes on same socket() socket() machine 2. Assign a name to the socket – The INET domain: processes on different bind() machines 3. Establish a queue for connections • Three main types of sockets: listen() SOCK_STREAM, SOCK_DGRAM, and 4. Extract connection from queue 2. Initiate a connection accept() Established! connect() SOCK_RAW … we’ll focus on 5. Communicate! 3. Communicate! SOCK_STREAM read() read() write() write()

2 Creating the Socket Bind to a name (Server Side) int socket(int family, int type, int protocol); int bind(int sockfd, const struct sockaddr • Family specifies the protocol family: *servaddr, socklen_t addrlen); – PF_INET – IPv4 • sockfd – returned by socket – PF_LOCAL – UNIX Domain • INET Address • Type: struct sockaddr_in{ short sin_family; /*PF_INET*/ – SOCK_STREAM, SOCK_DGRAM, SOCK_RAW u_short sin_port; • Protocol: struct in_addr sin_addr; – Set to 0 except for RAW sockets char sin_zero[8]; } • Returns a socket descriptor • sin_addr can be set to INADDR_ANY to communicate with any host

Set up queue (Server Side) Establish the connection (Server Side) int listen (int sockfd, int backlog) int accept(int sockfd, struct sockaddr* cliaddr, socklen_t * addrlen); • After calling listen, a socket is ready to accept • Blocks waiting for a connection (from the queue) connections • Returns a new descriptor which refers to the TCP • Prepares a queue in the kernel where partially connection with the client completed connections wait to be accepted • sockfd is the listening socket • backlog is the maximum number of partially • cliaddr is the address of the client completed connections that the kernel should • Reads and writes on the connection will use the queue socket returned by accept

Establish the connection (Client Side) INET vs UNIX Domain int connect(int sockfd, const struct sockaddr • The main difference is the bind()/connect command … in the UNIX *servaddr, socklen_t addrlen); domain, the socket name is a filename, but in the INET domain, the socket name is a machine name and port number: • The kernel will choose a dynamic port and static struct sockaddr_in serv_adr; memset(&serv_adr, 0, sizeof (serv_adr)); source IP address serv_adr.sin_family = AF_INET; serv_adr.sin_addr.s_addr = htonl(INADDR_ANY); serv_adr.sin_port = htons(6789); • Returns 0 on success and –1 on failure, VERSUS #define NAME “my_sock” setting errno static struct sockaddr_un serv_adr; serv_adr.sun_family = AF_UNIX; strcpy(serv_adr.sun_path,NAME); • AF_INET/PF_INET versus AF_UNIX/PF_UNIX • Client needs to know the machine name and port of the server: struct hostent *host; host = gethostbyname(“werewolf.cdf”);

3 Network byte order Port Numbers

• Intel CPUs are usually little-endian, and Sun Sparc • Well-known ports: 0 – 1023 CPUs are big-endian – 80: web • To communicate between machines with unknown or different “endian-ness” we convert numbers to – 22: ssh network byte order (big-endian) before we send – 23: telnet them. – 21: ftp • There are functions provided to do this: – unsigned long htonl(unsigned long) • Registered ports: 1024 – 49151 – unsigned short htons(unsigned short) • Private ports: 49152 - 65535 – unsigned long ntohl(unsigned long) – unsigned short ntohs(unsigned short)

4