TCP 套接字编程
服务端
创建 TCP 通信相对麻烦, 对于服务端来说:
- 首先需要指定通信协议(socket),
- 然后将 本地 ip 地址和端口号(比如 http 服务的 80 端口)绑定成一个主动套接字(bind), 这个主动套接字是准备调用 connect 向服务端发起连接的客户端套接字.
- 将主动套接字转为等待连接的被动套接字, 即磨合出一个可以监听客户端请求的套接字(listen)
- 万事俱备, 此时开始阻塞, 等待客户端连接(accept)
- 客户端连接到达, 读, 写, 读, 写即可.
客户端
对于客户端而言:
1. 首先需要指定通信协议(socket)
2. 然后将 本地 ip 地址和端口号绑定成一个主动套接字(bind), 这个主动套接字是准备调用 connect 向服务端发起连接的客户端套接字. 由于客户端使用什么端口和服务端通信并不重要, 故而可以省略, (服务端如果端口多变, 客户端会找不到服务的).
3. 建立连接 (connect)
4. 万事俱备, 客户端读, 写, 读, 写即可.
函数详解
- socket()
int socket(int family, int type, int protocol)
socket() 函数用于创建套接字,成功时返回一个小的非负整数,我们称之为套接字描述符 socket descriptor, sockfd (类似于文件描述符). 本套接字描述符用来指定协议(ipv4/6), 套接字类型(字节流, 数据报), 传输协议.
family: 协议族
type: 套接字类型
protocol: 传输协议 TCP, UDP SCTP
- connect()
int connect(int sockfd, const struct scokaddr *servaddr, socklen_t addrlen)
connect()函数用于通信中的主动方向被动方发起建立连接的请求。对于 TCP 套接字来说,connect()函数会发起 TCP 的三路握手.
- bind()
int bind(int socket, const struct sockaddr *address, size_t address_len);
+ sockfd:套接字描述符,由socket()函数返回的.
+ address:指向对端套接字地址结构的指针.
+ addrlen:对端套接字地址结构的大小.
在套接字编程中,bind() 函数通知应用程序,数据从哪个端口发送,以及应该从哪个端口接收数据。一些应用程序可能没有调用 bind() 函数来显示指定本地协议地址, TCP 客户端通常不会调用 bind() 指定本地协议地址,但是内核会为 TCP 客户端选择一个临时端口. 然而对于 TCP 服务端而言,需要指定本地协议地址, 一遍客户端能找到指定的服务端口.
- listen()
int listen(int sockfd, int backlog);
+ sockfd: 套接字描述符,由前面的socket()函数成功时返回.
+ backlog: 指定内核应该为相应的套接字排队的最大连接个数.
listen() 函数仅在 TCP 服务端调用,主要有完成两项任务:
1. 将一个未连接的套接字转换为被动套接字,指示内核应该接收指向该套接字的连接请求。(socket()函数创建套接字时,默认设为主动套接字)
2. 指定内核应该为相应的套接字排队的最大连接个数。
- accept() 函数
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
+ sockfd: 内核创建的新的套接字描述符,用于描述与返回的客户端之间的连接
+ addr:对端进程(客户端)的协议地址
+ addrlen: 对端协议地址结构的大小。
accept() 函数仅被 TCP 服务端调用,用于返回下一个已完成连接。如果已完成连接队列为空,则阻塞之.