This article turns from the 5 pitfalls of Linux socket programming in IBM Blog.
"Developing reliable Web applications in a heterogeneous environment."
The Socket API is a standard API for practical application development in Web applications. Although the API is simple, novice developers may experience some common problems. This article identifies some of the most common pitfalls and shows you how to avoid them.
First introduced in the 4.2 BSD UNIX® operating system, the Sockets API is now the standard feature of any operating system. In fact, it's hard to find a modern language that doesn't support the Sockets API. The API is fairly straightforward, but new developers will still encounter some common pitfalls.
This article identifies those pitfalls and shows you how to avoid them.
hidden trouble 1. Ignore return status
The first pitfall is obvious, but it's the easiest mistake for a novice developer to make. If you ignore the return state of a function, you may get lost when they fail or partially succeed. This, in turn, can spread errors, making it difficult to locate the source of the problem.
Captures and checks each return state, rather than ignoring them. Consider the example shown in Listing 1, a socket send
function.
Listing 1. Ignore API function return status
1 intstatus, sock, mode;2 /*Create A new stream (TCP) socket*/3Sock = socket (af_inet, Sock_stream,0 );4 ...5Status =Send (sock, buffer, Buflen, msg_dontwait);6 if(Status = =-1) {7 /*Send failed*/8printf"send failed:%s\n", Strerror (errno));9}Else {Ten /*send succeeded--or did it?*/ One}
Listing 1 explores a function fragment that completes a socket send
operation (sending data through a socket). The error state of the function is captured and tested, but this example ignores send
an attribute under nonblocking mode (enabled by the MSG_DONTWAIT
flag).
send
There are three possible types of return values for API functions:
- Returns 0 if the data is successfully queued to the transmission queue.
- If the queue fails, 1 is returned (by using
errno
a variable to understand the cause of the failure).
- If not all characters can be queued when the function is called, the final return value is the number of characters sent.
Because send
of the MSG_DONTWAIT
non-blocking nature of the variable, the function call returns after all the data, some data, or no data is sent. Ignoring the return status here will result in incomplete sending and subsequent data loss.
Hidden Trouble 2. Peer socket Closure
The funny side of UNIX is that you can almost think of anything as a file. The files themselves, directories, pipelines, devices, and sockets are treated as files. This is a novel abstraction, meaning that a complete set of APIs can be used on a wide range of device types.
Consider the read
API function, which reads a certain number of bytes from a file. read
The function returns the number of bytes read (up to the maximum value you specify), or 1, which indicates an error, or 0 if the end of the file has been reached.
If you complete an operation on a socket read
and get a return value of 0, this indicates that the peer layer on the remote socket side calls the close
API method. The indication is the same as the file read-no extra data can be read by descriptor (see Listing 2).
Listing 2. Properly handle the return value of the Read API function
1 intsock, status;2Sock = socket (af_inet, Sock_stream,0 );3 ...4Status =read (sock, buffer, buflen);5 if(Status >0) {6 /*Data read from the socket*/7}Else if(Status = =-1) {8 /*Error, check errno, take action ...*/9}Else if(Status = =0) {Ten /*Peer closed the socket, finish the close*/ One Close (sock); A /*further processing ...*/ -}
Similarly, you can use write
API functions to probe the closure of a peer socket. In this case, the signal is received SIGPIPE
, or if the signal is blocked, the write
function returns 1 and errno
is set to EPIPE
.
hidden Trouble 3. Address usage error (eaddrinuse)
You can use an bind
API function to bind an address (an interface and a port) to a socket endpoint. You can use this function in server settings to limit the interfaces that may come with the connection. You can also use this function in client settings to limit the interfaces that should be used for connections that should be made available. The bind
most common usage is to associate the port number and server, and use a wildcard address ( INADDR_ANY
), which allows any interface to be used for incoming connections.
bind
The common problem is trying to bind a port that is already in use. The trap is that there may not be an active socket, but it is still forbidden to bind the port ( bind
returned EADDRINUSE
), which is caused by the TCP socket state TIME_WAIT
. The status is retained for approximately 2-4 minutes after the socket is closed. TIME_WAIT
after the status exits, the socket is deleted and the address can be re-bound without problems.
Waiting for TIME_WAIT
the end can be annoying, especially if you are developing a socket server, you need to stop the server to make some changes, and then restart. Fortunately, there are ways to avoid the TIME_WAIT
state. You can apply SO_REUSEADDR
socket options to sockets so that ports can be reused immediately.
Consider the example in Listing 3. Before binding the address, I invoke it as an SO_REUSEADDR
option setsockopt
. To allow address reuse, I set the integer parameter ( on
) to 1 (otherwise, it can be set to zero address reuse).
Listing 3. Use SO_REUSEADDR socket option to avoid address usage errors
1 intSock, ret, on;2 structsockaddr_in servaddr;3 /*Create A new stream (TCP) socket*/4Sock = socket (af_inet, Sock_stream,0 ):5 /*Enable Address Reuse*/6On =1;7ret = setsockopt (sock, Sol_socket, SO_REUSEADDR, &on,sizeof(on));8 /*Allow connections to port 8080 from any available interface*/9memset (&SERVADDR,0,sizeof(SERVADDR));Tenservaddr.sin_family =af_inet; OneSERVADDR.SIN_ADDR.S_ADDR =htonl (inaddr_any); AServaddr.sin_port = htons (45000 ); - /*Bind to the address (Interface/port)*/ -ret = bind (sock, (structSOCKADDR *) &servaddr,sizeof(SERVADDR));
After the option has been applied SO_REUSEADDR
, the bind
API function will allow immediate reuse of the address.
Hidden Trouble 4. Send structured data
Sockets are the perfect tool for sending unstructured binary byte streams or ASCII traffic (such as HTTP pages on HTTP, or e-mail messages on SMTP). But if you try to send binary data on a socket, things will get more complicated.
For example, you want to send an integer: You can be sure that the receiver will use the same way to interpret the integer? Applications running on the same schema can rely on their common platform to make the same interpretation of that type of data. But what happens if a client running on a high-priority IBM PowerPC sends a 32-bit integer to a low-priority Intel x86? BYTE permutations will cause an incorrect explanation.
Byte swap or not?
Endianness refers to the order in which bytes in memory are arranged. The high priority (big endian) is ranked by the most significant byte, whereas the low priority (little endian) is sorted by the least significant byte in front.
High-priority architectures (such as powerpc®) have an advantage over low-priority architectures such as the Intel®pentium® family, whose network byte order is high priority. This means that for high-priority machines, the control of data within TCP/IP is natural and orderly. Low-priority architectures require byte swapping-a slight performance weakness for network applications.
What happens when you send a C structure through a socket? Here, too, there are problems, because not all compilers arrange the elements of a structure in the same way. The structure may also be compressed to minimize wasted space, which further causes the elements in the structure to be misaligned.
Fortunately, there are solutions to this problem that ensure consistent interpretation of data at both ends. In the past, the remote procedure call (Procedure Call,rpc) Suite tool provided the so-called external data representation (External data representation,xdr). XDR defines a standard representation of the data to support the development of heterogeneous Network application communications.
Now, there are two new protocols that provide similar functionality. Extensible Markup Language/Remote Procedure call (XML/RPC) arranges procedure calls on HTTP in XML format. Data and metadata are encoded in XML and transmitted as strings, and the values are separated from their physical representations through the host schema. SOAP follows Xml-rpc, extending its thinking with better features and functionality. See the Resources section for more information on each protocol.
hidden Trouble 5. Frame synchronization assumptions in TCP
TCP does not provide frame synchronization, which makes it perfect for byte-stream-oriented protocols. This is an important difference between TCP and UDP (user Datagram Protocol, Subscriber Datagram Protocol). UDP is a message-oriented protocol that preserves message boundaries between senders and receivers. TCP is a stream-oriented protocol that assumes that the data being communicated is unstructured, as shown in 1.
Figure 1. UDP frame synchronization capability and lack of frame synchronization for TCP
The upper part of Figure 1 illustrates a UDP client and server. The left peer layer completes the write operation of two sockets, each 100 bytes. The UDP layer of the protocol stack tracks the number of writes and ensures that when the receiver on the right gets the data through the socket, it arrives in the same number of bytes. In other words, the message boundaries provided by the writer are reserved for the reader.
Now, look at the bottom of Figure 1. It demonstrates the same granularity of write operations for the TCP layer. Two separate write operations (100 bytes each) are written to the stream socket. But in this case, the reader of the stream socket gets 200 bytes. The TCP layer of the protocol stack aggregates two write operations. This aggregation can occur on either the sender or the receiver of the TCP/IP protocol stack. It is important to note that aggregations may not occur--tcp only ensure that the data is sent in an orderly manner.
For most developers, this trap can cause confusion. You want to obtain TCP reliability and frame synchronization for UDP. Application layer developers are required to implement buffering and staging functions unless other transport protocols, such as streaming Transmission Control Protocol (STCP), are used instead.
Tools for debugging socket applications
Gnu/linux provides several tools that can help you discover some of the problems in your socket application. In addition, using these tools is instructive and can help explain the behavior of the application and the TCP/IP protocol stack. Here, you will see an overview of several tools. Check out the resources below for more information.
View details of the network subsystem
netstat
The tool provides the ability to view the Gnu/linux network subsystem. netstat
, you can view the currently active connection (viewed as a single protocol), view a connection for a particular state (such as a server socket in the listening state), and many other information. Listing 4 shows netstat
some of the options provided and the attributes they enable.
Listing 4. Netstat usage patterns for utility programs
1 View all TCP sockets currently active2$ netstat--TCP3 View all UDP sockets4$ netstat--UDP5View all TCP socketsinchThe listening state6$ netstat--Listening7 View The multicast group membership information8$ netstat--groups9 Display The list of masqueraded connectionsTen$ netstat--Masquerade OneView Statistics forEach protocol A$ netstat--statistics
Although there are many other utilities, netstat
the functionality is comprehensive and covers the route
ifconfig
functionality of, and other standard gnu/linux tools.
Monitor traffic
You can use several tools from Gnu/linux to check for low-level traffic on your network. tcpdump
tool is an older tool that is "sniffing" network packets from the Internet, printing to stdout
or recording in a file. This feature allows you to view the traffic generated by your application and the low-level flow control mechanism generated by TCP. A tcpflow
new tool called and tcpdump
complements it provides protocol flow analysis and methods for properly refactoring data streams, regardless of the order or the re-delivery of the packets. Listing 5 shows tcpdump
the two usage patterns.
Listing 5. Usage patterns for Tcpdump tools
1Display all traffic on the eth0Interface forThe Local host2$ tcpdump-l-i eth03Show all traffic on the network coming fromor going to host Plato4 $ tcpdump Host Plato5Show All HTTP traffic forhost Camus6 $ tcpdump Host Camus and (port http)7View Traffic Coming fromor going to TCP port45000On the Local host8$ tcpdump TCP port45000
tcpdump
and tcpflow
tools have a number of options, including the ability to create complex filter expressions. Refer to the resources below for more information on these tools.
tcpdump
And tcpflow
both are text-based command-line tools. If you prefer a graphical user interface (GUI), there is an open source tool that Ethereal
might suit your needs. Ethereal
is a professional protocol analysis software that can help debug application layer protocols. Its plug-in architecture (plug-in architecture) can decompose protocols such as HTTP and any protocol you can think of (there are 637 protocols for writing this article).
Summary
Socket programming is easy and fun, but you want to avoid introducing errors or at least making them easier to find, this requires considering the 5 common pitfalls described in this article, and using standard, error-proof programming practices. Gnu/linux tools and utilities can also help identify minor problems in some programs. Remember: keep track of relevant or "see" tools when viewing the Help manual for the utility. You may find a new tool that is necessary.
5 Pitfalls in Linux Socket programming (RPM)