-
1) 创建:
函数原型:
int socket(int domain, int type, int protocol);
参数说明:
domain:协议域,又称协议族(family),协议族决定了 socket 的地址类型,在通信中必须采用对应的地址
常用的协议族:
AF_INET (ipv4):用 ipv4 地址(32 位的)与端口号(16 位的)的组合
AF_INET6(ipv6):用 ipv6 地址(128 位的)与端口号(16 位的)的组合
AF_LOCAL(或称 AF_UNIX,Unix 域 Socket):用一个绝对路径名作为地址
AF_ROUTE :
type:指定 Socket 类型
常用的 socket 类型:
SOCK_STREAM(流式/TCP) :是一种面向连接的 Socket,针对于面向连接的 TCP 服务应用
SOCK_DGRAM (数据报式/UDP):是一种无连接的 Socket,对应于无连接的 UDP 服务应用
SOCK_RAW :
SOCK_PACKET :
SOCK_SEQPACKET :
protocol:指定协议
常用协议:
IPPROTO_TCP(TCP 传输协议) :
IPPROTO_UDP(UDP 传输协议) :
IPPROTO_STCP(STCP 传输协议):
IPPROTO_TIPC(TIPC 传输协议):
注意:1. type 和 protocol 不可以随意组合,如 SOCK_STREAM 不可以跟 IPPROTO_UDP 组合。当第三个参数为 0 时,
会自动选择第二个参数类型对应的默认协议。
2. Windows Socket 下 protocol 参数中不存在 IPPROTO_STCP。
返回值:
如果调用成功就返回新创建的套接字的描述符(大于 0),如果失败就返回 INVALID_SOCKET(Linux 下失败返回 -1)。
- 套接字描述符是一个整数类型的值。每个进程的进程空间里都有一个套接字描述符表,该表中存放着套接字描述符和套接字数据结构的对应关系。该表中有一个字段存放新创建的套接字的描述符,另一个字段存放套接字数据结构的地址,因此根据套接字描述符就可以找到其对应的套接字数据结构。每个进程在自己的进程空间里都有一个套接字描述符表但是套接字数据结构都是在操作系统的内核缓冲里。
-
2) 绑定:
函数原型:
int bind(int sockfd, const struct sockaddr * addr, socklen_t addrlen);
参数说明:
sockfd :套接字描述符
addr :是一个 sockaddr 结构指针,该结构中包含了要结合的地址和端口号
addrlen:addr 的长度
返回值:
如果函数执行成功,返回值为 0,否则为 -1。
当我们调用 socket() 创建一个 socket 时,返回的 socket 描述字它存在于协议族(address family,AF_XXX)空间中,但没有一个具体的地址。如果想要给它赋值一个地址,就必须调用 bind() 函数,否则就当调用 connect()、listen() 时系统会自动随机分配一个端口。
通常服务器在启动的时候都会绑定一个众所周知的地址(如 ip 地址 + 端口号),用于提供服务,客户就可以通过它来接连服务器;而客户端就不用指定,有系统自动分配一个端口号和自身的 ip 地址组合。这就是为什么通常服务器端在 listen 之前会调用 bind(),而客户端就不会调用,而是在 connect() 时由系统随机生成一个。
-
3) 监听:
函数原型:
int listen(int sockfd, int backlog);
参数说明:
sockfd :套接字描述符
backlog:socket 可以排队的最大连接个数
返回值:
如果函数执行成功,返回值为 0,否则为 -1。
- socket() 函数创建的 socket 默认是一个主动类型的,listen() 函数将 socket 变为被动类型的,等待客户的连接请求。
-
4) 接受连接:
函数原型:
int accept(int sockfd, struct sockaddr * addr, socklen_t * addrlen);
参数说明:
sockfd :监听套接字描述符
addr :返回客户端的地址
addrlen:addr 的长度
返回值:
成功返回由内核自动生成的一个全新的描述符(即连接成功的客户端的套接字描述符),失败返回 -1。
TCP 服务器端依次调用 socket()、bind()、listen() 之后,就会监听指定的 socket 地址了。TCP 客户端依次调用 socket()、connect() 之后就向 TCP 服务器发送了一个连接请求。TCP 服务器监听到这个请求之后,就会调用 accept() 函数去接收请求,这样连接就建立好了。之后就可以开始网络 I/O 操作了,即类同于普通文件的读写 I/O 操作。
accept() 的第一个参数为服务器的 socket 描述字,是服务器开始调用 socket() 函数生成的,称为监听 socket 描述符;而 accept() 函数返回的是已连接的 socket 描述字。一个服务器通常通常仅仅只创建一个监听 socket 描述字,它在该服务器的生命周期内一直存在。内核为每个由服务器进程接受的客户连接创建了一个已连接 socket 描述符,当服务器完成了对某个客户的服务,相应的已连接 socket 描述符就被关闭。
-
5) 连接:
函数原型:
int connect(int sockfd, const struct sockaddr * addr, socklen_t addrlen);
参数说明:
sockfd :套接字描述符
addr :指定数据发送的目的地,也就是服务器端的地址
addrlen:addr 的长度
返回值:
如果函数执行成功,返回值为 0,否则为 -1。
-
6) 断开连接:
函数原型:
int close(int sockfd);
参数说明:
sockfd:套接字描述符
返回值:
成功返回客户端的文件描述符,失败返回 -1。
-
7) 发送数据:
函数原型:
ssize_t write(int sockfd, const void * buf, size_t size);
参数说明:
sockfd:套接字描述符
buf :发送内容地址,message.UTF8String 将字符串转换成 UTF8 的 ASCII 码,一个汉字需要 3 个字节
size :发送内容长度,是字节的个数,需使用 strlen() 计算所有字节的长度
返回值:
如果成功,则返回发送的字节数,失败则返回 -1,并设置 errno 变量。
如果错误为 EINTR 表示在写的时候出现了中断错误。如果为 EPIPE 表示网络连接出现了问题(对方已经关闭了连接)。
函数原型:
ssize_t send(int sockfd, const void * buf, size_t size, int flags);
参数说明:
sockfd:套接字描述符
buf :发送内容地址,message.UTF8String 将字符串转换成 UTF8 的 ASCII 码,一个汉字需要 3 个字节
size :发送内容长度,是字节的个数,需使用 strlen() 计算所有字节的长度
flags :发送方式标志,一般为 0
返回值:
如果成功,则返回发送的字节数,失败则返回 -1。
函数原型:
ssize_t sendto(int sockfd, const void * buf, size_t size, int flags, const struct sockaddr * dest_addr, socklen_t addrlen);
参数说明:
sockfd :套接字描述符
buf :待发送数据的缓冲区
size :缓冲区长度,是字节的个数,需使用 strlen() 计算所有字节的长度
flags :调用方式标志位, 一般为 0, 改变 Flags,将会改变 Sendto 发送的形式
dest_addr:可选指针,指向目的套接字的地址
addrlen :dest_addr 的长度
返回值:
如果成功,则返回发送的字节数,失败则返回 -1。
函数原型:
ssize_t sendmsg(int sockfd, const struct msghdr * msg, int flags);
参数说明:
sockfd:套接字描述符
msg :送数据的内容
flags :调用方式标志位
返回值:
如果成功,则返回发送的字节数,失败则返回 -1。
-
8) 接收数据:
函数原型:
ssize_t read(int sockfd, void * buf, size_t size);
参数说明:
sockfd:套接字描述符
buf :用于接收数据的缓冲区
size :缓冲区长度
返回值:
如果成功,返回实际所读的字节数,如果返回的值是 0 表示已经读到文件的结束了,小于 0 表示出现了错误。
如果错误为 EINTR 说明读是由中断引起的,如果是 ECONNREST 表示网络连接出了问题。
函数原型:
ssize_t recv(int sockfd, void * buf, size_t size, int flags);
参数说明:
sockfd:套接字描述符
buf :用于接收数据的缓冲区
size :缓冲区长度
flags :指定调用方式
取值:
MSG_PEEK:查看当前数据,数据将被复制到缓冲区中,但并不从输入队列中删除;
MSG_OOB :指示接收到 out-of-band 数据(即需要优先处理的数据)。
返回值:
如果成功,返回实际所读的字节数,如果返回的值是 0 表示已经读到文件的结束了,小于 0 表示出现了错误。
函数原型:
ssize_t recvfrom(int sockfd, void * buf, size_t size, int flags, struct sockaddr * src_addr, socklen_t * addrlen);
参数说明:
sockfd :套接字描述符
buf :接收数据缓冲区
size :缓冲区长度
flags :调用操作方式。是以下一个或者多个标志的组合体,可通过 or 操作连在一起:
MSG_DONTWAIT:操作不会被阻塞
MSG_ERRQUEUE:指示应该从套接字的错误队列上接收错误值,依据不同的协议,错误值以某种辅佐性消息的方式传递进来,
使用者应该提供足够大的缓冲区。导致错误的原封包通过 msg_iovec 作为一般的数据来传递。导致错误
的数据报原目标地址作为 msg_name 被提供。错误以 sock_extended_err 结构形态被使用
MSG_PEEK :指示数据接收后,在接收队列中保留原数据,不将其删除,随后的读操作还可以接收相同的数据
MSG_TRUNC :返回封包的实际长度,即使它比所提供的缓冲区更长, 只对 packet 套接字有效
MSG_WAITALL :要求阻塞操作,直到请求得到完整的满足。然而,如果捕捉到信号,错误或者连接断开发生,或者下次被
接收的数据类型不同,仍会返回少于请求量的数据
MSG_EOR :指示记录的结束,返回的数据完成一个记录
MSG_CTRUNC :指明由于缓冲区空间不足,一些控制数据已被丢弃
MSG_OOB :指示接收到 out-of-band 数据(即需要优先处理的数据)
MSG_ERRQUEUE:指示除了来自套接字错误队列的错误外,没有接收到其它数据
src_addr:可选指针,指向装有源地址的缓冲区
addrlen :可选指针,指向 address 缓冲区长度值
返回值:
如果成功,返回实际所读的字节数,如果返回的值是 0 表示已经读到文件的结束了,小于 0 表示出现了错误。
函数原型:
ssize_t recvmsg(int sockfd, struct msghdr * msg, int flags);
参数说明:
sockfd:套接字描述符
buf :用于接收数据的缓冲区
size :缓冲区长度
返回值:
如果成功,返回实际所读的字节数,如果返回的值是 0 表示已经读到文件的结束了,小于 0 表示出现了错误。
-
9) 获取本地协议地址:
函数原型:
int getsockname(int sockfd, struct sockaddr * addr, socklen_t * addrlen);
参数说明:
sockfd :套接字描述符
addr :返回本地协议的地址
addrlen:addr 的长度
返回值:
成功返回 0,失败返回 1。
-
10) 获取远程协议地址:
函数原型:
int getpeername(int sockfd, struct sockaddr * addr, socklen_t * addrlen);
参数说明:
sockfd :套接字描述符
addr :返回远程协议的地址
addrlen:addr 的长度
返回值:
成功返回 0,失败返回 1。
-
11) 获取套接字选项当前值:
函数原型:
int getsockopt(int sockfd, int level, int optname, void * optval, socklen_t * optlen);
参数说明:
sockfd :套接字描述符
level :选项定义的层次。例如,支持的层次有 SOL_SOCKET、IPPROTO_TCP。、
optname:需获取的套接字选项
optval :指向存放所获得选项值的缓冲区
optlen :指向 optval 缓冲区的长度值
返回值:
成功返回 0。