2、Socket编程入门:什么是Socket,Socket类型(流式、数据报),Socket编程的基本流程。
2.1 到底什么是Socket?
很多初学者一上来就被「Socket」这个词唬住了。说白了,它就是网络通信的一个「门」。
你想想看,两台电脑要聊天,总得有个入口吧?Socket就是操作系统提供的一个编程接口,让你能通过代码往网络里发数据、从网络里收数据。
我个人习惯把Socket理解成「电话插座」。你家里墙上那个电话插孔,就是Socket。你插上电话机,拨号,对方接听,就能通话了。网络编程里,Socket就是那个插孔,IP地址是门牌号,端口号是房间号。
核心定义:Socket是应用层与传输层之间的抽象层,它封装了TCP/IP协议的复杂性,让你用几行代码就能完成网络通信。
我在项目中遇到过不少新手,上来就问:「Socket是不是就是TCP?」嗯,这里要注意——Socket不是协议,是接口。TCP和UDP是底层协议,Socket是操作这些协议的工具。
2.2 Socket的两种类型
Socket分两种,对应TCP和UDP。我刚开始学的时候也搞混过,后来用了一个比喻就记住了。
2.2.1 流式Socket(SOCK_STREAM)
流式Socket对应TCP协议。它就像两个人打电话——先建立连接,再通话,挂断后结束。
- 面向连接:通信前必须三次握手
- 可靠传输:数据不会丢,不会乱序
- 无边界:数据像水流一样,没有明确的分隔
这里有个坑,我必须要说。流式Socket的「无边界」特性,直接导致了粘包问题。你发两次数据,对方可能一次就收完了。这就是我们后面要重点讲的粘包拆包。
避坑指南:我曾经在一个即时通讯项目里,客户端连续发了三条消息,服务端一次全收走了。用户看到的是三条消息粘在一起,根本没法看。从那以后,我每次用流式Socket都会加消息边界处理。
2.2.2 数据报Socket(SOCK_DGRAM)
数据报Socket对应UDP协议。它就像寄快递——你只管把包裹扔出去,对方能不能收到、什么时候收到,你不管。
- 无连接:不需要建立连接,直接发
- 不可靠:可能丢包,可能乱序
- 有边界:每次发送都是一个独立的数据报
你可能会问:「不可靠的东西有什么用?」其实很多场景不在乎丢包,比如视频直播、在线游戏。丢一帧画面,用户根本感觉不到。但如果你用TCP,重传机制反而会导致延迟。
| 特性 | 流式Socket(TCP) | 数据报Socket(UDP) |
|---|---|---|
| 连接方式 | 面向连接 | 无连接 |
| 可靠性 | 可靠,保证到达 | 不可靠,可能丢包 |
| 数据边界 | 无边界(流式) | 有边界(数据报) |
| 速度 | 相对较慢 | 快 |
| 典型应用 | HTTP、FTP、邮件 | DNS、视频流、游戏 |
2.3 Socket编程的基本流程
不管用哪种Socket,编程流程都差不多。我总结了一个四步法,你记住这个,写代码就有方向了。
2.3.1 服务端流程
- 创建Socket:调用socket()函数,指定协议族和类型
- 绑定地址:调用bind(),把Socket和IP、端口绑在一起
- 监听连接:调用listen(),告诉内核可以接受连接了
- 接受连接:调用accept(),阻塞等待客户端连上来
嗯,这里要注意,accept()是阻塞的。如果没有客户端连接,程序会停在这里不动。我第一次写服务端时,以为程序卡死了,查了半天才发现是正常行为。
2.3.2 客户端流程
- 创建Socket:同样调用socket()
- 连接服务端:调用connect(),指定服务端的IP和端口
- 收发数据:用send()和recv()通信
- 关闭连接:调用close()
服务端和客户端的区别,说白了就是:服务端等着别人来找它,客户端主动去找别人。
2.3.3 代码示例(C语言风格)
// 服务端示例
int server_fd = socket(AF_INET, SOCK_STREAM, 0); // 创建Socket
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
addr.sin_addr.s_addr = INADDR_ANY;
bind(server_fd, (struct sockaddr*)&addr, sizeof(addr)); // 绑定
listen(server_fd, 5); // 监听
int client_fd = accept(server_fd, NULL, NULL); // 接受连接
// 然后就可以用client_fd收发数据了
// 客户端示例
int client_fd = socket(AF_INET, SOCK_STREAM, 0); // 创建Socket
struct sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(8080);
inet_pton(AF_INET, "127.0.0.1", &server_addr.sin_addr);
connect(client_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)); // 连接
// 然后就可以用client_fd收发数据了
个人经验:我建议初学者先用Python的socket模块练手,语法更简洁,能快速理解流程。等流程跑通了,再切换到C语言深入底层。
2.4 核心知识体系
下面这张图是我自己画的,把Socket编程的核心逻辑串起来了。你看一遍,心里就有谱了。
这张图把Socket的类型和流程都串起来了。你从左往右看,先搞清楚用哪种Socket,再按流程写代码。顺序别搞反了——我见过有人先写代码再选类型,结果推倒重来。
一句话总结:Socket是网络编程的基石。流式Socket可靠但有粘包问题,数据报Socket快但不保证到达。选哪个,看你的业务能不能接受丢包。
好了,这一章的内容就这些。记住Socket的本质,理解两种类型的区别,把编程流程刻在脑子里。下一章我们会深入代码,手把手写一个完整的Socket通信程序。
可可精品资料库