网络间通信---TCP

一、进程间通信方式回顾

1. 同一主机(单机内部通信)

传统方式:

  • 管道:

    • 有名管道(FIFO)

    • 无名管道(pipe)

  • 信号(signal)

IPC方式(System V IPC):

  • 共享内存(share memory)

  • 信号量(semaphore)

  • 消息队列(message queue)

2. 不同主机(网络通信)

  • Socket(套接字):用于不同主机之间的进程通信

二、网络通信核心问题

举例说明:

  • 西安的 PC(QQ_A)←→ 成都的 PC(QQ_B)

需要解决两个核心问题:

  1. 找到对方的电脑 → 需要 IP 地址

  2. 找到电脑中的进程(QQ 程序)→ 需要 端口号(Port)


三、OSI 七层网络模型

OSI 七层模型(理想模型,尚未完全实现):


四、TCP/IP 模型(实际工业标准)


五、Linux 系统中的网络层次对应关系

如何通过编程实现网络通信?

复制代码
[用户空间]  ←→  应用层(应用程序)
------------------------------------ Socket 接口(套接字)
[内核空间]  ←→  传输层(TCP/UDP)
                网络层(IP)
                链路层(网卡驱动)
                物理层(硬件)

如何实现应用层代码?

  • 通过 Socket 函数接口

六、Socket 编程基础

Socket 是什么?

  1. socket() 是一个函数接口,用来使用网络功能

  2. Linux 一切皆文件的思想:socket 也是一种特殊的文件,专门用来进行网络通信

网络编程两大协议:

  • 基于 TCP 协议

  • 基于 UDP 协议


七、TCP 协议

TCP(Transmit Control Protocol,传输控制协议)

类比:打电话

TCP 三大特点:

  1. 面向连接:通信前必须建立好逻辑通路

  2. 可靠传输(四个"无"):

    • 无丢失

    • 无失序

    • 无差错

    • 无重复

  3. 字节流:有好处也有问题

TCP 三次握手(建立连接):

复制代码
客户端(Client)          服务器(Server)
    1.  ----- SYN(我要通话)----→
    2.  ←--- SYN+ACK(嗯,我知道了,你可以)----
    3.  ----- ACK(嗯,好的)----→

TCP 应用场合:

  1. 对可靠性要求较高的场合

  2. QQ、微信等软件的登录过程


八、UDP 协议

UDP(User Datagram Protocol,用户数据报协议)

类比:生活中的广播

UDP 三大特点:

  1. 无连接:数据发送时不需要建立连接

  2. 不保证可靠传输

  3. 数据报:一包一包发送

UDP 应用场合:

  1. 小数据量,但对速度要求较高

    • QQ 即时文本信息

    • 语音通话

    • 实时性要求较高的场合

  2. 广播、组播

    • 电子教室(VNC 广播)
  3. 无线网络传输


九、网络编程模型

三种模型:

  1. C/S 模型(Client/Server,客户端/服务器模型) --- 重点(本次学习的)

  2. B/S 模型(Browser/Server,浏览器/服务器模型)

  3. P2P 模型(Peer to Peer,点对点模型)

    • 正在使用 P2P 加速技术

TCP C/S 通信模型(重点)

两个阶段:

  1. 建立连接(三次握手)

  2. 通信(数据收发)


十、TCP 客户端/服务器编程流程

客户端流程(主动方):

复制代码
1. socket()     // 买个手机
2. bind()       // 可选的,一般不绑定
3. connect()    // 拨打电话
4. read() / write()
5. close()

服务器端流程(被动方):

复制代码
1. socket()     // 买个手机
2. bind()       // 插 SIM 卡,绑定服务器地址信息
3. listen()     // 待机,监听是否有客户端连接
4. accept()     // 接听电话,处理客户端连接,完成连接建立
5. read() / write()
6. close()

十一、Socket 函数

1. socket() 函数

复制代码
#include <sys/types.h>
#include <sys/socket.h>

int socket(int domain, int type, int protocol);

功能: 创建通信的一端

参数:

  • @domain:地址族

    • AF_UNIX, AF_LOCAL:本地通信

    • AF_INET:IPv4 互联网协议

    • AF_INET6:IPv6 互联网协议

  • @type:套接字类型

    • SOCK_STREAM:流式套接字(TCP)

      • 提供有序、可靠、双向、基于连接的字节流

      • 支持带外数据传输

    • SOCK_DGRAM:数据报套接字(UDP)

      • 支持数据报(无连接、不可靠、固定最大长度)
  • @protocol:协议类型

    • TCP 协议

    • UDP 协议

    • 填 0 表示使用默认协议

返回值:

  • 成功:返回 socket 的文件描述符

  • 失败:返回 -1,并设置 errno


2. connect() 函数(客户端)

复制代码
#include <sys/types.h>
#include <sys/socket.h>

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

功能: 发起连接请求(拨打电话)

参数:

  • @sockfd:使用的 socket 文件描述符

  • @addr:要通信的地址(对于客户端就是服务器的 IP + 端口)

  • @addrlen:addr 参数地址的大小

返回值:

  • 成功:0

  • 失败:-1,并设置 errno


3. 地址结构 struct sockaddr_in

复制代码
struct sockaddr_in {
    sa_family_t    sin_family;   // 地址族:AF_INET
    in_port_t      sin_port;     // 端口号(网络字节序)
    struct in_addr sin_addr;     // IP 地址(网络字节序)
};

/* Internet address. */
struct in_addr {
    uint32_t       s_addr;       // IP 地址(网络字节序)
};

十二、字节序(Byte Order)

大端 vs 小端

  • 大端(Big Endian):高字节存放在低地址

  • 小端(Little Endian):低字节存放在低地址(x86 架构)

  • 网络字节序 :采用大端字节序

字节序转换函数:

端口号转换:htons()
复制代码
uint16_t htons(uint16_t hostshort);

功能: 将主机字节序的端口号转换为网络字节序

IP 地址转换:inet_addr()
复制代码
in_addr_t inet_addr(const char *cp);

功能: 将点分十进制字符串形式的 IP 地址(如 "192.168.1.1")转换为网络字节序的 IP 地址

IP 地址反向转换:inet_ntoa()
复制代码
char *inet_ntoa(struct in_addr in);

功能: 将网络字节序的 IP 地址转换为点分十进制字符串形式的 IP 地址


十三、IP 地址详解

IP 地址的本质:

  • 在 IPv4 网络中,IP 地址是 32 位二进制数

  • 采用大端字节序(网络字节序)

点分十进制表示法:

  • 将 32 位二进制码划分为 4 个字节

  • 每个字节转换为十进制数

  • 字节之间用 . 分隔

  • 例如:192.168.31.243

    • 二进制:11000000.10101000.00011111.11110011

IP 地址分类(IPv4):

  • A 类

  • B 类

  • C 类

  • D 类

  • E 类

子网掩码(Subnet Mask):

  • 作用

    • 用来区分网络号

    • 也可以获取主机号


十四、端口号(Port)

端口号的作用:

  • 用来标识一个网络进程

  • 在网络中找到主机后(IP),通过端口号找到主机中的进程

端口号的组成:

  • 16 位数据(2 字节)

  • 范围:0 ~ 65535

端口号分类:

  • 1 ~ 1023:知名端口号(Well-known Ports)

    • 80:HTTP

    • 23:Telnet

    • 21:FTP

  • 1024 ~ 50000:系统注册端口(Registered Ports)

  • ≥ 50000:动态端口(Dynamic/Private Ports)

网络进程唯一标识:

  • IP 地址 + 端口号 = 网络中进程的唯一地址

十五、服务器端其他函数

4. bind() 函数(服务器)

复制代码
#include <sys/types.h>
#include <sys/socket.h>

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

功能: 给 socket 绑定一个名字(地址信息)

参数:

  • @sockfd:要绑定的 socket 文件描述符

  • @addr:绑定服务器自己的地址信息

  • @addrlen:服务器地址的大小

返回值:

  • 成功:0

  • 失败:-1,并设置 errno


5. listen() 函数(服务器)

复制代码
#include <sys/types.h>
#include <sys/socket.h>

int listen(int sockfd, int backlog);

功能: 将 socket 设置为监听套接字(被动套接字)

参数:

  • @sockfd:要设置的 socket

  • @backlog:表示等待队列的大小

返回值:

  • 成功:0

  • 失败:-1,并设置 errno


6. accept() 函数(服务器)

复制代码
#include <sys/types.h>
#include <sys/socket.h>

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

功能:

  • 从 listen 的等待队列中提取第一个连接请求

  • 完成连接

  • 同时创建一个新的 socket(表示已经连接好的 socket)

  • 最终返回这个已连接好的 socket 的文件描述符

参数:

  • @sockfd:监听套接字的文件描述符

  • @addr:用来保存对端(客户端)的地址信息

  • @addrlen值结果参数

    • 必须初始化:sizeof(客户端地址类型)

返回值:

  • 成功:返回已连接好的 socket 的文件描述符

  • 失败:返回 -1,并设置 errno

注意:

代码:


十六、客户端与服务器的 Socket 数量

  • 客户端 :只有 1 个 socket

  • 服务器 :有 2 个 socket

    • a. 监听 socket(用于接受连接)

    • b. 通信 socket(用于数据收发)


十七、练习

练习 1:客户端输入信息,服务器打印

客户端(client):
复制代码
1. socket()
2. connect()
3. while(1) {
       fgets()  // 从键盘获取输入
       write(fd) // 发送给服务器
       read(fd)  // 接收服务器响应
   }
4. close()
服务器端(server):
复制代码
1. socket()
2. bind()
3. listen()
4. connfd = accept()
5. 通信:
   while(1) {
       read(connfd, buf, ...)  // 读取客户端数据
       printf("%s", buf)       // 打印输出
       
       // 可选:回显修改后的数据
       char sbuf[1024];
       sprintf(sbuf, "server+%s\n", buf);
       write(connfd, sbuf);
   }
6. close()


练习 2:点对点聊天(多进程版本)

客户端(client):
复制代码
1. socket()
2. connect()
3. fork()
   if (pid > 0) {
       // 父进程:发送消息
   } else if (pid == 0) {
       // 子进程:接收服务器消息
   }
4. close()
服务器端(server):
复制代码
1. socket()
2. bind()
3. listen()
4. connfd = accept()
5. fork()
   if (pid > 0) {
       // 父进程:发送消息
   } else if (pid == 0) {
       // 子进程:接收客户端消息
   }
6. close()

多任务分工:

  • 任务1:专门负责发送消息

  • 任务2:专门负责接收消息

练习 3:点对点聊天(多线程版本)


十八、总结

关键函数列表:

函数 用途 使用方
socket() 创建套接字 双方
bind() 绑定地址 服务器
listen() 监听连接 服务器
accept() 接受连接 服务器
connect() 发起连接 客户端
read()/write() 数据收发 双方
close() 关闭连接 双方
相关推荐
程序猿乐锅1 小时前
从 dsh 源码看「一切皆插件」与 Spring IoC
java·网络·数据库·人工智能·后端·spring
艾醒(AiXing-w)1 小时前
LangChain 1.0 入门(五):提示词工程、partial变量、ChatPromptTemplate、Hub模板库
服务器·网络·langchain
wno7041 小时前
nginx反向代理设置ssl,支持https
运维·服务器·网络
FreeTinker2 小时前
联想个人云存储:大厂入局NAS赛道,体验如何?
网络
迪康coolmu2 小时前
企业IT运维闭环——工单管理与远程协助一体化实践
java·大数据·运维·网络·数据库·人工智能·安全
polarislove02142 小时前
Windows安全中心网络盘打开文件提示“打开这些文件可能会对你的计算机有害”的解决方法
网络·windows·安全
幻步智能网络云专线2 小时前
幻步智能网络专线的优势
网络·网络协议
东方护航数据恢复(深圳)2 小时前
勒索病毒零赎金解密:加密样本逆向分析与可恢复性评估方法论
大数据·网络·数据库
richard_first2 小时前
第8章 Feed Forward Network(FFN 网络)
网络·人工智能·深度学习·机器学习·transformer