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 服务端流程

  1. 创建Socket:调用socket()函数,指定协议族和类型
  2. 绑定地址:调用bind(),把Socket和IP、端口绑在一起
  3. 监听连接:调用listen(),告诉内核可以接受连接了
  4. 接受连接:调用accept(),阻塞等待客户端连上来

嗯,这里要注意,accept()是阻塞的。如果没有客户端连接,程序会停在这里不动。我第一次写服务端时,以为程序卡死了,查了半天才发现是正常行为。

2.3.2 客户端流程

  1. 创建Socket:同样调用socket()
  2. 连接服务端:调用connect(),指定服务端的IP和端口
  3. 收发数据:用send()和recv()通信
  4. 关闭连接:调用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,再按流程写代码。顺序别搞反了——我见过有人先写代码再选类型,结果推倒重来。

一句话总结:Socket是网络编程的基石。流式Socket可靠但有粘包问题,数据报Socket快但不保证到达。选哪个,看你的业务能不能接受丢包。

好了,这一章的内容就这些。记住Socket的本质,理解两种类型的区别,把编程流程刻在脑子里。下一章我们会深入代码,手把手写一个完整的Socket通信程序。

可可精品资料库