Sockets: Introduction

Sockets: Introduction


Overview

fd = socket(domain, type, protocol);

Communication donmains
每个socket必须存在于一个communication domain中,该域决定了:

  • 标识一个socket的方法(一个socket地址的格式);
  • 通信的范围(比如在同一个主机上不同应用之间的通信,或者通过网络在不同主机上的通信)

现代操作系统支持如下域:

  • AF_UNIX域允许在同一个主机上不同的应用通信。
  • AF_INET允许应用通过IPv4协议网络在不同的主机之间通信
  • AF_INET6

AF是address family的缩写,PF是protocol family的缩写。

Socket types
stream and datagram.

流式socket(SOCK_STREAM),提供了可靠的、双向的、字节流的通信通道。意即:

  • Reliable 保证传输的数据一定会到达接收应用。如果失败则会得到提示。
  • Bidirectional
  • Byte-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。

扫描二维码关注公众号,回复: 7110692 查看本文章

Socket system calls

  • socket()创建新socket
  • bind()将一个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.

可以通过readwrite系统调用执行 socket I/O,或者使用socket-specified system calls(e.g., send(), recv(), sendto(), and recvfrom()). 默认情况下,如果IO操作没法立即完成,那么该系统调用将会阻塞。非阻塞IO也是可能的,通过使用fcntl() F_SETFL操作可以实现O_NONBLOCK打开文件状态标志位。

Creating a Socket: socket()

#include <sys/socket.h>
int socket(int domain, int type, int protocol);
    Returns file descriptor on success, or –1 on error

Binding a Socket to an Address: bind()

#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.

Generic Socket Address Structures: struct sockaddr

每个不同的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.

Stream Sockets

流式socket的操作可以用电话系统来类比:

  1. socket()系统调用创建一个socket,类似于安装了一步电话。为了在两个应用中通信,两端都需要创建一个socket。
  2. 一个应用通过它的socket来和另一个应用的socket通信的签提条件是通信可以发生。两个socket通过如下过程进行连接: a. 应用通过调用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()之前被调用的,那么它将会阻塞。

  3. 一旦连接建立,数据可以在两个应用之间传递,直到某一方通过close()关闭连接。之间的通信通过read()write()系统调用来实现。

Listening for Incoming Connections: listen()

#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的数量。

Accepting a Connection: accept()

#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()).

Connecting to a Peer Socket: connect()

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

I/O on Stream Sockets

Datagram Sockets

Exchanging Datagrams: recvfrom() and sendto()

#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

Using connect() with Datagram Sockets

尽管数据报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).

猜你喜欢

转载自www.cnblogs.com/hezhiqiangTS/p/11419206.html