Informasi Dokumen
- Penulis:
- Warren W. Gay
- Mata Pelajaran: Computer Networking
- Topik: Linux Socket Programming
- Tipe: Book
- Tahun: 2000
Ringkasan Dokumen
I. Introducing Sockets
This chapter provides a foundational understanding of sockets, beginning with a brief historical overview tracing their development from the early days of ARPAnet and the challenges of inter-computer communication to the emergence of TCP/IP and the BSD socket interface, which heavily influences the Linux implementation. The chapter then defines sockets as endpoints in a communication line, analogous to telephones, and emphasizes that unlike pipes, sockets facilitate bidirectional communication. The core concept of socket referencing within the Linux kernel and application programs via file descriptors is introduced, highlighting the similarities and differences between sockets and regular files. The chapter concludes with an example program demonstrating the creation of a socket pair using socketpair(2), showcasing how sockets are allocated file unit numbers similarly to opened files and illustrating their use in basic I/O operations.
1.1 A Brief Historical Introduction
This subsection details the historical context of socket development, starting from the Sputnik launch and the formation of ARPA. It explains the challenges of communication between diverse computer systems and the evolution of ARPAnet into the modern internet. The crucial role of UCB in developing BSD UNIX, which incorporated the TCP/IP implementation forming the basis of modern socket interfaces, is also emphasized. The timeline illustrates the development of the socket interface, connecting it to significant milestones in networking history, and highlights the legacy that Linux inherits.
1.2 Understanding Sockets
This section defines sockets as endpoints in communication channels, drawing an analogy to telephones. It explains how sockets possess network addresses, allowing programs to establish communication between local and remote endpoints. The section also contrasts sockets with pipes, highlighting the bidirectional nature of socket communication compared to the unidirectional flow in pipes. This distinction lays the foundation for understanding the versatility of sockets in network programming.
1.3 Defining a Socket
This subsection focuses on the core definition of a socket, comparing it to a telephone in terms of having two endpoints and a communication line in between. The concept of network addresses for sockets is introduced, contrasting them with telephone numbers. It sets the stage for the later chapters where the details of socket addresses and their structure are explored. The section underscores the fundamental role of sockets as endpoints in establishing communication links.
1.4 Using Sockets
The subsection clarifies how Linux handles sockets in a manner similar to file descriptors. It explains how the same file I/O functions (read(2), write(2), close(2)) can operate on sockets, emphasizing the seamless integration of sockets into the file system's interface. The section also highlights key differences between sockets and regular files (e.g., inability to lseek(2) on a socket, presence of socket addresses, and specific socket options). The concept of file unit numbers as references to both files and sockets is explained to illustrate the flexibility offered by this approach.
1.5 Referencing Sockets
This subsection explains how the Linux kernel allocates file descriptors (unit numbers) to both files and sockets, emphasizing the interchangeable nature of these references. The allocation process, whereby the lowest available file descriptor is assigned to the next open file or socket, is described. An example sequence of file and socket openings illustrates how the kernel manages these unit numbers, providing a practical understanding of socket referencing within the system.
1.6 Comparing Sockets to Pipes
This subsection contrasts sockets with pipes, focusing on the unidirectional nature of pipes versus the bidirectional nature of sockets. It explains how pipe(2) creates a one-way communication channel, while sockets enable data transmission in both directions. This comparison highlights a critical distinction in functionality that influences the choice between pipes and sockets for specific programming tasks. This section also showcases the pipe(2) function synopsis to illustrate the difference in the returned file descriptors.
1.7 Creating Sockets
This section introduces the socketpair(2) function, detailing its arguments (domain, type, protocol, and the array to receive file descriptors). It explains the significance of the AF_LOCAL domain and the options for socket type (SOCK_STREAM, SOCK_DGRAM), although a full explanation is deferred to later chapters. The importance of checking the return value of socketpair(2) for success or failure and consulting errno in case of error is emphasized. The section provides the complete function synopsis, ready for use in later programming examples.
1.8 Using socketpair(2) in an Example
This subsection presents a practical example (Listing 1.1) demonstrating the use of socketpair(2). It explains the code step-by-step, including the declaration of the socket array, the socketpair(2) function call, error handling, and reporting of the returned file descriptors. The use of netstat (or lsof) to verify the creation of the sockets is explained, along with instructions for compilation using a provided Makefile. Alternative commands for older Linux versions are suggested to cater to potential compatibility issues.
1.9 Running the Demonstration Program
This subsection provides instructions for running the example program and interprets the output from the program and netstat. The output shows the process ID and file descriptors used, visually confirming the socket creation and assignment of file unit numbers. The section highlights the importance of observing the case-sensitivity of file names when executing the program. Analysis of the netstat output demonstrates the practical application of the example.
1.10 Performing I/O on Sockets
This subsection demonstrates basic I/O operations on sockets using read(2) and write(2). It begins by reviewing the function synopses of read(2), write(2), and close(2), reinforcing concepts already familiar to the reader. Listing 1.2 shows an example program that sends and receives messages over the socket pair, confirming that data flows bidirectionally. Detailed step-by-step explanations of the code are provided, including error handling.
1.11 Closing Sockets
This section explains the implications of closing sockets, particularly the need for a graceful shutdown to avoid problems. The shutdown(2) function and its arguments are introduced, allowing for partial shutdown (of reading or writing). The different how values (SHUT_RD, SHUT_WR, SHUT_RDWR) are explained in detail. The use of shutdown(2) is described in terms of sending an end-of-file condition to the remote socket without closing the local socket for receiving confirmation messages. The importance of proper shutdown is stressed through code snippets showcasing both scenarios where the socket is fully closed and where it is partially closed using the shutdown(2) function.
1.12 The shutdown(2) Function
This subsection details the shutdown(2) function, explaining its arguments (socket descriptor and shutdown mode) and return value. Table 1.1 summarizes the permissible values for the shutdown mode (how), providing a clear reference for programmers. It emphasizes the use of shutdown(2) to gracefully close the socket, allowing for controlled termination of communication channels.
1.13 Shutting Down Writing to a Socket
This subsection showcases a code example illustrating how to use shutdown(2) to prevent further writes to a socket (SHUT_WR). It explains the benefits of this approach: sending an EOF indication to the remote end, keeping the socket open for reading, and resolving issues with multiple open references (duplicated sockets).
1.14 Dealing with Duplicated Sockets
This section explains the behavior of close(2) when multiple file descriptors refer to the same socket (via dup(2) or dup2(2)). It clarifies that only the last close(2) call actually closes the socket. The code examples illustrate how shutdown(2) provides a more reliable way to close a socket, regardless of the number of open references. This emphasizes the need for understanding how the operating system handles duplicate file descriptors in the context of network programming.
II. Domains and Address Families
This chapter delves into the concept of socket domains and address families, providing a detailed explanation of how to form socket addresses. It starts by introducing nameless sockets and anonymous calls, followed by an exploration of various address families, including internet (IPv4) socket addresses and other address families. The critical topic of network byte order and endian conversions is addressed, providing the necessary background for working with different architectures. The chapter provides practical examples of forming various types of socket addresses, enabling readers to handle diverse networking scenarios.
III. Address Conversion Functions
This chapter focuses on functions for converting between different representations of IP addresses. It covers topics such as internet IP numbers, address classes, and netmask values. It provides a comprehensive explanation of functions like inet_addr(3), inet_aton(3), inet_ntoa(3), inet_network(3), inet_lnaof(3), inet_netof(3), and inet_makeaddr(3), illustrating their usage with practical examples. The chapter equips readers with the essential tools to manipulate and interpret IP addresses effectively.
IV. Socket Types and Protocols
This chapter explores the different types of sockets and protocols available in Linux. It discusses the use of the PF_LOCAL and AF_LOCAL macros and the socket(2) function, emphasizing how to choose appropriate socket types (SOCK_STREAM, SOCK_DGRAM, SOCK_SEQPACKET) and protocols based on specific networking requirements. The chapter offers clear explanations of each socket type and provides examples of their usage with various protocols, providing a solid understanding of how to select the appropriate socket type and protocol for a given networking task. This chapter focuses on the implications of choosing a specific socket type and protocol and covers various options for socket(2).
V. Binding Addresses to a Socket
This chapter explains the purpose and usage of the bind(2) function, demonstrating how to associate a socket with a specific network address. It covers the process of obtaining socket addresses, including creating a custom sock_addr() function. The chapter also delves into the relationship between interfaces and addressing, showcasing examples of specifying interface addresses and binding to any interface. Understanding the process of binding addresses ensures that network programs operate correctly and can be reached by other systems.
VI. Connectionless-Oriented Protocols
This chapter focuses on connectionless communication using protocols like UDP. It explores the advantages and disadvantages of connectionless communication, highlighting the differences compared to connection-oriented protocols. It provides a comprehensive explanation of sendto(2) and recvfrom(2), including examples of a UDP datagram server and client. The chapter also addresses scenarios such as sending to a wild address and leaving out bind(2) in client programs.
VII. Connection-Oriented Protocols for Clients
This chapter covers connection-oriented communication using protocols like TCP, focusing on the client-side perspective. It explains the advantages of TCP/IP, emphasizing its handling of lost, duplicated, and out-of-order packets. The chapter details the use of /etc/services and the functions getservent(3), setservent(3), and endservent(3) for service lookups. It also discusses /etc/protocols and the functions setprotoent(3) and endprotoent(3) for protocol lookups. Finally, it presents a practical example of a TCP/IP client program, illustrating the use of the connect(2) function.
VIII. Connection-Oriented Protocols for Servers
This chapter focuses on the server-side aspects of connection-oriented communication using TCP. It details the role of the server and explains the listen(2) function, including the concept of the connect queue and the backlog parameter. It introduces the accept(2) function and its role in accepting incoming connections. A practical example of a TCP/IP server program is presented, showcasing the implementation of a server that can handle multiple clients, demonstrating the fundamental concepts of server-side socket programming.
IX. Hostname and Network Name Lookups
This chapter addresses the essential task of resolving hostnames and network names to IP addresses. It explains the use of the uname(2) function and demonstrates obtaining hostnames and domain names using gethostname(2) and getdomainname(2). It provides a detailed explanation of gethostbyname(3) and gethostbyaddr(3) for resolving remote addresses, including error handling. The chapter also covers the functions sethostent(3) and endhostent(3) for managing the host entry database.
X. Using Standard I/O on Sockets
This chapter explores using standard I/O functions with sockets, explaining the need for this approach. It shows how to associate a socket with a stream using fdopen(3) and illustrates handling separate read and write streams. It also demonstrates duplicating sockets, closing streams, and handling interrupts with EINTR. The chapter includes a practical example of applying FILE streams to sockets, including code for an RPN calculator server. This showcases how to leverage standard I/O for improved code readability and easier interaction with socket data.
XI. Concurrent Client Servers
This chapter delves into techniques for handling multiple clients concurrently. It provides an overview of server functions and showcases the use of fork(2) to create child processes for servicing individual clients. It discusses the challenges of designing servers for handling multiple concurrent clients efficiently and explains how to overcome them. The chapter introduces the select(2) function and demonstrates its application in building a server that can handle multiple clients concurrently.
XII. Socket Options
This chapter covers socket options, explaining how to retrieve and set socket options using getsockopt(2) and setsockopt(2). It provides detailed explanations and examples for various options, including SO_TYPE, SO_REUSEADDR, SO_LINGER, SO_KEEPALIVE, SO_BROADCAST, SO_OOBINLINE, SO_PASSCRED, and SO_PEERCRED. Mastering socket options is critical for fine-tuning network applications and handling various networking scenarios effectively.
XIII. Broadcasting with UDP
This chapter explores UDP broadcasting, explaining broadcast addresses and how to broadcast on 255.255.255.255. It presents examples of broadcasting from a server and receiving broadcasts, including broadcasting to a specific network. The chapter provides practical examples of broadcasting and receiving, including troubleshooting hints to help readers debug their broadcast-based programs.
XIV. Out-of-Band Data
This chapter addresses the handling of out-of-band data, explaining its importance and how it is handled using sockets. It discusses variations in implementation and details techniques for sending and receiving out-of-band data using appropriate functions. The chapter explains the role of the SIGURG signal and provides practical examples demonstrating the use of out-of-band data, including troubleshooting common issues and limitations.
XV. Using the inetd Daemon
This chapter introduces the inetd daemon and its role in managing network services. It outlines steps common to most servers and explains how to configure inetd using the /etc/inetd.conf file. The chapter presents examples of implementing stream TCP and datagram servers with inetd, covering the concepts of wait and nowait and showcasing how to enable and disable services using inetd. Understanding inetd allows for efficient management of network services.
XVI. Network Security Programming
This chapter focuses on incorporating security measures into network programming. It discusses the importance of security and the challenges involved in securing network applications. It explores techniques for securing inetd servers and introduces the TCP Wrapper concept, providing a method for controlling access to network services based on IP addresses. The chapter includes examples of implementing and testing TCP Wrappers, emphasizing the importance of security logging.
XVII. Passing Credentials and File Descriptors
This chapter addresses advanced techniques for passing credentials and file descriptors between processes using ancillary data. It introduces the concepts of ancillary data, I/O vectors (struct iovec), and the sendmsg(2) and recvmsg(2) functions. The chapter includes a practical example demonstrating how to pass file descriptors between a client and a server, highlighting the structures involved (struct msghdr, struct cmsghdr) and the use of cmsg(3) macros.
XVIII. A Practical Network Project
This chapter presents a comprehensive network project that ties together many of the concepts and techniques learned throughout the book. It involves creating a stock quote service, including both a server and a client. The chapter details the design and implementation of both components, explaining how data is fetched, processed, and broadcast. This final project provides a practical application of the material covered and showcases the power of socket programming in building real-world network applications.