写点什么

Android C++ 系列:Linux Socket 编程(二)网络套接字函数

作者:轻口味
  • 2021 年 12 月 14 日
  • 本文字数:2498 字

    阅读完需:约 8 分钟

1. socket

#include <sys/types.h> /* See NOTES */ #include <sys/socket.h>int socket(int domain, int type, int protocol);
复制代码


  • domain:

  • AF_INET 这是大多数用来产生 socket 的协议,使用 TCP 或 UDP 来传输,用 IPv4 的地址

  • AF_INET6 与上面类似,不过是来用 IPv6 的地址

  • AF_UNIX 本地协议,使用在 Unix 和 Linux 系统上,一般都是当客户端和服务器在同一台及其上的时候使用

  • type:

  • SOCK_STREAM 这个协议是按照顺序的、可靠的、数据完整的基于字节流的连接。这是一个使用最多的 socket 类型,这个 socket 是使用 TCP 来进行传输。

  • SOCK_DGRAM 这个协议是无连接的、固定长度的传输调用。该协议是不可靠的,使用 UDP 来进行它的连接。 SOCK_SEQPACKET 这个协议是双线路的、可靠的连接,发送固定长度的数据包进行传输。必须把这个包完整的接受才能进行读取。

  • SOCK_RAW 这个 socket 类型提供单一的网络访问,这个 socket 类型使用 ICMP 公共协议。(ping、traceroute 使用该协议)

  • SOCK_RDM 这个类型是很少使用的,在大部分的操作系统上没有实现,它是提供给数据链路层使用,不保证数

  • 据包的顺序 protocol:

  • 0 默认协议

  • 返回值:

  • 成功返回一个新的文件描述符,失败返回-1,设置 errno


socket()打开一个网络通讯端口,如果成功的话,就像 open()一样返回一个文件描 述符,应用程序可以像读写文件一样用 read/write 在网络上收发数据,如果 socket()调 用出错则返回-1。对于 IPv4,domain 参数指定为 AF_INET。对于 TCP 协议,type 参数指定为 SOCK_STREAM,表示面向流的传输协议。如果是 UDP 协议,则 type 参数指定为 SOCK_DGRAM,表 示面向数据报的传输协议。protocol 参数的介绍从略,指定为 0 即可。

2. bind

#include <sys/types.h> /* See NOTES */ #include <sys/socket.h>int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
复制代码


  • sockfd: socket 文件描述符

  • addr: 构造出 IP 地址加端口号

  • addrlen: sizeof(addr)长度

  • 返回值:

  • 成功返回 0,失败返回-1, 设置 errno


服务器程序所监听的网络地址和端口号通常是固定不变的,客户端程序得知服务器程序的地址和端口号后就可以向服务器发起连接,因此服务器需要调用 bind 绑定一个固定的网络地址和端口号。


bind()的作用是将参数 sockfd 和 addr 绑定在一起,使 sockfd 这个用于网络通讯的文件 描述符监听 addr 所描述的地址和端口号。前面讲过,struct sockaddr *是一个通用指针类 型,addr 参数实际上可以接受多种协议的 sockaddr 结构体,而它们的长度各不相同,所以需 要第三个参数 addrlen 指定结构体的长度。如:


struct sockaddr_in servaddr;bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(8000);
复制代码


首先将整个结构体清零,然后设置地址类型为 AF_INET,网络地址为 INADDR_ANY,这个 宏表示本地的任意 IP 地址,因为服务器可能有多个网卡,每个网卡也可能绑定多个 IP 地址, 这样设置可以在所有的 IP 地址上监听,直到与某个客户端建立了连接时才确定下来到底用哪 个 IP 地址,端口号为 8000。

3. listen

#include <sys/types.h> /* See NOTES */ #include <sys/socket.h>int listen(int sockfd, int backlog);
复制代码


  • sockfd:socket 文件描述符

  • backlog:排队建立 3 次握手队列和刚刚建立 3 次握手队列的链接数和


查看系统默认 backlog


 cat /proc/sys/net/ipv4/tcp_max_syn_backlog
复制代码


典型的服务器程序可以同时服务于多个客户端,当有客户端发起连接时,服务器调用的 accept()返回并接受这个连接,如果有大量的客户端发起连接而服务器来不及处理,尚未 accept 的客户端就处于连接等待状态,listen()声明 sockfd 处于监听状态,并且最多允许有 backlog 个客户端处于连接待状态,如果接收到更多的连接请求就忽略。listen()成功返回 0,失败返回-1。

4. accept

#include <sys/types.h> /* See NOTES */ #include <sys/socket.h>int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
复制代码


  • sockdf:socket 文件描述符

  • addr:传出参数,返回链接客户端地址信息,含 IP 地址和端口号

  • addrlen:传入传出参数(值-结果),传入 sizeof(addr)大小,函数返回时返回真正接收到地址结构体的大小

  • 返回值:成功返回一个新的 socket 文件描述符,用于和客户端通信,失败返回-1,设置 errno


三方握手完成后,服务器调用 accept()接受连接,如果服务器调用 accept()时还没有 客户端的连接请求,就阻塞等待直到有客户端连接上来。addr 是一个传出参数,accept() 返回时传出客户端的地址和端口号。addrlen 参数是一个传入传出参数(value-result argument),传入的是调用者提供的缓冲区 addr 的长度以避免缓冲区溢出问题,传出的是客 户端地址结构体的实际长度(有可能没有占满调用者提供的缓冲区)。如果给 addr 参数传 NULL,表示不关心客户端的地址。


我们的服务器程序结构是这样的:


while (1) {  cliaddr_len = sizeof(cliaddr);  connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);  n = read(connfd, buf, MAXLINE);  ......  close(connfd);}
复制代码


整个是一个 while 死循环,每次循环处理一个客户端连接。由于 cliaddr_len 是传入传出 参数,每次调用 accept()之前应该重新赋初值。accept()的参数 listenfd 是先前的监听文件 描述符,而 accept()的返回值是另外一个文件描述符 connfd,之后与客户端之间就通过这个 connfd 通讯,最后关闭 connfd 断开连接,而不关闭 listenfd,再次回到循环开头 listenfd 仍 然用作 accept 的参数。accept()成功返回一个文件描述符,出错返回-1。

5. connect

#include <sys/types.h> /* See NOTES */ #include <sys/socket.h>int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
复制代码


  • sockdf:socket 文件描述符

  • addr:传入参数,指定服务器端地址信息,含 IP 地址和端口号

  • addrlen:传入参数,传入 sizeof(addr)大小

  • 返回值:成功返回 0,失败返回-1,设置 errno


客户端需要调用 connect()连接服务器,connect 和 bind 的参数形式一致,区别在于 bind 的参数是自己的地址,而 connect 的参数是对方的地址。connect()成功返回 0,出错返 回-1。

6. 总结

本文介绍了网络套接字函数:socket、bind、listen、accept、connect 等。

发布于: 2 小时前阅读数: 6
用户头像

轻口味

关注

🏆2021年InfoQ写作平台-签约作者 🏆 2017.10.17 加入

Android、音视频、AI相关领域从业者。 邮箱:qingkouwei@gmail.com

评论

发布
暂无评论
Android C++系列:Linux Socket编程(二)网络套接字函数