# TCP/UDP的Socket编程接口详解

---- socket概述：

socket是在应用层和传输层之间的一个抽象层，它把TCP/IP层复杂的操作抽象为几个简单的接口供应用层调用已实现进程在网络中通信。

socket起源于UNIX，在Unix一切皆文件哲学的思想下，socket是一种"打开—读/写—关闭"模式的实现，服务器和客户端各自维护一个"文件"，在建立连接打开后，可以向自己文件写入内容供对方读取或者读取对方内容，通讯结束时关闭文件。

---- 接口简介：

socket()：创建socket

bind()：绑定socket到本地地址和端口，通常由服务端调用

listen()：TCP专用，开启监听模式

accept()：TCP专用，服务器等待客户端连接，一般是阻塞态

connect()：TCP专用，客户端主动连接服务器

send()：TCP专用，发送数据

recv()：TCP专用，接收数据

sendto()：UDP专用，发送数据到指定的IP地址和端口

recvfrom()：UDP专用，接收数据，返回数据远端的IP地址和端口

closesocket()：关闭socket

---- 流程如下：

>> socket() : creating  a socket

A socket is an abstraction of a communication endpoint. Just as they would use file descriptors to access files, applications use socket descriptors to access sockets. To create a socket, we call the socket() function.

domain：AF_INET, AF_INET6, AF_UNIX, AF_UNSPEC (address format)

type：SOCK_DGRAM, SOCK_RAW, SOCK_STREAM, SOCK_SEQPACKET

protocol：IPPROTO_IP,  IPPROTO_IPV6,  IPPROTO_TCP, IPPROTO_UDP

The protocol argument is usually zero, to select the default protocol for the given domain and socket type. The default protocol for a SOCK_STREAM socket in the AF_INET communication domain is TCP(Transmission Control Protocol). The default protocol for a SOCK_DGRAM socket in the AF_INET communication domain is UDP(User Datagram Protocol).

NOTE: UDP -- 数据报（datagram），无连接的，no logical connection exist between peers for them to communicate. A datagram is a self-contained(独立的) message. 类似于（analogous）发邮件，你可以发送多封邮件，但是不能保证邮件是否到达和邮件到达的顺序。每一封邮件都包含了接收者的地址。

TCP -- 字节流 A byte stream(SOCK_STREAM), in contrast, 在传输数据之前，需要建立连接，面向连接的通信类似于打电话。点到点的连接里包含了source and destination。

Communication on a socket is bidirectional. We can disable I/O on a socket with the shutdown function.

>> shutdown()

The shutdown() system call closes one or both channels of the socket sockfd, depending on the value of how, which is specified as one of the following:

howSHUT_RD, then reading from the socket is disabled.  SHUT_WR, then we can't use the socket for transmitting data. We can use SHUT_RDWR to disable both data transmission and reception.

shutdown() differs from close() in another important respect: it closes the socket channels regardless of whether there are other file descriptors referring to the socket. For example, sockfd refers to a connected stream socket. If we make the following calls, then the connection remains open, and we can still perform I/O on the connection via the file descriptor fd2:

    fd = dup(sockfd);
close(sockfd);

However, if we make the following sequence of calls, then both channels of the connection are closed, and I/O can no longer be performed via fd2:

    fd2 = dup(sockfd);
shutdown(sockfd,SHUT_RDWR);

Note that shutdown() doesn't close the file descriptor, even if how is specified as SHUT_RDWR. To close the file descriptor, we must additionally call close().

>> bind() : binding a socket to an address

The bind() system call binds a socket to an address.

The sockfd argument is a file descriptor obtained from a previous call to socket(). The addr argument is a pointer to a structure specifying the address to which this socket is to be bound. The type of structure passed in this argument depends on the socket domain. The addrlen argument specifies the size of the address structure.

Typically, we bind a server's socket to a well-known address - that is, a fixed address that is known in advance to client applications that need to communicate with that server.

>> listen() : listening for incoming connections

The listen() system call marks the stream socket referred to by the file descriptor sockfd as passive. The socket will subsequently be used to accept connections from other(active) sockets.

The client may call connect() before the server calls accept(). This could happen, for example, because the server is busy handling some other clients. This results in a pending connection, as illustrated in Figure 56-2.

The kernel must record some information about each pending connection request so that a subsequent accept() can be processed. The backlog argument allows us to limit the number of such pending connections. Further connection requests block until a pending connection is accepted(via accept()), and thus removed from the queue of pending connections.

>> accept() : accepting a connection

The accept() system call accepts an incoming connection on the listening stream socket referred to by the file descriptor sockfd. If there are no pending connections when accept() is called, the call blocks until a connection request arrives when the sockfd in block mode. If sockfd is in nonblocking mode, accept() will return -1 and set errno to either EAGAIN or EWOULDBLOCK.

The key point to understand about accept() is that it creates a new socket, and this new socket that is connected to the peer socket that performed the connect(). This new socket descriptor has the same socket type and address family as the  original socket(sockfd). A file descriptor for the connected socket is returned as the function result of the accept() call. The listening socket(sockfd) remains open, and can be used to accept further connections. A typical server application creates one listening socket, binds it to a well-known address, and then handles all client requests by accepting connections via that socket.

The remaining（剩余的） arguments to accept() return the address of the peer socket.(客户端)

If we don't care about the client's identity, we can set the addr and len parameters to NULL. Otherwise, before calling accept, we need to set the addr (指向一个buffer) parameter to a buffer large enough to hold the address and set the integer pointed to by len to the size of the buffer in bytes. On return, accept will fill in the client's address in the buffer and update the integer pointed to by len to reflect the size of the address.

>> connect() : connecting to a peer socket

The connect() system call connects the active socket referred to by the file descriptor sockfd to the listening socket whose address is specified by addr and addrlen.

>> send() : TCP类型的数据发送

sockfd -- 发送端套接字描述符 （非监听描述符）

msg -- 待发送数据的缓冲区 （将其内容的len长度拷贝到socket的发送缓冲区）

len -- 待发送数据的字节长度。

flags -- 一般情况下置为0.

>> recv() : TCP类型的数据接收

recv()从接收缓冲区拷贝数据。成功时，返回拷贝的字节数，失败时，返回-1。阻塞模式下，recv()将会阻塞直到缓冲区中至少有一个字节才返回，没有数据时处于休眠状态。若非阻塞，则立即返回，有数据则返回拷贝的数据大小，否则返回错误-1.

sockfd -- 接收端套接字描述符（非监听描述符）

buf -- 接收数据的基地址（将socket的接收缓冲区中的内容拷贝至buf中）

len -- 接收到数据的字节数

flags -- 一般情况下置为0.

>> sendto() : UDP类型的数据发送

UDP没有真正的发送缓冲区，因为是不可靠连接，不必保存应用进程的数据拷贝，应用进程的数据在沿协议栈向下传递时，以某种形式拷贝到内核缓冲区，当数据链路层把数据传出后就把内核缓冲区中数据拷贝删除。因此它不需要一个发送缓冲区。

For sendto(), the dest_addr and addrlen arguments specify the socket to which the datagram is to be sent. These arguments are employed in the same manner as the corresponding arguments to connect(). The dest_addr argument is an address structure suitable for this communication domain. It is initialized with the address of the destination socket. The addrlen argument specifies the size of addr.

>> recvfrom() : UDP类型的数据接收

sockfd -- 接收端套接字描述；

buf -- 用于接收数据的应用缓冲区地址；

len -- 指明缓冲区大小；

flags -- 通常为0；

For recvfrom(), the src_addr and addrlen arguments return the address of the remote socket used to send the datagram. (These arguments are analogous to the addr and addrlen arguments of accept(), which return the address of a connecting peer socket.) Prior to the call(在调用之前), addrlen should be initialized to the size of the structure pointed to by src_addr（结构的大小）; upon return（在返回时）, it contains the number of bytes actually written to this structure.

07-18 21万+

09-09 1万+
09-06 1809
02-02 626
07-20 3865
08-19 408
02-16 672
11-25 3万+
06-14 1455
11-30 1130
12-11 527
03-08 6369
10-12 827
05-16 7204
04-06 8862
05-18 1326
12-22