目录
fd = socket(domain, type, protocol);
Communication donmains
每个socket必须存在于一个communication domain
中,该域决定了:
现代操作系统支持如下域:
AF_UNIX
域允许在同一个主机上不同的应用通信。AF_INET
允许应用通过IPv4
协议网络在不同的主机之间通信AF_INET6
AF
是address family的缩写,PF
是protocol family的缩写。
Socket types
stream and datagram.
流式socket(SOCK_STREAM
),提供了可靠的、双向的、字节流的通信通道。意即:
A stream socket is similar to using a pair of pipes to allow idirectional communication between two applications, with the difference that (Internet domain) sockets permit communication over a network.
流式socket成双成对工作。因此流式socket被称为是面向连接的。
数据报socket(SOCK_DGRAM
)允许数据通过类似消息的形式交换,这种消息被称为数据报(datagram)。数据报socket中,消息具有边界,但是数据传输是不可靠的。消息到达可能是乱序,可能是有重复的,甚至可能根本没有达到。
数据报socket是更通用的无连接socket的一个例子。
在Internet domain中,数据报socket使用UDP,流式socket使用TCP。
Socket system calls
socket()
创建新socketbind()
将一个socket和一个地址绑定起来。通常来说,一个服务端使用该系统调用将它的socket和一个well-known地址绑定起来,这样客户端就可以locate the socket.listen()
系统调用允许一个流式socket从另一个socket接收到来的连接。accept()
system call accepts a connection from a peer application on a listening stream socket, and optionally returns the address of the peer socket.connect()
system call establishes a connection with another socket.可以通过read
和write
系统调用执行 socket I/O,或者使用socket-specified system calls(e.g., send(), recv(), sendto(), and recvfrom()). 默认情况下,如果IO操作没法立即完成,那么该系统调用将会阻塞。非阻塞IO也是可能的,通过使用fcntl() F_SETFL
操作可以实现O_NONBLOCK
打开文件状态标志位。
#include <sys/socket.h>
int socket(int domain, int type, int protocol);
Returns file descriptor on success, or –1 on error
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
Returns 0 on success, or –1 on error
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.
每个不同的socket domain使用不同的地址格式。比如,UNIX domain socket使用pathname,Internet domain socket使用IP地址+端口号。
struct sockaddr
目的在于将不同域下的地址格式相互转换。
struct sockaddr {
sa_family_t sa_family; /* Address family (AF_* constant) */
char sa_data[14]; /* Socket address (size varies according to socket domain) */
};
该结构体作用就像是为不同域下的地址提供了一个模板。每个地址结构体都是以family
字段开始,对应于sa_family
(sa_family_t
是在SUSv3
中定义的一个整数类型)。The value in the family
field is sufficient to determine the size and format of the address stored in the remainder of the structure.
流式socket的操作可以用电话系统来类比:
socket()
系统调用创建一个socket,类似于安装了一步电话。为了在两个应用中通信,两端都需要创建一个socket。bind()
来将一个 socket 和一个 well-known address 绑定起来,然后调用listen()
提醒内核,它愿意接收到来的连接。这一步就好像使用一个大家都知道的手机号码,并且保证我们的电话是开通的。
b. 其他应用通过调用connect()
和本地应用建立联系,specifying the address of the socket to which the connection is to be made.
c. 调用过listen()
的应用然后使用accept()
来接受连接。类似于当电话响起时拿起电话。如果accept()
是在远程应用调用connect()
之前被调用的,那么它将会阻塞。
一旦连接建立,数据可以在两个应用之间传递,直到某一方通过close()
关闭连接。之间的通信通过read()
和write()
系统调用来实现。
#include <sys/socket.h>
int listen(int sockfd, int backlog);
Returns 0 on success, or –1 on error
内核必须记录每次pending connection request的部分信息,来帮助后面执行accept()
的处理过程。backlog
参数就限定了pending connections
的数量。
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
Returns file descriptor on success, or –1 on error
理解accept的关键点在于:它创建了一个新的socket,事实上是这个新的socket负责与远程执行connect的应用进行通信。accept
的返回值是该新创建的socket file descriptor。The listening socket (sockfd) remains open, and can be used to accept further connections.
The remaining arguments to accept() return the address of the peer socket. The addr argument points to a structure that is used to return the socket address. The type of this argument depends on the socket domain (as for bind()).
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.
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
Returns 0 on success, or –1 on error
#include <sys/socket.h>
ssize_t recvfrom(int sockfd, void *buffer, size_t length, int flags,
struct sockaddr *src_addr, socklen_t *addrlen);
Returns number of bytes received, 0 on EOF, or –1 on error
ssize_t sendto(int sockfd, const void *buffer, size_t length, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
Returns number of bytes sent, or –1 on error
尽管数据报socket是无连接的,connect()
系统调用对于数据报socket依然有其作用。Calling connect() on a datagram socket causes the kernel to record a particular address as this socket’s peer。把这种socket称为 connected datagram socket。术语 unconnected datagram socket 指的是没有调用过connect
的数据报socket。
当一个datagram socket调用过connect之后:
write()(or send())
系统调用来传递数据,并且数据被自动传递给相同的peer socket. 正如sendto()
一样,每次write()
系统调用产生一个独立的数据报。peer socket
发送的数据才可以在socket上获取。The obvious advantage of setting the peer for a datagram socket is that we can use simpler I/O system calls when transmitting data on the socket. We no longer need to use sendto() with dest_addr and addrlen arguments, but can instead use write(). Setting the peer is useful primarily in an application that needs to send multiple datagrams to a single peer (which is typical of some datagram clients).
原文:https://www.cnblogs.com/hezhiqiangTS/p/11419206.html