网络编程之Elementary TCP Sockets(一)

1. socket

#include <sys/socket.h>
int socket(int family, int type, int protocol);
Returns: non-negative descriptor if OK,1 on error

加个表

上表就是对该函数的一个总结,在APUE篇笔者也记录过该内容,在此不赘述,上表没有反映出来的几点需要注意下:

  • AF_UNIX (the historical Unix name)
  • TCP is a byte stream protocol, and supports only SOCK_STREAM sockets.
  • Linux supports a new socket type, SOCK_PACKET, that provides access to the datalink.
  • The key socket, AF_KEY, is newer than the others. It provides support for cryptographic security. Similar to the way that a routing socket (AF_ROUTE) is an interface to the kernel’s routing table, the key socket is an interface into the kernel’s key table.

2. connect Function

#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *servaddr, socklen_t addrlen);
Returns: 0 if OK,1 on error

一些基本的使用注意点:

  • The socket address structure must contain the IP address and port number of
    the server.
  • The client does not have to call bind before calling connect: the kernel will choose both an ephemeral port and the source IP address if necessary.
  • In the case of a TCP socket, the connect function initiates TCP’s three-way handshake . The function returns only when the connection is established or anerror occurs.

第三点指出connect会发起TCP的三次握手,而该函数只有在连接建立或者错误发生时返回,所以UNP列出了几种错误的情况:

  • If the client TCP receives no response to its SYN segment, ETIMEDOUT is returned.
  • If the server’s response to the client’s SYN is a reset (RST), this indicates that no process is waiting for connections on the server host at the port specified (i.e., the server process is probably not running). This is a hard error and the error ECONNREFUSED is returned to the client as soon as the RST is received. An RST is a type of TCP segment that is sent by TCP when something is wrong. Three conditions that generate an RST are:
    • when a SYN arrives for a port that has no listening server (what we just described),
    • when TCP wants to abort an existing connection//server发送给client
    • when TCP receives a segment for a connection that does not exist.
  • If the client’s SYN elicits an ICMP ‘‘destination unreachable’’ from some intermediate router, this is considered a soft error. The client kernel saves the message but keeps sending SYNs with the same time between each SYN as in the first scenario. If no response is received after some fixed amount of time, the saved ICMP error is returned to the process as either EHOSTUNREACH or ENETUNREACH. It is also possible that the remote system is not reachable by any route in the local system’s forwarding table, or that the connect call returns without waiting at all.

特别的上述第三个原因需要以后再次回顾,这里不过分追究,注意elicits在英文中的的用法为elicit sth from sb,UNP中举了几个connet错误的列子,并相应做出解释:

solaris % daytimetcpcli 192.168.1.100
connect error: Connection timed out

we specify an IP address that is on the local subnet (192.168.1/24) but the host ID (100) is nonexistent. That is, there is no host on the subnet with a host ID of 100, so when the client host sends out ARP requests (asking for that host to respond with its hardware address), it will never receive an ARP reply.//问题1

solaris % daytimetcpcli 192.168.1.5
connect error: Connection refused

This example is to specify a host (a local router) that is not running a daytime server. The server responds immediately with an RST.

solaris % daytimetcpcli 192.3.4.5
connect error: No route to host

This example specifies an IP address that is not reachable on the Interne. If we watch the packets with tcpdump, we see that a router six hops away returns an ICMP host unreachable error. As with the ETIMEDOUT error, in this example, connect returns the EHOSTUNREACH error only after waiting its specified amount of time.

tcpdump是抓包工具,从该工具也能判断网络的故障原因,抓包分析是针对网络问题比较基本的手段。

3. bind

#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *myaddr, socklen_t addrlen);
Returns: 0 if OK,1 on error

对于TCP来说,可以由编程人员指定地址和端口号,也可以让系统自己指定:

  • Servers bind their well-known port when they start. If a TCP client or server does not do this, the kernel chooses an ephemeral port for the socket when either connect or listen is called.
  • TCP client does not bind an IP address to its socket. The kernel chooses the source IP address when the socket is connected, based on the outgoing interface that is used, which in turn is based on the route required to reach the server.
  • If a TCP server does not bind an IP address to its socket, the kernel uses the destination IP address of the client’s SYN as the server’s source IP address。//问题2

加个图:

如果在bind中使用通配地址或者端口号指定为0时,发生以下行为:

If we specify a port number of 0, the kernel chooses an ephemeral port when bind is called. But if we specify a wildcard IP address, the kernel does not choose the local IP address until either the socket is connected (TCP) or a datagram is sent on the socket (UDP).//只是说明了该自动选择一个地址的发生的时间

//IPv4
struct sockaddr_in servaddr;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY); /* wildcard */
//IPv6
struct sockaddr_in6 serv;
serv.sin6_addr = in6addr_any; /* wildcard */

上述给了两个如何指定通配地址的例子,特别注意到htonlINADDR_ANY是一个值为0的常量,本质上其并不需要转换字节顺序,但是这里还是调用了htonl,原因如下:

all the INADDR_ constants defined by the netinet/in.h header are defined in host byte order, we should use htonl with any of these constants.

4. listen Function

TCP server调用listen后,会进行以下事情:

  • When a socket is created by the socket function, it is assumed to be an active
    socket, that is, a client socket that will issue a connect. The listen function
    converts an unconnected socket into a passive socket, indicating that the kernel
    should accept incoming connection requests directed to this socket.
  • The second argument to this function specifies the maximum number of connections the kernel should queue for this socket.
#include <sys/socket.h>
int listen(int sockfd, int backlog);
Returns: 0 if OK, −1 on error

UNP特别强调了backlog参数的理解,因为内核维护了两个队列:

  • An incomplete connection queue, which contains an entry for each SYN that has arrived from a client for which the server is awaiting completion of the TCP three-way handshake. These sockets are in the SYN_RCVD state.//这里佐证TCP状态的变化时以socekt为单位的。
  • A completed connection queue, which contains an entry for each client with whom the TCP three-way handshake has completed. These sockets are in the ESTABLISHED state.

加个两个图
这里写图片描述

具体描述上述过程的细节如下:

  • When a SYN arrives from a client, TCP creates a new entry on the incomplete queue and then responds with the second segment of the three-way handshake: the server’s SYN with an ACK of the client’s SYN. This entry will remain on the incomplete queue until the third segment of the three-way handshake arrives (the client’s ACK of the server’s SYN), or until the entry times out.
  • If the three-way handshake completes normally, the entry moves from the incomplete queue to the end of the completed queue. When the process calls accept, he first entry on the completed queue is returned to the process, or if the queue is empty, the process is put to sleep until an entry is placed onto the completed queue.

关于这两个队列有以下情况值得思考:

  • The backlog argument to the listen function has historically specified the maximum value for the sum of both queues.
  • Do not specify a backlog of 0, as different implementations interpret this differently. If you do not want any clients connecting to your listening socket, close the listening socket.
  • If the queues are full when a client SYN arrives, TCP ignores the arriving SYN ; it does not send an RST. This is because the condition is considered temporary, and the client TCP will retransmit its SYN, hopefully finding room on the queue in the near future. If the server TCP immediately responded with an RST, the client’s connect would return an error, forcing the application to handle this condition instead of letting TCP’s normal retransmission take over. Also, the client could not differentiate between an RST in response to a SYN meaning there is no server at this port versus there is a server at this port but its queues are full.
  • Data that arrives after the three-way handshake completes, but before the server calls accept, should be queued by the server TCP, up to the size of the connected socket’s receive buffer.

5. accept

#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *cliaddr, socklen_t *addrlen);
Returns: non-negative descriptor if OK,1 on error

API用法如下:

  • The cliaddr and addrlen arguments are used to return the protocol address of the connected peer process (the client). Before the call, we set the integer value referenced by *addrlen to the size of the socket address structure pointed to by cliaddr; on return, this integer value contains the actual number of bytes stored by the kernel in the socket address structure.
  • This function returns up to three values: an integer return code that is either a new socket descriptor or an error indication, the protocol address of the client process (through the cliaddr pointer), and the size of this address (through the addrlen pointer). If we are not interested in having the protocol address of the client returned, we set both cliaddr and addrlen to null pointers.

一些accept函数的需要注意的小知识点:

  • If accept is successful, its return value is a brand-new descriptor automatically created by the kernel. This new descriptor refers to the TCP connection with the client.//问题3
  • A given server normally creates only one listening socket, which then exists for the lifetime of the server.
  • The kernel creates one connected socket for each client connection that is accepted (i.e., for which the TCP three-way handshake completes).

6. close Function

#include <unistd.h>
int close(int sockfd);
Returns: 0 if OK,1 on error

The default action of close with a TCP socket is to mark the socket as closed and return to the process immediately. The socket descriptor is no longer usable by the process: It cannot be used as an argument to read or write. But, TCP will try to send any data that is already queued to be sent to the other end, and after this occurs, the normal TCP connection termination sequence takes place.

上述一为close的一般概念,但是sockfd同一般文件一样,都有引用计数,所以真正内核关闭该文件的时间,应该是引用计数为0时。要与link count相区分,两个是不一样的概念。打开的文件描述符的引用计数位于内核维护的文件列表条目中。

7. TCP state transmitation

虽然看上去最基本的API介绍完了,但是其与TCP state的对应却没有仔细讨论过,先看一个图:
  这里写图片描述
  之前的理解一直以为是针对client TCPServer TCP两个协议栈来说的,现在觉得如果想理解listen的两个队列,就必需理解状态变化是对socket说的。从client调用connect时,其socket变为SYN_SENT,而server端调用listen,此时未完成三次握手的socket,状态为SYN_RCVD,当三次握手完成时,该socket状态变为ESTABLISHED,当server调用accept时,返回该socketfd

这里笔者假设新的socket在listen函数调用后,accept调用前就已经生成了。

8. 遗留问题

  1. the client host sends out ARP requests (asking for that host to respond with its hardware address), it will never receive an ARP reply.
  2. Server没有bind一个地址,此时client又怎么知道connect谁呢?
  3. 既然listen中的描述以每个socekt描述TCP的状态,那么在调用accept时返回的新socektfd到底是在什么时候形成的呢?
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值