【Linux探索学习】第三十一弹——线程互斥与同步(下):深入理解确保线程安全的机制

线程互斥与同步(上):【Linux探索学习】第三十弹——线程互斥与同步(上):深入理解线程保证安全的机制-CSDN博客

Linux探索学习:

https://blog.csdn.net/2301_80220607/category_12805278.html?spm=1001.2014.3001.5482

前言:

在上一篇中我们已经讲解了线程互斥与同步中关于互斥的相关知识点,今天我们来学习一下Linux中线程同步的相关知识点。其实要想做到线程同步,首先就要确保线程互斥部分是没问题的,也就是说做好线程互斥也是确保线程同步与线程安全的重要一步

在多线程编程中,线程同步是一个至关重要的问题。当多个线程共享同一资源时,如果没有合适的同步机制,可能会导致数据竞争、死锁等问题。Linux提供了多种线程同步机制,如互斥锁(前面讲过)、条件变量、读写锁、信号量等。本文将详细讲解Linux中的线程同步问题,并重点介绍POSIX信号量的使用。

目录

1. 线程同步的基本概念

1.1 什么是线程同步?

1.2 为什么需要线程同步?

2. 条件变量

2.1 条件变量的基本概念

2.2 条件变量的相关函数详解

2.2.1 pthread_cond_init

2.2.2 pthread_cond_destroy

2.2.3 pthread_cond_wait

2.2.4 pthread_cond_signal

2.2.5 pthread_cond_broadcast

2.3 条件变量的使用示例

2.4 运行结果

​编辑

2.5 代码解析

3. 读写锁

3.1 读写锁的基本概念

3.2 读写锁的相关函数详解

3.2.1 pthread_rwlock_init

3.2.2 pthread_rwlock_destroy

3.2.3 pthread_rwlock_rdlock

3.2.4 pthread_rwlock_wrlock

3.2.5 pthread_rwlock_unlock

3.3 读写锁的使用示例

3.4 运行结果

​编辑

3.5 代码解析

4. POSIX信号量

4.1 POSIX信号量的基本概念

4.2 POSIX信号量的相关函数详解

4.2.1 sem_init

4.2.2 sem_destroy

4.2.3 sem_wait

4.2.4 sem_post

4.2.5 sem_trywait

4.2.6 sem_getvalue

4.3 POSIX信号量的使用示例

4.4 运行结果

​编辑

4.5 代码解析

5. 总结

5.1 条件变量的适用场景

5.2 读写锁的适用场景

5.3 POSIX信号量的适用场景

5.4 选择同步机制的考虑因素


1. 线程同步的基本概念

1.1 什么是线程同步?

线程同步是指多个线程在访问共享资源时,通过某种机制来协调它们的执行顺序,以避免数据竞争和不一致性问题。常见的线程同步机制包括互斥锁、条件变量、读写锁、信号量等。

1.2 为什么需要线程同步?

在多线程环境中,多个线程可能会同时访问共享资源。如果没有适当的同步机制,可能会导致以下问题:

  • 数据竞争:多个线程同时修改同一数据,导致数据不一致。

  • 死锁:多个线程相互等待对方释放资源,导致程序无法继续执行。

  • 活锁:线程不断尝试获取资源但总是失败,导致程序无法继续执行。(活锁的具体讲解想了解的可以再搜一下)

2. 条件变量

2.1 条件变量的基本概念

条件变量是一种线程同步机制,用于在多个线程之间传递信号。条件变量通常与互斥锁一起使用,用于在某个条件成立时唤醒等待的线程。

2.2 条件变量的相关函数详解

2.2.1 pthread_cond_init

函数原型:

int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);

功能:
初始化条件变量。

参数:

  • cond:指向条件变量的指针。

  • attr:条件变量的属性,通常设置为NULL,表示使用默认属性。

返回值:

  • 成功返回0,失败返回错误码。

示例:

pthread_cond_t cond;
pthread_cond_init(&cond, NULL);
2.2.2 pthread_cond_destroy

函数原型:

int pthread_cond_destroy(pthread_cond_t *cond);

功能:
销毁条件变量。

参数:

  • cond:指向条件变量的指针。

返回值:

  • 成功返回0,失败返回错误码。

示例:

pthread_cond_destroy(&cond);
2.2.3 pthread_cond_wait

函数原型:

int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);

功能:
等待条件变量,并释放互斥锁。具体点来说就是需要等待满足条件才能执行下一步,但是在这一步之前我们可能已经获取了锁,为了避免死锁问题,就需要在等待条件变量时把互斥锁释放掉

参数:

  • cond:指向条件变量的指针。

  • mutex:指向互斥锁的指针。

返回值:

  • 成功返回0,失败返回错误码。

示例:

pthread_mutex_lock(&mutex);
while (!ready) {
    pthread_cond_wait(&cond, &mutex);
}
pthread_mutex_unlock(&mutex);
2.2.4 pthread_cond_signal

函数原型:

int pthread_cond_signal(pthread_cond_t *cond);

功能:
唤醒一个等待条件变量的线程。

参数:

  • cond:指向条件变量的指针。

返回值:

  • 成功返回0,失败返回错误码。

示例:

pthread_cond_signal(&cond);
2.2.5 pthread_cond_broadcast

函数原型:

int pthread_cond_broadcast(pthread_cond_t *cond);

功能:
唤醒所有等待条件变量的线程。在使用这种方式时需要注意,因为有些时候当出现多进程的线程时,这种方法可能会导致线程误唤醒,在我们后面讲生产消费模型的时候会讲到这一点

参数:

  • cond:指向条件变量的指针。

返回值:

  • 成功返回0,失败返回错误码。

示例:

pthread_cond_broadcast(&cond);

2.3 条件变量的使用示例

下面是一个简单的示例,演示了如何使用条件变量来同步两个线程的执行。

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

pthread_mutex_t mutex;
pthread_cond_t cond;
int ready = 0;

void* thread_func(void* arg) {
    pthread_mutex_lock(&mutex);
    while (!ready) {
        printf("Thread waiting...\n");
        pthread_cond_wait(&cond, &mutex);
    }
    printf("Thread awakened!\n");
    pthread_mutex_unlock(&mutex);
    return NULL;
}

int main() {
    pthread_t tid;
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);

    pthread_create(&tid, NULL, thread_func, NULL);

    sleep(1);  // 模拟一些工作
    pthread_mutex_lock(&mutex);
    ready = 1;
    pthread_cond_signal(&cond);
    pthread_mutex_unlock(&mutex);

    pthread_join(tid, NULL);

    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);

    return 0;
}

2.4 运行结果

Thread waiting...
Thread awakened!

2.5 代码解析

  • pthread_cond_wait(&cond, &mutex):线程在等待条件变量时,会释放互斥锁,并在被唤醒后重新获取互斥锁。

  • pthread_cond_signal(&cond):唤醒一个等待条件变量的线程。

  • pthread_cond_broadcast(&cond):唤醒所有等待条件变量的线程。

这里的这个代码比较简单,也没有什么现实意义,重要的是先要理解一下条件变量的出现场景和使用,条件变量一般是和互斥锁同时出现的,能够确保在满足条件的情况下才能执行临界区的语句

3. 读写锁

3.1 读写锁的基本概念

读写锁是一种特殊的锁,允许多个线程同时读取共享资源,但在写操作时需要独占访问。读写锁适用于读多写少的场景,可以提高并发性能。

3.2 读写锁的相关函数详解

3.2.1 pthread_rwlock_init

函数原型:

int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);

功能:
初始化读写锁。

参数:

  • rwlock:指向读写锁的指针。

  • attr:读写锁的属性,通常设置为NULL,表示使用默认属性。

返回值:

  • 成功返回0,失败返回错误码。

示例:

pthread_rwlock_t rwlock;
pthread_rwlock_init(&rwlock, NULL);
3.2.2 pthread_rwlock_destroy

函数原型:

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);

功能:
销毁读写锁。

参数:

  • rwlock:指向读写锁的指针。

返回值:

  • 成功返回0,失败返回错误码。

示例:

pthread_rwlock_destroy(&rwlock);
3.2.3 pthread_rwlock_rdlock

函数原型:

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);

功能:
获取读锁。

参数:

  • rwlock:指向读写锁的指针。

返回值:

  • 成功返回0,失败返回错误码。

示例:

pthread_rwlock_rdlock(&rwlock);
3.2.4 pthread_rwlock_wrlock

函数原型:

int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);

功能:
获取写锁。

参数:

  • rwlock:指向读写锁的指针。

返回值:

  • 成功返回0,失败返回错误码。

示例:

pthread_rwlock_wrlock(&rwlock);
3.2.5 pthread_rwlock_unlock

函数原型:

int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

功能:
释放读写锁。

参数:

  • rwlock:指向读写锁的指针。

返回值:

  • 成功返回0,失败返回错误码。

示例:

pthread_rwlock_unlock(&rwlock);

3.3 读写锁的使用示例

下面是一个简单的示例,演示了如何使用读写锁来保护共享资源。

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

pthread_rwlock_t rwlock;
int shared_data = 0;

void* reader_func(void* arg) {
    pthread_rwlock_rdlock(&rwlock);
    printf("Reader: shared_data = %d\n", shared_data);
    pthread_rwlock_unlock(&rwlock);
    return NULL;
}

void* writer_func(void* arg) {
    pthread_rwlock_wrlock(&rwlock);
    shared_data++;
    printf("Writer: shared_data = %d\n", shared_data);
    pthread_rwlock_unlock(&rwlock);
    return NULL;
}

int main() {
    pthread_t readers[5], writers[2];
    pthread_rwlock_init(&rwlock, NULL);

    for (int i = 0; i < 5; i++) {
        pthread_create(&readers[i], NULL, reader_func, NULL);
    }

    for (int i = 0; i < 2; i++) {
        pthread_create(&writers[i], NULL, writer_func, NULL);
    }

    for (int i = 0; i < 5; i++) {
        pthread_join(readers[i], NULL);
    }

    for (int i = 0; i < 2; i++) {
        pthread_join(writers[i], NULL);
    }

    pthread_rwlock_destroy(&rwlock);

    return 0;
}

3.4 运行结果

Reader: shared_data = 0
Reader: shared_data = 0
Reader: shared_data = 0
Reader: shared_data = 0
Reader: shared_data = 0
Writer: shared_data = 1
Writer: shared_data = 2

3.5 代码解析

  • pthread_rwlock_rdlock(&rwlock):获取读锁,允许多个线程同时读取共享资源。

  • pthread_rwlock_wrlock(&rwlock):获取写锁,独占访问共享资源。

  • pthread_rwlock_unlock(&rwlock):释放读写锁。

读写锁出现的场景还是比较多的,尤其是当写少读多的时候,读写锁可以帮助我们提高并发效率

4. POSIX信号量

4.1 POSIX信号量的基本概念

POSIX信号量是一种用于线程同步的机制,可以用来控制对共享资源的访问。信号量有一个整数值,表示可用资源的数量。线程可以通过sem_waitsem_post来分别减少和增加信号量的值。

4.2 POSIX信号量的相关函数详解

4.2.1 sem_init

函数原型:

int sem_init(sem_t *sem, int pshared, unsigned int value);

功能:
初始化信号量。

参数:

  • sem:指向信号量的指针。

  • pshared:指定信号量的类型,0表示线程间共享,非0表示进程间共享。

  • value:信号量的初始值。

返回值:

  • 成功返回0,失败返回-1。

示例:

sem_t sem;
sem_init(&sem, 0, 0);
4.2.2 sem_destroy

函数原型:

int sem_destroy(sem_t *sem);

功能:
销毁信号量。

参数:

  • sem:指向信号量的指针。

返回值:

  • 成功返回0,失败返回-1。

示例:

sem_destroy(&sem);
4.2.3 sem_wait

函数原型:

int sem_wait(sem_t *sem);

功能:
减少信号量的值,如果信号量的值为0,则阻塞。

参数:

  • sem:指向信号量的指针。

返回值:

  • 成功返回0,失败返回-1。

示例:

sem_wait(&sem);
4.2.4 sem_post

函数原型:

int sem_post(sem_t *sem);

功能:
增加信号量的值,并唤醒等待的线程。

参数:

  • sem:指向信号量的指针。

返回值:

  • 成功返回0,失败返回-1。

示例:

sem_post(&sem);
4.2.5 sem_trywait

函数原型:

int sem_trywait(sem_t *sem);

功能:
尝试减少信号量的值,如果信号量的值为0,则立即返回错误。

参数:

  • sem:指向信号量的指针。

返回值:

  • 成功返回0,失败返回-1。

示例:

sem_trywait(&sem);
4.2.6 sem_getvalue

函数原型:

int sem_getvalue(sem_t *sem, int *sval);

功能:
获取信号量的当前值。

参数:

  • sem:指向信号量的指针。

  • sval:指向存储信号量值的整数的指针。

返回值:

  • 成功返回0,失败返回-1。

示例:

int value;
sem_getvalue(&sem, &value);

4.3 POSIX信号量的使用示例

下面是一个简单的示例,演示了如何使用POSIX信号量来同步两个线程的执行。

#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>

sem_t sem;

void* thread_func(void* arg) {
    printf("Thread waiting...\n");
    sem_wait(&sem);
    printf("Thread awakened!\n");
    return NULL;
}

int main() {
    pthread_t tid;
    sem_init(&sem, 0, 0);

    pthread_create(&tid, NULL, thread_func, NULL);

    sleep(1);  // 模拟一些工作
    printf("Main thread signaling...\n");
    sem_post(&sem);

    pthread_join(tid, NULL);

    sem_destroy(&sem);

    return 0;
}

4.4 运行结果

Thread waiting...
Main thread signaling...
Thread awakened!

4.5 代码解析

  • sem_wait(&sem):减少信号量的值,如果信号量的值为0,则阻塞。

  • sem_post(&sem):增加信号量的值,并唤醒等待的线程。

我们在前面讲进程的时候就讲过信号量的知识了,这里基本没啥大的差别,关于POSIX信号量的使用,我们在下一章讲生产消费模型的时候也会再详细讲解的,这里大家先理解一下基本用法即可

5. 总结

本文详细讨论了Linux中线程同步的几种机制,包括条件变量、读写锁和POSIX信号量。通过简单的代码示例,我们演示了如何使用这些机制来同步多个线程的执行。在实际的多线程编程中,选择合适的同步机制非常重要,可以有效避免数据竞争、死锁等问题。

5.1 条件变量的适用场景

条件变量适用于需要等待某个条件成立的场景,通常与互斥锁一起使用。例如,生产者-消费者模型中的缓冲区满或空的情况。

5.2 读写锁的适用场景

读写锁适用于读多写少的场景,可以提高并发性能。例如,数据库系统中的读操作远多于写操作。

5.3 POSIX信号量的适用场景

POSIX信号量适用于需要控制对共享资源访问的场景。例如,限制同时访问某个资源的线程数量。

5.4 选择同步机制的考虑因素

在选择线程同步机制时,需要考虑以下因素:

  • 性能:不同的同步机制对性能的影响不同,需要根据具体场景选择最合适的机制。

  • 复杂性:某些同步机制的使用较为复杂,需要仔细设计和测试。

  • 可维护性:选择易于理解和维护的同步机制,可以减少代码的复杂性和出错的可能性。

总之,线程的同步是确保线程安全很重要的一步,今天我们讲解了确保线程同步机制的重要的几个方面,关于这几个方面的知识点的应用在下一篇生成消费模型还会仔细讲解一遍

本篇笔记:

感谢各位大佬观看,创作不易,还请各位大佬点赞支持一下!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/977633.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

UVM_CALLBACK 应用举例

UVM_CALLBACK是一种基于回调函数的设计模式&#xff0c;允许用户在特定事件发生时插入自定义的行为。UVM提供了uvm_callback类作为基类&#xff0c;用户可以通过继承该类来定义自己的回调行为。采用uvm_callback基类&#xff0c;用户可以在不更改原始代码的情况下轻松插入调试代…

优选算法大集合(待更新)

1.双指针 1.1.移动零 leetcode链接&#xff1a;283. 移动零 - 力扣&#xff08;LeetCode&#xff09;​​​​​​ 移动零的问题我们可以将它归类为数组划分的问题&#xff0c;我们将数组划分为非零部分和零部分。我们会使用到双指针的算法&#xff0c;在这里&#xff0c;我…

本地大模型编程实战(22)用langchain实现基于SQL数据构建问答系统(1)

使 LLM(大语言模型) 系统能够查询结构化数据与非结构化文本数据在性质上可能不同。后者通常生成可在向量数据库中搜索的文本&#xff0c;而结构化数据的方法通常是让 LLM 编写和执行 DSL&#xff08;例如 SQL&#xff09;中的查询。 我们将演练在使用基于 langchain 链 &#x…

在 Mac mini M2 上 MaxKb配置ollama,API域名无效的解决方案

环境说明 docker方案安装与使用的maxkb 本地ollama安装deekseek r1 解决方案 参考https://bbs.fit2cloud.com/t/topic/4165 mac m1用户&#xff0c;根据github的以下回复&#xff0c;成功绑定域名api 如果你想调用本地的ollama 中的大模型&#xff0c;域名试试&#xff1a;…

【STL专题】优先级队列priority_queue的使用和模拟实现,巧妙利用仿函数解决优先级

欢迎来到 CILMY23的博客 &#x1f3c6;本篇主题为&#xff1a;优先级队列priority_queue的使用和模拟实现&#xff0c;巧妙利用仿函数解决优先级 &#x1f3c6;个人主页&#xff1a;CILMY23-CSDN博客 &#x1f3c6;系列专栏&#xff1a; C | C语言 | 数据结构与算法 | Linux…

【NLP 23、预训练语言模型】

人类发明后悔&#xff0c;来证明拥有的珍贵 —— 25.1.15 Bert的优势&#xff1a;① 预训练思想 ② Transformer模型结构 一、传统方法 VS 预训练方式 Pre-train&#xff1a; ① 收集海量无标注文本数据 ② 进行模型预训练&#xff0c;并在任务模型中使用 Fine-tune&#xff1a…

阳光高考瑞数6vmp算法还原

URL aHR0cHM6Ly9nYW9rYW8uY2hzaS5jb20uY24v这个站平时没有防护的&#xff0c;只有在平时填报高峰期&#xff0c;才会出来防护&#xff0c;也是为了防护自动脚本吧瑞数就是典型的cookie反爬 O开头cookie 6开头6代vmp&#xff0c;P值是加密的cookie&#xff0c;只有带上0开头的…

危化品经营单位安全管理人员的职责及注意事项

危化品经营单位安全管理人员肩负着保障经营活动安全的重要责任&#xff0c;以下是其主要职责及注意事项&#xff1a; 职责 1. 安全制度建设与执行&#xff1a;负责组织制定本单位安全生产规章制度、操作规程和生产安全事故应急救援预案&#xff0c;确保这些制度符合国家相关法…

微软推出Office免费版,限制诸多,只能编辑不能保存到本地

易采游戏网2月25日独家消息&#xff1a;微软宣布推出一款免费的Office版本&#xff0c;允许用户进行基础文档编辑操作&#xff0c;但限制颇多&#xff0c;其中最引人关注的是用户无法将文件保存到本地。这一举措引发了广泛讨论&#xff0c;业界人士对其背后的商业策略和用户体验…

VMware虚拟机安装win10系统详细图文安装教程(附安装包) 2025最新版详细图文安装教程

文章目录 前言一、软件下载二、安装步骤1.创建新的虚拟机2.安装 Windows 10 系统: 前言 在计算机使用中&#xff0c;有时需借助虚拟机来拓展功能。VMware 虚拟机是强大的工具&#xff0c;能让我们在同一台电脑上运行多个系统。Windows 10 系统功能丰富、应用广泛。本教程将详细…

开源基准测试模拟器:BlueROV2 水下机器人的控制

拜读An Open-Source Benchmark Simulator: Control of a BlueROV2 Underwater Robot 非常感谢Esben Uth的帮助。 本文介绍了在 Simulink™ 中实现的常用且低成本的遥控潜水器 &#xff08;ROV&#xff09; BlueROV2 的仿真模型环境&#xff0c;该环境已针对水下航行器的基准控…

联想 SR590 服务器 530-8i 更换损坏的硬盘

坏了的硬盘会自动亮黄灯。用一个空的新盘来替换&#xff0c;新盘最好不要有东西。但是有东西可能也没啥&#xff0c;因为我看 RAID 控制器里有格式化的选项 1. 从 IPMI 把服务器关机&#xff0c;电源键进入绿色闪烁状态 2. 断电&#xff0c;推开塑料滑块拉出支架&#xff0c;…

【多模态处理篇三】【DeepSeek语音合成:TTS音色克隆技术揭秘】

最近帮某明星工作室做AI语音助手时遇到魔幻需求——要求用5秒的咳嗽声克隆出完整音色!传统TTS系统直接翻车,生成的语音像得了重感冒的电音怪物。直到祭出DeepSeek的TTS音色克隆黑科技,才让AI语音从"机器朗读"进化到"声临其境"。今天我们就来扒开这个声音…

Bybit事件技术分析

事件概述 2025年2月21日UTC时间下午02:16:11&#xff0c;Bybit的以太坊冷钱包&#xff08;0x1db92e2eebc8e0c075a02bea49a2935bcd2dfcf4&#xff09;因恶意合约升级遭到资金盗取。根据Bybit CEO Ben Zhou的声明&#xff0c;攻击者通过钓鱼攻击诱骗冷钱包签名者错误签署恶意交易…

002简单MaterialApp主题和Scaffold脚手架

002最简单的MaterialApp主题和Scaffold脚手架使用导航栏_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1RZ421p7BL?spm_id_from333.788.videopod.episodes&vd_source68aea1c1d33b45ca3285a52d4ef7365f&p1501.MaterialApp纯净的 /*MaterialApp 是主题,自带方向设…

本地部署AI模型 --- DeepSeek(二)---更新中

目录 FAQ 1.Failed to load the model Exit code: 18446744072635812000 FAQ 1.Failed to load the model Exit code: 18446744072635812000 问题描述&#xff1a; &#x1f972; Failed to load the model Error loading model. (Exit code: 18446744072635812000). Unkn…

FastAPI系列:Ubuntu部署FastAPI项目实战

这篇文章提供了在Ubuntu上部署FastAPI应用程序的详细指南。首先&#xff0c;读者将学习如何创建项目目录并设置Python虚拟环境&#xff0c;接着安装FastAPI、Uvicorn和Gunicorn等必要依赖。随后&#xff0c;文章指导用户编写基本的FastAPI应用程序代码&#xff0c;并使用Gunico…

【ECMAScript6】

【ECMAScript6】 01. ES6介绍02. let和const命令03. 模板字符串04. 函数之默认值、剩余参数05. 函数之扩展运算符、箭头函数06. 箭头函数this指向和注意事项07. 解构赋值08. 扩展的对象的功能&#xff08;简写&#xff09;09. Symbol类型10. Set集合数据类型11. Map数据类型12.…

基于 sklearn 的均值偏移聚类算法的应用

基于 sklearn 的均值偏移聚类算法的应用 在机器学习和数据挖掘中&#xff0c;聚类算法是一类非常重要的无监督学习方法。它的目的是将数据集中的数据点划分为若干个类&#xff0c;使得同一类的样本点彼此相似&#xff0c;而不同类的样本点相互之间差异较大。均值偏移聚类&…

浅谈HTTP及HTTPS协议

1.什么是HTTP&#xff1f; HTTP全称是超文本传输协议&#xff0c;是一种基于TCP协议的应用非常广泛的应用层协议。 1.1常见应用场景 一.浏览器与服务器之间的交互。 二.手机和服务器之间通信。 三。多个服务器之间的通信。 2.HTTP请求详解 2.1请求报文格式 我们首先看一下…