﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-分享知识-随笔分类-Unix网络编程第三版,卷一:套接字联网API学习笔记</title><link>http://www.cppblog.com/sscchh-2000/category/1672.html</link><description>与大家一起分享知识</description><language>zh-cn</language><lastBuildDate>Thu, 22 May 2008 18:33:12 GMT</lastBuildDate><pubDate>Thu, 22 May 2008 18:33:12 GMT</pubDate><ttl>60</ttl><item><title>Unix网络编程第三版,卷一:套接字联网API学习笔记(3)</title><link>http://www.cppblog.com/sscchh-2000/archive/2006/05/17/7292.html</link><dc:creator>史传红</dc:creator><author>史传红</author><pubDate>Wed, 17 May 2006 01:42:00 GMT</pubDate><guid>http://www.cppblog.com/sscchh-2000/archive/2006/05/17/7292.html</guid><wfw:comment>http://www.cppblog.com/sscchh-2000/comments/7292.html</wfw:comment><comments>http://www.cppblog.com/sscchh-2000/archive/2006/05/17/7292.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/sscchh-2000/comments/commentRss/7292.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/sscchh-2000/services/trackbacks/7292.html</trackback:ping><description><![CDATA[TCP连接的建立和终止<br /><br />为了帮助我们理解conncet，accept，close这几个函数，以及使用netstat工具来调试TCP应用程序，我们必须理解TCP连接是如何建立和终止的和TCP状态转换图。<br /><br /><a name="ch02lev2sec1"></a><h4 class="docSection2Title">三次握手</h4><p class="docText">当一个TCP连接建立时，发生了以下场景：</p><a name="ch02pro01"></a><span style="FONT-WEIGHT: bold"><ol class="docList"><li><span style="FONT-WEIGHT: normal" value="1"><p class="docText">The server must be prepared to accept an incoming connection. This is normally done by calling <tt>socket</tt>, <tt>bind</tt>, and <tt>listen</tt> and is called a <span class="docEmphasis">passive open</span>.</p></span></li><li><span style="FONT-WEIGHT: normal" value="2"><p class="docText">The client issues an <span class="docEmphasis">active open</span> by calling <tt>connect</tt>. This causes the client TCP to send a "synchronize" (SYN) segment, which tells the server the client's initial sequence number for the data that the client will send on the connection. Normally, there is no data sent with the SYN; it just contains an IP header, a TCP header, and possible TCP options (which we will talk about shortly).</p></span></li><li><span style="FONT-WEIGHT: normal" value="3"><p class="docText">The server must acknowledge (ACK) the client's SYN and the server must also send its own SYN containing the initial sequence number for the data that the server will send on the connection. The server sends its SYN and the ACK of the client's SYN in a single segment.</p></span></li><li><span style="FONT-WEIGHT: normal" value="4"><p class="docText">The client must acknowledge the server's SYN.</p></span></li></ol></span><p class="docText">最少需要三次包交换，因此称作TCP的三次握手，如下图所示：<br /><a name="ch02fig02"></a>图示： TCP 的三次握手</p><p class="docText"><img height="192" alt="graphics/02fig02.gif" src="mk:@MSITStore:E:\正在看的书籍\UNIX%20Network%20Programming%20Volume[1].1%20The%20Sockets%20Networking%20API.chm::/FILES/02fig02.gif" width="500" border="0" /></p><p class="docText">We show the client's initial sequence number as <span class="docEmphasis">J</span> and the server's initial sequence number as <span class="docEmphasis">K</span>. The acknowledgment number in an ACK is the next expected sequence number for the end sending the ACK. Since a SYN occupies one byte of the sequence number space, the acknowledgment number in the ACK of each SYN is the initial sequence number plus one. Similarly, the ACK of each FIN is the sequence number of the FIN plus one.</p><blockquote><p></p><p class="docList">An everyday analogy for establishing a TCP connection is the telephone system [Nemeth 1997]. The <tt>socket</tt> function is the equivalent of having a telephone to use. <tt>bind</tt> is telling other people your telephone number so that they can call you. <tt>listen</tt> is turning on the ringer so that you will hear when an incoming call arrives. <tt>connect</tt> requires that we know the other person's phone number and dial it. <tt>accept</tt> is when the person being called answers the phone. Having the client's identity returned by <tt>accept</tt> (where the identify is the client's IP address and port number) is similar to having the caller ID feature show the caller's phone number. One difference, however, is that <tt>accept</tt> returns the client's identity only after the connection has been established, whereas the caller ID feature shows the caller's phone number before we choose whether to answer the phone or not. If the DNS is used (<a class="docLink" href="mk:@MSITStore:E:\正在看的书籍\UNIX%20Network%20Programming%20Volume[1].1%20The%20Sockets%20Networking%20API.chm::/0131411551_ch11.html#ch11">Chapter 11</a>), it provides a service analogous to a telephone book. <tt>getaddrinfo</tt> is similar to looking up a person's phone number in the phone book. <tt>getnameinfo</tt> would be the equivalent of having a phone book sorted by telephone numbers that we could search, instead of a book sorted by name.</p><p></p></blockquote><a name="ch02lev2sec2"></a><h4 class="docSection2Title">TCP 选项</h4><h4 class="docSection2Title">Each SYN can contain TCP options. Commonly used options include the following:</h4><ul><li><p class="docText"><span class="docEmphRoman">MSS option.</span> With this option, the TCP sending the SYN announces its <span class="docEmphasis">maximum segment size</span>, the maximum amount of data that it is willing to accept in each TCP segment, on this connection. The sending TCP uses the receiver's MSS value as the maximum size of a segment that it sends. We will see how to fetch and set this TCP option with the <tt>TCP_MAXSEG</tt> socket option (<a class="docLink" href="mk:@MSITStore:E:\正在看的书籍\UNIX%20Network%20Programming%20Volume[1].1%20The%20Sockets%20Networking%20API.chm::/0131411551_ch07lev1sec9.html#ch07lev1sec9">Section 7.9</a>).</p></li><li><p class="docText"><span class="docEmphRoman">Window scale option.</span> The maximum window that either TCP can advertise to the other TCP is 65,535, because the corresponding field in the TCP header occupies 16 bits. But, high-speed connections, common in today's Internet (45 Mbits/sec and faster, as described in RFC 1323 [Jacobson, Braden, and Borman 1992]), or long delay paths (satellite links) require a larger window to obtain the maximum throughput possible. This newer option specifies that the advertised window in the TCP header must be scaled (left-shifted) by 0–14 bits, providing a maximum window of almost one gigabyte (65,535 x 2<sup>14</sup>). Both end-systems must support this option for the window scale to be used on a connection. We will see how to affect this option with the <tt>SO_RCVBUF</tt> socket option (<a class="docLink" href="mk:@MSITStore:E:\正在看的书籍\UNIX%20Network%20Programming%20Volume[1].1%20The%20Sockets%20Networking%20API.chm::/0131411551_ch07lev1sec5.html#ch07lev1sec5">Section 7.5</a>).</p><blockquote><p></p><p class="docList">To provide interoperability with older implementations that do not support this option, the following rules apply. TCP can send the option with its SYN as part of an active open. But, it can scale its windows only if the other end also sends the option with its SYN. Similarly, the server's TCP can send this option only if it receives the option with the client's SYN. This logic assumes that implementations ignore options that they do not understand, which is required and common, but unfortunately, not guaranteed with all implementations.</p><p></p></blockquote></li><li><p class="docText"><span class="docEmphRoman">Timestamp option.</span> This option is needed for high-speed connections to prevent possible data corruption caused by old, delayed, or duplicated segments. Since it is a newer option, it is negotiated similarly to the window scale option. As network programmers there is nothing we need to worry about with this option.</p></li></ul><p class="docText">These common options are supported by most implementations. The latter two are sometimes called the "RFC 1323 options," as that RFC [Jacobson, Braden, and Borman 1992] specifies the options. They are also called the "long fat pipe options," since a network with either a high bandwidth or a long delay is called a <span class="docEmphasis">long fat pipe</span>. Chapter 24 of TCPv1 contains more details on these options.</p><a name="ch02lev2sec3"></a><h4 class="docSection2Title">TCP 连接的终止</h4><p class="docText">TCP建立时需要三次通知，而终止一个TCP连接时需要四次通知。<br /><a name="ch02pro02"></a><span style="FONT-WEIGHT: bold"><span style="FONT-WEIGHT: normal" value="1">One application calls <tt>close</tt> first, and we say that this end performs the <span class="docEmphasis">active close</span>. This end's TCP sends a FIN segment, which means it is finished sending data.</span></span></p><ol class="docList"><li><span style="FONT-WEIGHT: normal" value="2"><p class="docText">The other end that receives the FIN performs the <span class="docEmphasis">passive close</span>. The received FIN is acknowledged by TCP. The receipt of the FIN is also passed to the application as an end-of-file (after any data that may have already been queued for the application to receive), since the receipt of the FIN means the application will not receive any additional data on the connection.</p></span></li><li><span style="FONT-WEIGHT: normal" value="3"><p class="docText">Sometime later, the application that received the end-of-file will <tt>close</tt> its socket. This causes its TCP to send a FIN.</p></span></li><li><span style="FONT-WEIGHT: normal" value="4"><p class="docText">The TCP on the system that receives this final FIN (the end that did the active close) acknowledges the FIN.</p></span></li></ol><p class="docText">Since a FIN and an ACK are required in each direction, four segments are normally required. We use the qualifier "normally" because in some scenarios, the FIN in Step 1 is sent with data. Also, the segments in Steps 2 and 3 are both from the end performing the passive close and could be combined into one segment. We show these packets in <a class="docLink" href="mk:@MSITStore:E:\正在看的书籍\UNIX%20Network%20Programming%20Volume[1].1%20The%20Sockets%20Networking%20API.chm::/0131411551_ch02lev1sec6.html#ch02fig03">Figure 2.3</a>.</p><center><h5 class="docFigureTitle"><a name="ch02fig03"></a>Figure 2.3. Packets exchanged when a TCP connection is closed.</h5><p class="docText"><img height="241" alt="graphics/02fig03.gif" src="mk:@MSITStore:E:\正在看的书籍\UNIX%20Network%20Programming%20Volume[1].1%20The%20Sockets%20Networking%20API.chm::/FILES/02fig03.gif" width="500" border="0" /></p></center><p class="docText">A FIN occupies one byte of sequence number space just like a SYN. Therefore, the ACK of each FIN is the sequence number of the FIN plus one.</p><p class="docText">Between Steps 2 and 3 it is possible for data to flow from the end doing the passive close to the end doing the active close. This is called a <span class="docEmphasis">half-close</span> and we will talk about this in detail with the <tt>shutdown</tt> function in <a class="docLink" href="mk:@MSITStore:E:\正在看的书籍\UNIX%20Network%20Programming%20Volume[1].1%20The%20Sockets%20Networking%20API.chm::/0131411551_ch06lev1sec6.html#ch06lev1sec6">Section 6.6</a>.</p><p class="docText">The sending of each FIN occurs when a socket is closed. We indicated that the application calls <tt>close</tt> for this to happen, but realize that when a Unix process terminates, either voluntarily (calling <tt>exit</tt> or having the <tt>main</tt> function return) or involuntarily (receiving a signal that terminates the process), all open descriptors are closed, which will also cause a FIN to be sent on any TCP connection that is still open.</p><p class="docText">Although we show the client in <a class="docLink" href="mk:@MSITStore:E:\正在看的书籍\UNIX%20Network%20Programming%20Volume[1].1%20The%20Sockets%20Networking%20API.chm::/0131411551_ch02lev1sec6.html#ch02fig03">Figure 2.3</a> performing the active close, either end—the client or the server—can perform the active close. Often the client performs the active close, but with some protocols (notably HTTP/1.0), the server performs the active close.</p><a name="ch02lev2sec4"></a><h4 class="docSection2Title">TCP 状态转换图</h4><p class="docText">关于TCP连接的建立和终止操作可以由一个状态转换图来详细说明，如下图所示：<br /><a name="ch02fig04"></a>图示：TCP 状态转换图<br /><img height="636" alt="graphics/02fig04.gif" src="mk:@MSITStore:E:\正在看的书籍\UNIX%20Network%20Programming%20Volume[1].1%20The%20Sockets%20Networking%20API.chm::/FILES/02fig04.gif" width="500" border="0" /></p><p class="docText">There are 11 different states defined for a connection and the rules of TCP dictate the transitions from one state to another, based on the current state and the segment received in that state. For example, if an application performs an active open in the CLOSED state, TCP sends a SYN and the new state is SYN_SENT. If TCP next receives a SYN with an ACK, it sends an ACK and the new state is ESTABLISHED. This final state is where most data transfer occurs.</p><p class="docText">The two arrows leading from the ESTABLISHED state deal with the termination of a connection. If an application calls <tt>close</tt> before receiving a FIN (an active close), the transition is to the FIN_WAIT_1 state. But if an application receives a FIN while in the ESTABLISHED state (a passive close), the transition is to the CLOSE_WAIT state.</p><p class="docText">We denote the normal client transitions with a darker solid line and the normal server transitions with a darker dashed line. We also note that there are two transitions that we have not talked about: a simultaneous open (when both ends send SYNs at about the same time and the SYNs cross in the network) and a simultaneous close (when both ends send FINs at the same time). Chapter 18 of TCPv1 contains examples and a discussion of both scenarios, which are possible but rare.</p><p class="docText">One reason for showing the state transition diagram is to show the 11 TCP states with their names. These states are displayed by <tt>netstat</tt>, which is a useful tool when debugging client/server applications. We will use <tt>netstat</tt> to monitor state changes in <a class="docLink" href="mk:@MSITStore:E:\正在看的书籍\UNIX%20Network%20Programming%20Volume[1].1%20The%20Sockets%20Networking%20API.chm::/0131411551_ch05.html#ch05">Chapter 5</a>.</p><a name="ch02lev2sec5"></a><h4 class="docSection2Title">观察包（Watching the Packets）</h4><p class="docText">下土显示了一个TCP连接实际发生的包交换情况：连接建立，数据传输和连接终止。在每一端传输的时候，也给出了TCP状态。 <br /><a name="ch02fig05"></a>TCP 连接的包交换</p><p class="docText"><img height="414" alt="graphics/02fig05.gif" src="mk:@MSITStore:E:\正在看的书籍\UNIX%20Network%20Programming%20Volume[1].1%20The%20Sockets%20Networking%20API.chm::/FILES/02fig05.gif" width="478" border="0" /></p><p class="docText">The client in this example announces an MSS of 536 (indicating that it implements only the minimum reassembly buffer size) and the server announces an MSS of 1,460 (typical for IPv4 on an Ethernet). It is okay for the MSS to be different in each direction (see <a class="docLink" href="mk:@MSITStore:E:\正在看的书籍\UNIX%20Network%20Programming%20Volume[1].1%20The%20Sockets%20Networking%20API.chm::/0131411551_ch02lev1sec15.html#ch02lev1sec15">Exercise 2.5</a>).</p><p class="docText">Once a connection is established, the client forms a request and sends it to the server. We assume this request fits into a single TCP segment (i.e., less than 1,460 bytes given the server's announced MSS). The server processes the request and sends a reply, and we assume that the reply fits in a single segment (less than 536 in this example). We show both data segments as bolder arrows. Notice that the acknowledgment of the client's request is sent with the server's reply. This is called <span class="docEmphasis">piggybacking</span> and will normally happen when the time it takes the server to process the request and generate the reply is less than around 200 ms. If the server takes longer, say one second, we would see the acknowledgment followed later by the reply. (The dynamics of TCP data flow are covered in detail in Chapters 19 and 20 of TCPv1.)</p><p class="docText">We then show the four segments that terminate the connection. Notice that the end that performs the active close (the client in this scenario) enters the TIME_WAIT state. We will discuss this in the next section.</p><p class="docText">It is important to notice in <a class="docLink" href="mk:@MSITStore:E:\正在看的书籍\UNIX%20Network%20Programming%20Volume[1].1%20The%20Sockets%20Networking%20API.chm::/0131411551_ch02lev1sec6.html#ch02fig05">Figure 2.5</a> that if the entire purpose of this connection was to send a one-segment request and receive a one-segment reply, there would be eight segments of overhead involved when using TCP. If UDP was used instead, only two packets would be exchanged: the request and the reply. But switching from TCP to UDP removes all the reliability that TCP provides to the application, pushing lots of these details from the transport layer (TCP) to the UDP application. Another important feature provided by TCP is congestion control, which must then be handled by the UDP application. Nevertheless, it is important to understand that many applications are built using UDP because the application exchanges small amounts of data and UDP avoids the overhead of TCP connection establishment and connection termination.</p><a href="mk:@MSITStore:E:\正在看的书籍\UNIX%20Network%20Programming%20Volume[1].1%20The%20Sockets%20Networking%20API.chm::/0131411551_22961534.html"><img height="1" src="mk:@MSITStore:E:\正在看的书籍\UNIX%20Network%20Programming%20Volume[1].1%20The%20Sockets%20Networking%20API.chm::/FILES/pixel.gif" width="1" border="0" /></a><h3 class="docSection1Title">TIME_WAIT 状态</h3><h3 class="docSection1Title">毫无疑问，关于网络编程中最让人误解点之一就是 TIME_WAIT 状态。 在一端调用了close之后，该端维持这个状态的时间为两倍最大段生存时间（<span class="docEmphasis">maximum segment lifetime</span> (MSL)）。</h3><p class="docText">Every implementation of TCP must choose a value for the MSL. The recommended value in RFC 1122 [Braden 1989] is 2 minutes, although Berkeley-derived implementations have traditionally used a value of 30 seconds instead. This means the duration of the TIME_WAIT state is between 1 and 4 minutes. The MSL is the maximum amount of time that any given IP datagram can live in a network. We know this time is bounded because every datagram contains an 8-bit hop limit (the IPv4 TTL field in <a class="docLink" href="mk:@MSITStore:E:\正在看的书籍\UNIX%20Network%20Programming%20Volume[1].1%20The%20Sockets%20Networking%20API.chm::/0131411551_app01lev1sec2.html#app01fig01">Figure A.1</a> and the IPv6 hop limit field in <a class="docLink" href="mk:@MSITStore:E:\正在看的书籍\UNIX%20Network%20Programming%20Volume[1].1%20The%20Sockets%20Networking%20API.chm::/0131411551_app01lev1sec3.html#app01fig02">Figure A.2</a>) with a maximum value of 255. Although this is a hop limit and not a true time limit, the assumption is made that a packet with the maximum hop limit of 255 cannot exist in a network for more than MSL seconds.</p><p class="docText">The way in which a packet gets "lost" in a network is usually the result of routing anomalies. A router crashes or a link between two routers goes down and it takes the routing protocols seconds or minutes to stabilize and find an alternate path. During that time period, routing loops can occur (router A sends packets to router B, and B sends them back to A) and packets can get caught in these loops. In the meantime, assuming the lost packet is a TCP segment, the sending TCP times out and retransmits the packet, and the retransmitted packet gets to the final destination by some alternate path. But sometime later (up to MSL seconds after the lost packet started on its journey), the routing loop is corrected and the packet that was lost in the loop is sent to the final destination. This original packet is called a <span class="docEmphasis">lost duplicate</span> or a <span class="docEmphasis">wandering duplicate</span>. TCP must handle these duplicates.</p><p class="docText">There are two reasons for the TIME_WAIT state:</p><span style="FONT-WEIGHT: bold"><ol class="docList" type="1"><li><span style="FONT-WEIGHT: normal"><p class="docList">To implement TCP's full-duplex connection termination reliably</p></span></li><li><span style="FONT-WEIGHT: normal"><p class="docList">To allow old duplicate segments to expire in the network</p></span></li></ol></span><p class="docText">The first reason can be explained by looking at <a class="docLink" href="mk:@MSITStore:E:\正在看的书籍\UNIX%20Network%20Programming%20Volume[1].1%20The%20Sockets%20Networking%20API.chm::/0131411551_ch02lev1sec6.html#ch02fig05">Figure 2.5</a> and assuming that the final ACK is lost. The server will resend its final FIN, so the client must maintain state information, allowing it to resend the final ACK. If it did not maintain this information, it would respond with an RST (a different type of TCP segment), which would be interpreted by the server as an error. If TCP is performing all the work necessary to terminate both directions of data flow cleanly for a connection (its full-duplex close), then it must correctly handle the loss of any of these four segments. This example also shows why the end that performs the active close is the end that remains in the TIME_WAIT state: because that end is the one that might have to retransmit the final ACK.</p><p class="docText">To understand the second reason for the TIME_WAIT state, assume we have a TCP connection between 12.106.32.254 port 1500 and 206.168.112.219 port 21. This connection is closed and then sometime later, we establish another connection between the same IP addresses and ports: 12.106.32.254 port 1500 and 206.168.112.219 port 21. This latter connection is called an <span class="docEmphasis">incarnation</span> of the previous connection since the IP addresses and ports are the same. TCP must prevent old duplicates from a connection from reappearing at some later time and being misinterpreted as belonging to a new incarnation of the same connection. To do this, TCP will not initiate a new incarnation of a connection that is currently in the TIME_WAIT state. Since the duration of the TIME_WAIT state is twice the MSL, this allows MSL seconds for a packet in one direction to be lost, and another MSL seconds for the reply to be lost. By enforcing this rule, we are guaranteed that when we successfully establish a TCP connection, all old duplicates from previous incarnations of the connection have expired in the network.</p><blockquote><p></p><p class="docList">There is an exception to this rule. Berkeley-derived implementations will initiate a new incarnation of a connection that is currently in the TIME_WAIT state if the arriving SYN has a sequence number that is "greater than" the ending sequence number from the previous incarnation. Pages 958–959 of TCPv2 talk about this in more detail. This requires the server to perform the active close, since the TIME_WAIT state must exist on the end that receives the next SYN. This capability is used by the <tt>rsh</tt> command. RFC 1185 [Jacobson, Braden, and Zhang 1990] talks about some pitfalls in doing this.</p><p></p><table cellspacing="0" cellpadding="0" width="100%" border="0"><tbody><tr><td valign="top"><h3 class="docSection1Title" id="225793-970">一般网络程序所使用的协议</h3><p class="docText">下图总结了一些：</p><center><h5 class="docFigureTitle"><a name="ch02fig19"></a>Figure 2.19. Protocol usage of various common Internet applications.</h5><p class="docText"><img id="129022115118" height="454" alt="graphics/02fig19.gif" src="mk:@MSITStore:E:\正在看的书籍\UNIX%20Network%20Programming%20Volume[1].1%20The%20Sockets%20Networking%20API.chm::/FILES/02fig19.gif" width="500" border="0" /></p></center><p class="docText">The first two applications, <tt>ping</tt> and <tt>traceroute</tt>, are diagnostic applications that use ICMP. <tt>traceroute</tt> builds its own UDP packets to send and reads ICMP replies.</p><p class="docText">The three popular routing protocols demonstrate the variety of transport protocols used by routing protocols. OSPF uses IP directly, employing a raw socket, while RIP uses UDP and BGP uses TCP.</p><p class="docText">The next five are UDP-based applications, followed by seven TCP applications and four that use both UDP and TCP. The final five are IP telephony applications that use SCTP exclusively or optionally UDP, TCP, or SCTP.</p></td></tr></tbody></table></blockquote><img src ="http://www.cppblog.com/sscchh-2000/aggbug/7292.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/sscchh-2000/" target="_blank">史传红</a> 2006-05-17 09:42 <a href="http://www.cppblog.com/sscchh-2000/archive/2006/05/17/7292.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Unix网络编程第三版,卷一:套接字联网API学习笔记(2)</title><link>http://www.cppblog.com/sscchh-2000/archive/2006/05/10/6860.html</link><dc:creator>史传红</dc:creator><author>史传红</author><pubDate>Wed, 10 May 2006 03:13:00 GMT</pubDate><guid>http://www.cppblog.com/sscchh-2000/archive/2006/05/10/6860.html</guid><wfw:comment>http://www.cppblog.com/sscchh-2000/comments/6860.html</wfw:comment><comments>http://www.cppblog.com/sscchh-2000/archive/2006/05/10/6860.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/sscchh-2000/comments/commentRss/6860.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/sscchh-2000/services/trackbacks/6860.html</trackback:ping><description><![CDATA[
		<p>该节主要展示一个简单的[日期时间]服务器的程序示例.<br />§1.3 一个简单的日期时间服务器程序代码<br />   这个服务器程序可以为上一节的客户端提供服务。<br /></p>
		<pre> 1 #include     "unp.h"<br />
 2 #include     &lt;time.h&gt;<br />

 3 int<br />
 4 main(int argc, char **argv)<br />
 5 {<br />
 6     int     listenfd, connfd;<br />
 7     struct sockaddr_in servaddr;<br />
 8     char    buff[MAXLINE];<br />
 9     time_t ticks;<br />

10     listenfd = Socket(AF_INET, SOCK_STREAM, 0);<br />

11     bzeros(&amp;servaddr, sizeof(servaddr));<br />
12     servaddr.sin_family = AF_INET;<br />
13     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);<br />
14     servaddr.sin_port = htons(13); /* daytime server */<br />

15     Bind(listenfd, (SA *) &amp;servaddr, sizeof(servaddr));<br />

16     Listen(listenfd, LISTENQ);<br />

17     for ( ; ; ) {<br />
18         connfd = Accept(listenfd, (SA *) NULL, NULL);<br />

19         ticks = time(NULL);<br />
20         snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&amp;ticks));<br />
21         Write(connfd, buff, strlen(buff));<br />

22         Close(connfd);<br />
23     }<br />
24 }<br /></pre>
		<a name="ch01lev3sec8">
		</a>
		<h4 class="docSection2Title">产生一个TCP套接字</h4>
		<h4 class="docSection2Title">
				<span class="docEmphasis">
						<tt>10</tt>
				</span>创建一个TCP套接字，与客户端一样。</h4>
		<h4 class="docSection2Title">
				<a name="ch01lev3sec9">
				</a>绑定服务器的广为人知的端口到该套接字</h4>
		<p class="docText">
				<span class="docEmphasis">
						<tt>11–15</tt>
				</span>服务器通过填充网络套接字结构体中的端口域，以及服务器的网络接口（IP地址），然后进行绑定（调用bind）。在这里指定IP地址为INADDR_ANY，为了让客户端可以连接服务器的任一网络接口（因为服务器可能有多块网卡，也就对应了多个IP地址），也就是说如果服务器有两个IP地址，客户端连接任一IP地址即可。后续章节中介绍了如何限制客户端连接到一个固定的接口上。</p>
		<a name="ch01lev3sec10">
		</a>
		<h4 class="docSection2Title">转换为监听套接字</h4>
		<p class="docText">
				<span class="docEmphasis">
						<tt>16</tt>
				</span>通过调用listen，一个套接字就转换为监听套接字，这就是说该套接字负责接收来自客户端的连接请求，而并不真正与客户端进行信息传输。<br />常量<tt>LISTENQ</tt> 是在头文件<tt>unp.h中定义的，它是指能够同时监听客户端连接的个数。不超过LISTENQ的客户端同时连接服务器，它们会在一个队列中排队，来等待服务器的处理。后续章节有更详细的讨论。<br /></tt><br />接收客户端连接，发送回复</p>
		<p class="docText">
				<span class="docEmphasis">
						<tt>17–21</tt>
				</span>一般地，服务器进程在调用accept之后进入到睡眠状态，等待着客户端地连接请求. 一个TCP连接通过一个称为三方握手来建立，当三方握手完成之后，accept调用返回。返回值是一个新的套接字描述符（一个整数值connfd），这个新的套接字负责与客户端进行通讯。对于每一个客户端地连接，accept都返回一个新的套接字描述符。整本书使用的无限循环风格是这样的：</p>
		<blockquote>
				<pre>
				</pre>
				<pre>for ( ; ; ) {
    . . .
}
</pre>
				<pre>
				</pre>
		</blockquote>
		<p class="docText">当前时间和日期通过调用库函数time来获得，并且通过调用ctime进行转换，使得我们能够直观的阅读。如下：<br />Mon May 26 20:58:40 2003</p>
		<p>
		</p>
		<a name="ch01lev3sec12">
		</a>
		<h4 class="docSection2Title">终止连接</h4>
		<p class="docText">
				<span class="docEmphasis">
						<tt>22</tt>
				</span>客户端调用close之后，服务器关闭连接。这时候引起了一个TCP连接终止序列：一个FIN发送到每一端，同时每一个FIN都要被另一端确认。在后面章节中将会对TCP连接建立时候的三方握手以及TCP连接终止时候的四包交换有更详细的讨论。<br />   以上给出的客户端和服务器版本都是协议相关的（IPv4），在后面将会给出一个协议无关的版本（IPv4和IPv6都适用，主要通过使用getaddrinfo函数）。<br />   最后需要补充的一点是，在以上涉及到Socket API调用的时候，每个函数的第一个字母变成了大写，其意义和小写开头的是一样的，只不过多了一个错误处理罢了。</p>
<img src ="http://www.cppblog.com/sscchh-2000/aggbug/6860.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/sscchh-2000/" target="_blank">史传红</a> 2006-05-10 11:13 <a href="http://www.cppblog.com/sscchh-2000/archive/2006/05/10/6860.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Unix网络编程第三版,卷一:套接字联网API学习笔记(1)</title><link>http://www.cppblog.com/sscchh-2000/archive/2006/05/09/6834.html</link><dc:creator>史传红</dc:creator><author>史传红</author><pubDate>Tue, 09 May 2006 12:31:00 GMT</pubDate><guid>http://www.cppblog.com/sscchh-2000/archive/2006/05/09/6834.html</guid><wfw:comment>http://www.cppblog.com/sscchh-2000/comments/6834.html</wfw:comment><comments>http://www.cppblog.com/sscchh-2000/archive/2006/05/09/6834.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/sscchh-2000/comments/commentRss/6834.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/sscchh-2000/services/trackbacks/6834.html</trackback:ping><description><![CDATA[§有关电子资料链接:<a class="" title="" href="http://www.unpbook.com" target="_blank">http://www.unpbook.com/<br /></a>§1.1 介绍<br /><br /><h5 class="docFigureTitle"><a name="ch01fig01"></a>图1.1  网络程序: 客户端和服务器</h5><p class="docText"><img id="129022115118" height="63" alt="graphics/01fig01.gif" src="mk:@MSITStore:E:\study_magizine\UNIX%20Network%20Programming%20Volume[1].1%20The%20Sockets%20Networking%20API.chm::/FILES/01fig01.gif" width="407" border="0" /><br /><br /><strong><font color="#000000">图 1.2 服务器同时处理多个客户端</font></strong></p><p class="docText"><img id="129022115118" height="272" alt="graphics/01fig02.gif" src="mk:@MSITStore:E:\study_magizine\UNIX%20Network%20Programming%20Volume[1].1%20The%20Sockets%20Networking%20API.chm::/FILES/01fig02.gif" width="408" border="0" /><br /></p><h5 class="docFigureTitle"><a name="ch01fig03"></a>图 1.3 在同一个以太网使用TCP协议通信的客户端和服务器<img id="129022115118" height="271" alt="graphics/01fig03.gif" src="mk:@MSITStore:E:\study_magizine\UNIX%20Network%20Programming%20Volume[1].1%20The%20Sockets%20Networking%20API.chm::/FILES/01fig03.gif" width="500" border="0" /><br /><h5 class="docFigureTitle"><a name="ch01fig04"></a>图 1.4 广域网上的客户端与服务器（例如Web浏览器和Web服务器）</h5><p class="docText"><img id="129022115118" height="264" alt="graphics/01fig04.gif" src="mk:@MSITStore:E:\study_magizine\UNIX%20Network%20Programming%20Volume[1].1%20The%20Sockets%20Networking%20API.chm::/FILES/01fig04.gif" width="500" border="0" /></p><p class="docText">§1.2 代码示例和解说</p><p class="docText">1 #include  "unp.h"<br /><br />2 int<br />3 main(int argc, char **argv)<br />4 {<br />5     int     sockfd, n;<br />6     char    recvline[MAXLINE + 1];<br />7     struct sockaddr_in servaddr;<br /><br />8     if (argc != 2)<br />9         err_quit("usage: a.out &lt;IPaddress&gt;");<br /><br />10     if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) &lt; 0)<br />11         err_sys("socket error");<br /><br />12     bzero(&amp;servaddr, sizeof(servaddr));<br />13     servaddr.sin_family = AF_INET;<br />14     servaddr.sin_port = htons(13);  /* daytime server */<br />15     if (inet_pton(AF_INET, argv[1], &amp;servaddr.sin_addr) &lt;= 0)<br />16         err_quit("inet_pton error for %s", argv[1]);<br /><br />17     if (connect(sockfd, (SA *) &amp;servaddr, sizeof(servaddr)) &lt; 0)<br />18         err_sys("connect error");<br /><br />19     while ( (n = read(sockfd, recvline, MAXLINE)) &gt; 0) {<br />20         recvline[n] = 0;        /* null terminate */<br />21         if (fputs(recvline, stdout) == EOF)<br />22             err_sys("fputs error");<br />23     }<br />24     if (n &lt; 0)<br />25         err_sys("read error");<br /><br />26     exit(0);<br />27 }<br />其中unp.h是自定义的头文件，<a class="" title="unp.h文件内容" href="/sscchh-2000/archive/2006/05/09/6836.html" target="_blank">查看源代码</a>。我们编译并执行以上代码，得到以下输出结果：</p><p class="docText"></p><table cellspacing="0" cellpadding="5" rules="none" width="100%" frame="void"><tbody><tr><td class="docTableCell" valign="top"><p class="docText"><tt>solaris %</tt><span class="docEmphStrong"><tt>a.out 206.168.112.96     <span class="docEmphasis">our input</span></tt></span></p></td><td class="docTableCell" valign="top"></td></tr><tr><td class="docTableCell" valign="top"><p class="docText"><tt>Mon May 26 20:58:40 2003          <span class="docEmphasis">the program's output</span></tt></p></td><td class="docTableCell" valign="top"></td></tr></tbody></table></h5>下面简要分析以上27行代码,后续章节中有更详细的讨论.<br /><h4 class="docSection2Title">包含我们自己的头文件</h4><p class="docText"><span class="docEmphasis"><tt>1</tt></span> 该头文件包含了大多数网络程序所需要的多个头文件以及定义了我们将要使用的一些常量(例如 <tt>MAXLINE</tt>).<br /></p><h4 class="docSection2Title"><span class="docEmphasis"><tt><font face="Times New Roman">命令行参数</font></tt></span></h4><h4 class="docSection2Title"><span class="docEmphasis"><tt>2–3</tt></span> 这是含有命令行参数的主函数定义(即main函数).我们以ANSI C标准来书写代码.</h4><h4 class="docSection2Title">创建TCP套接字<a name="ch01lev3sec3"></a></h4><p class="docText"><span class="docEmphasis"><tt>10–11 </tt></span><tt>socket</tt>函数调用创建了一个网络流套接字.(Internet (<tt>AF_INET</tt>) stream (<tt>SOCK_STREAM</tt>) socket), 该函数返回一个整数值,它描述了该套接字,以后的函数通过该整数值来使用这个套接字(例如connect和read等调用). 其中err_开头的函数是我们自定义的函数,详见<a class="" title="sys_开头的自定义函数" href="/sscchh-2000/archive/2006/05/09/6838.html" target="_blank">这里</a>.</p><a name="ch01lev3sec4"></a><h4 class="docSection2Title">确定服务器IP地址和端口号</h4><h4 class="docSection2Title"><span class="docEmphasis"><tt>12–16</tt></span> 我们填充了网络套接字地址结构(一个名为servaddr的结构体sockaddr_in),填充的信息包括服务器IP地址和端口号.我们把整个结构体首先清零,然后设置地址族为AF_INET(IPV6该项为AF_INET6),端口号为13(时间服务器的端口号,是一个大家都知道的端口号).IP地址由命令行参数指定(argv[1]).IP地址和端口号必须按照指定的格式来填充,我们通过调用htons(主机字节流到网络字节流的转换)和inet_pton(点分十进制到32位整数的转换)两个调用来进行转化到所需要的格式.</h4><h4 class="docSection2Title">在调用inet_pton的时候可而能会遇到问题,因为这是IPv6新增的函数,以前的IPv4版本可以调用inet_addr来替代该函数.</h4><p></p><a name="ch01lev3sec5"></a><h4 class="docSection2Title">与服务器建立一个连接</h4><h4 class="docSection2Title"><span class="docEmphasis"><tt>17–18</tt></span> TCP套接字调用connect函数,就与服务器(main函数的第二个参数)建立了一个TCP连接,我们必须指定套接字结构体的第三个参数长度,它总是让编译器通过C的sizeof运算符来计算.</h4><p></p><a name="ch01lev3sec6"></a><h4 class="docSection2Title">读取和显示服务器的回复</h4><h4 class="docSection2Title"><span class="docEmphasis"><tt>19–25</tt></span> 调用read来读取服务器的回复,利用标准I/O来显示该回复信息.此外,在使用TCP的时候我们必须要注意,因为它是一个没有边界的字节流协议.服务器的回复是一个26字节的串:</h4><pre></pre><pre>Mon May 26 20 : 58 : 40 2003\r\n
</pre><pre></pre><p class="docText"><tt>\r</tt> 是回车, <tt>\n</tt> 是换行. <br /><br />终止程序<br /><span class="docEmphasis"><tt>26 </tt></span><tt>exit</tt> 终止程序.Unix在一个进程结束时候总是关闭所有打开的描述符,因此我们的TCP套接字此时关闭了.<br />后续内容将对此有更深入的讨论.</p><img src ="http://www.cppblog.com/sscchh-2000/aggbug/6834.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/sscchh-2000/" target="_blank">史传红</a> 2006-05-09 20:31 <a href="http://www.cppblog.com/sscchh-2000/archive/2006/05/09/6834.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>