网络:3.Socket编程TCP
Socket编程TCP
一.接口补充
1. listen(系统调用)
作用:
将一个主动套接字(客户端用的 socket)转换为被动套接字,使其可以接受来自客户端的连接请求。(常用于 TCP 服务器端,UDP 无需调用该函数)
函数原型
#include <sys/types.h>
#include <sys/socket.h>int listen(int sockfd, int backlog);
-
参数
- sockfd:TCP 套接字文件描述符;
- backlog:未完成连接队列(半连接队列) 的最大长度;表示允许系统为该监听 socket 同时排队的最大连接请求数;超过该值的连接请求将被丢弃;
-
返回值
- 成功: 0
- 失败: -1,并设置 errno
2. accept(系统调用)
作用:从已完成连接队列中取出一个已建立的客户端连接,生成一个新的套接字用于与该客户端通信 (常用于 TCP 服务器端;UDP 无连接,不需要调用此函数).
函数原型
#include <sys/types.h>
#include <sys/socket.h>int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
//没有连接就会阻塞卡在这里
-
参数
- sockfd:服务器端用于监听的套接字文件描述符(必须已调用bind和listen);
- addr(输出型参数):
- 指向一个
struct sockaddr_in
结构体的指针,用于保存已连接客户端的地址信息(IP + 端口); - 若不关心客户端地址,可填
NULL
。
- 指向一个
- addrlen(输出型参数):
- 指向一个
socklen_t
类型变量的指针,初始时应设置为sizeof(struct sockaddr_in)
; - 函数返回时,会被修改为实际的地址结构长度。
- 指向一个
-
返回值
- 成功: 返回一个新的套接字文件描述符(用于与客户端通信);
- 失败: 返回 -1,并设置errno。
3. connect(系统调用)
作用: 用于客户端向服务器发起连接请求; 当服务器端调用了 listen()
并处于监听状态后,客户端使用 connect()
来建立 TCP 连接(三次握手);(常用于 TCP 客户端;UDP 客户端也可调用该函数以指定默认通信目标地址.)
函数原型
#include <sys/types.h>
#include <sys/socket.h>int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
-
参数
- sockfd:套接字文件描述符;
- 通常是:
socket(AF_INET, SOCK_STREAM, 0)
(TCP); - 对于 UDP(
SOCK_DGRAM
),调用connect
并不会真正建立连接,而是指定默认的目标地址。
- 通常是:
- addr: 指向服务器端地址结构体的指针(struct sockaddr_in server; 使用时需强制类型转换为
struct sockaddr*
); - addrlen:地址结构体的大小,
sizeof(struct sockaddr_in)
。
- sockfd:套接字文件描述符;
-
返回值
- 成功: 0
- 失败: -1,并设置 errno
-
注:
client会在connect成功之后,在底层自动进行bind!
4. popen(库函数)
作用:
创建一个管道,与一个子进程之间建立单向通信通道,并通过该子进程执行指定的命令(通常是 shell 命令)。
它相当于同时调用了 fork()
+ pipe()
+ exec()
,让父进程可以像读写文件一样与命令交互。
函数原型
#include <stdio.h>FILE *popen(const char *command, const char *type);
-
参数
- **command:**要执行的 shell 命令字符串,例如
"ls -l"
,"cat /etc/passwd"
等;- 实际上会调用
/bin/sh -c command
来执行; - 因此几乎所有命令行都可以写在这里。
- 实际上会调用
- type: 打开管道的模式
"r"
:从命令的标准输出(stdout) 读取数据(父进程读,子进程写);一般用这个"w"
:向命令的标准输入(stdin) 写入数据(父进程写,子进程读)。
- **command:**要执行的 shell 命令字符串,例如
-
返回值
- 成功: 返回一个
FILE*
类型的文件流指针; - 失败: 返回
NULL
,并设置errno
。
- 成功: 返回一个
-
注意事项
管道使用完毕后,必须调用:
int pclose(FILE *stream);
来关闭管道并等待子进程结束(否则会产生僵尸进程)。
二.命令补充
1. telnet
作用:用于测试服务器端口是否可连通、是否在监听, 常用于验证TCP服务是否正常工作, 它通过建立 TCP 连接的方式模拟客户端访问。
命令格式
telnet [主机名或IP地址] [端口号]
- 返回结果说明
情况 | 输出提示 | 含义 |
---|---|---|
成功连接 | Connected to xxx.xxx.xxx.xxx | 说明端口已开放且可通信 |
连接失败 | Connection refused | 端口未被监听或被防火墙拦截** |
超时 | Connection timed out | 网络不可达 / 服务器无响应 |
未解析 | Unknown host | 域名错误或 DNS 无法解析 |
- 按下:
Ctrl + ]
, 回车,开始输入 - 然后输入:
quit
,退出 - 或再按下
Ctrl + ]
, 回车,退出。
三.知识补充
1. TCP 服务器中 listen 和 accept 的分工关系:
listen
的socket(_listensockfd):
就像“店员张三在门口拉客”,只负责等待并接受新的连接请求(即负责“拉人进来”)。
它本身不提供服务,不进行读写操作。accept
返回的新socket(sockfd):
就像张三把客人带进来后,交给店里的人(李四、王五、赵六)去“提供具体服务”。
每个客户端连接都会生成一个新的 socket,用于 读写数据(I/O 服务)。