一、 客户端的核心动作:Connect
在 TCP 握手过程中,服务器是被动打开(Passive Open),而客户端是主动打开(Active Open)。发起这个“主动”动作的函数就是connect。
1. 函数原型
intconnect(intsockfd,conststructsockaddr*addr,socklen_taddrlen);- sockfd:客户端自己创建的套接字。
- addr:重点!这里填的是对方(服务器)的 IP 和端口。
- addrlen:地址结构体的大小。
2. 隐式绑定 (Implicit Binding)
你可能会问:服务器需要 bind 固定端口,客户端需要吗?
- 不需要。客户端通常不关心自己用什么端口(只要能连上服务器就行)。
- 机制:当你调用
connect时,内核会自动为客户端分配一个临时的、未被占用的端口(例如 54321)。这就是隐式绑定。
3. 三次握手
当connect函数被调用时,客户端的协议栈会正式向服务器发送SYN 包,发起 TCP 三次握手。
- 返回 0:握手成功,连接建立。
- 返回 -1:握手失败(如服务器没开,返回
ECONNREFUSED)。
二、 发送数据:Write vs Send
连接建立后,客户端就可以通过sockfd向服务器发送数据了。
1. 函数对比
write(fd, buf, len):- 通用文件 IO 函数。
- 用法简单,适合大多数场景。
send(fd, buf, len, flags):- Socket 专用函数。
- 多了一个
flags参数。如果flags为 0,等同于write。
2. 发送原理(再次强调内核缓冲区)
调用write或send时,数据并不是立即飞到了网线上。
- 步骤 1:程序把数据从用户态内存拷贝到内核的写缓冲区。
- 步骤 2:内核的 TCP 协议栈负责在合适的时机,把写缓冲区的数据打包发给对方。
- 意义:
write返回成功,只代表“数据成功交给了内核”,不代表对方一定收到了。
3. 关于字符串的小细节
发送字符串 “Hello” 时,len该填多少?
strlen("Hello")= 5:发送Hello。sizeof("Hello")= 6:发送Hello\0。
建议:通常文本协议不需要发送
\0,接收端根据读取长度处理即可。但如果双方约定以\0结尾,则需要发送长度+1。
三、 代码实战:编写一个 TCP 客户端
下面的代码实现了一个完整的客户端,它会连接本地的 8888 端口,发