【Linux】网络编程套接字

文章目录

  • 网络编程套接字
    • 1. 认识TCP协议
    • 2. 认识UDP协议
    • 3. 网络字节序
    • 4. socket编程接口
      • 4.1 sockaddr 结构
    • 5. 简单的UDP网络程序
    • 6. 简单的TCP网络程序
      • 6.1 TCP socket的封装
      • 6.2 TCP协议通讯流程

网络编程套接字

1. 认识TCP协议

  • 传输层协议
  • 有连接
  • 可靠传输
  • 面向字节流

2. 认识UDP协议

  • 传输层协议
  • 无连接
  • 不可靠传输
  • 面向数据包

3. 网络字节序

不管这台主机是大端还是小端,就需要先将数据转换为大端字节序

b31xq9ivwl-1691562183517.png

h表示本地,n 表示 network, l表示32为长整数, s表示16位短整数

4. socket编程接口

// 创建 socket 文件描述符 (TCP/UDP, 客户端 + 服务器)
int socket(int domain, int type, int protocol);
// 绑定端口号 (TCP/UDP, 服务器) 
int bind(int socket, const struct sockaddr *address,socklen_t address_len);
// 开始监听socket (TCP, 服务器)
int listen(int socket, int backlog);
// 接收请求 (TCP, 服务器)
int accept(int socket, struct sockaddr* address,socklen_t* address_len);
// 建立连接 (TCP, 客户端)
int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

4.1 sockaddr 结构

d86s9hjfue-1691562350564.png

5. 简单的UDP网络程序

void InitServer()
{_sock = socket(AF_INET, SOCK_DGRAM, 0);if (_sock < 0){std::cout << "create sock error" << strerror(errno) << std::endl;exit(SOCKET_ERR);}std::cout << "create sock success" << std::endl;struct sockaddr_in local;sizeof (local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(_port);local.sin_addr.s_addr = inet_addr(_ip.c_str());if (bind(_sock, (struct sockaddr*)&local, sizeof(local)) < 0){std::cout << "bind socket error" << strerror(errno) << std::endl;exit(BIND_ERR);}std::cout << "bind socket success" << std::endl;
}

下面是实现的简单群聊服务器:

  1. 环形队列

    #pragma once#include <iostream>
    #include <vector>
    #include <pthread.h>
    #include <semaphore.h>static const int N = 50;template <class T>
    class RingQueue
    {
    private:void P(sem_t &s){sem_wait(&s);}void V(sem_t &s){sem_post(&s);}void Lock(pthread_mtuex_t &m){pthread_mutex_lock(&m);}void Unlock(pthread_mutex_t &m){pthread_mutex_unlock(&m);}public:RingQueue(int num = N) : _ring(num), _cap(num){sem_init(&_data_sem, 0, 0);sem_init(&_space_sem, 0, num);_c_step = _p_step = 0;pthread_mutex_init(&_c_mutex, nullptr);pthread_mutex_init(&_p_mutex, nullptr);}void push(const T &in){P(_space_sem);Lock(_p_mutex);_ring[_p_step++] = in;_p_step %= _cap;Unlock(_p_mutex);V(_data_sem);}void pop(T *out){P(_data_sem);Lock(_c_mutex);*out = _ring[_c_step++];_c_step %= _cap;Unlock(_c_mutex);V(_space_sem);}~RingQueue(){sem_destroy(&_data_sem);sem_destroy(&_space_sem);pthread_mutex_destroy(&_p_mutex);pthread_mutex_destroy(&_c_mutex);}private:std::vector<T> _ring;int _cap; // 环形队列的大小sem_t _data_sem; // 消费者关心sem_t _space_sem;  // 生产者关心int _c_step; // 消费位置int _p_step; // 生产位置pthread_mutex_t _c_mutex;pthread_mutex_t _p_mutex;
    };
    
  2. 线程包装

    #pragma once#include <iostream>
    #include <string>
    #include <cstdlib>
    #include <pthread.h>
    #include <functional>class Thread
    {
    public:typedef enum{NEW = 0, RUNNING, EXITED} ThreadStatus;using func_t = std::function<void ()>;public:Thread(int num, func_t func) : _tid(0), _status(NEW), _func(func){char name[128];snprintf(name, sizeof(name), "thread-%d", num);_name = name;   }int status() {return _status;}std::string threadname(){return _name;}pthread_t pthreadid(){if (_status == RUNNING){return _tid;}else{return 0;}}void operator()() // 仿函数{if (_func != nullptr) _func();}static void *runHelper(void *args){Thread* ts = (Thread*)args;(*ts)();return nullptr;}void run(){int n = pthread_create(&_tid, nullptr, runHelper, this);if (n != 0) exit(1);_status = RUNNING;}void join(){int n = pthread_join(_tid, nullptr);if (n != 0){std::cout << "man thread join thread" << _name << " error" << std::endl;return;}_status = EXITED;}~Thread(){}private:pthread_t _tid;std::string _name;func_t _func;ThreadStatus _status;
    };
    
  3. 锁包装

    #pragma once#include <iostream>
    #include <pthread.h>class Mutex // 自己不维护锁,外部传入
    {
    private:pthread_mutex_t *_pmutex;
    public:Mutex(pthread_mutex_t *mutex) : _pmutex(mutex){}void lock(){pthread_mutex_lock(_pmutex);}void unlock(){pthread_mutex_unlock(_pmutex);}~Mutex(){}
    };class lockGuard
    {
    private:Mutex _mutex;
    public:lockGuard(pthread_mutex_t *mutex) : _mutex(mutex){_mutex.lock();}~lockGuard(){_mutex.unlock();}
    };
    
  4. 服务器

    #pragma once#include <iostream>
    #include <cerrno>
    #include <cstring>
    #include <cstdlib>
    #include <functional>
    #include <strings.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <pthread.h>
    #include <unordered_map>
    #include "err.hpp"
    #include "RingQueue.hpp"
    #include "lockGuard.hpp"
    #include "Thread.hpp"const static uint16_t port = 8888;
    using func_t = std::function<std::string(std::string)>;class UdpServer
    {
    private:uint16_t _port;int _sock;std::unordered_map<std::string, struct sockaddr_in> _onlineuser;pthread_mutex_t _lock;RingQueue<std::string> _rq;Thread *c;Thread *p;public:UdpServer(uint16_t port = port) : _port(port){std::cout << "server addr " << _port << std::endl;pthread_mutex_init(&_lock, nullptr);p = new Thread(1, std::bind(&UdpServer::Recv, this));c = new Thread(1, std::bind(&UdpServer::Broadcast, this));}void start(){_sock = socket(AF_INET, SOCK_DGRAM, 0); // 创建套接字if (_sock < 0){std::cout << "create _sock error" << std::endl;exit(SOCKET_ERR);}std::cout << "create _sock success" << std::endl;struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_port = htons(_port);local.sin_family = AF_INET;local.sin_addr.s_addr = INADDR_ANY;if (bind(_sock, (struct sockaddr *)&local, sizeof(local)) < 0) // 绑定套接字{std::cout << "bind socket error" << std::endl;exit(BIND_ERR);}std::cout << "bind socket success" << std::endl;p->run();c->run();}void addUser(const std::string &name, const struct sockaddr_in &peer){lockGuard guard(&_lock);auto it = _onlineuser.find(name);if (it != _onlineuser.end()){return;}_onlineuser.insert(std::pair<const std::string, const struct sockaddr_in>(name, peer));}void Recv(){char buf[2056];while (true){struct sockaddr_in peer;socklen_t len = sizeof(peer);int n = recvfrom(_sock, buf, sizeof(buf) - 1, 0, (struct sockaddr *)&peer, &len);if (n > 0){buf[n] = '\0';}elsecontinue;std::cout << "recv done" << std::endl;std::string clientip = inet_ntoa(peer.sin_addr);uint16_t clientport = ntohs(peer.sin_port);std::cout << clientip << "-" << clientport << "#" << buf << std::endl;std::string name = clientip;name += "-";name += std::to_string(clientport);addUser(name, peer);_rq.push(buf);}}void Broadcast(){while (true){std::string sendstring;_rq.pop(&sendstring);std::vector<struct sockaddr_in> v;{lockGuard guard(&_lock);for (auto user : _onlineuser){v.push_back(user.second);}}for (auto user : v){sendto(_sock, sendstring.c_str(), sendstring.size(), 0, (struct sockaddr*)&(user), sizeof(user));std::cout << "send done" << sendstring << std::endl;}}}~UdpServer(){pthread_mutex_destroy(&_lock);c->join();p->join();delete p, c;}
    };
    

    地址转换函数

    inet_ntoa 是把返回结果放到了静态区,这个时候不需要手动释放。如果多次调用,会覆盖掉上一次的值

    6. 简单的TCP网络程序

    TCP由于是全双工的,所以初始化工作一共有五步

    1. socket
    2. bind
    3. listen
    4. accept
    5. connect

    listen 声明socket fd处于监听状态,并且允许多个客户端来连接等待状态

    accept 三次握手完成后,调用服务器连接,

    id4zt0ehel-1691576412570.png

connect 客户端需要调用这个函数连接服务器

TCP 简单服务器:

#pragma once#include <iostream>
#include <cstdlib>
#include <cstring>
#include <functional>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
#include <pthread.h>
#include "err.hpp"static const int defaultport = 8888;
static const int backlog = 32;
using func_t = std::function<std::string(const std::string &)>;class TcpServer;class ThreadData
{
public:int _sock;std::string _clientip;uint16_t _port;TcpServer *_current;
public: ThreadData(int fd, const std::string &ip, const uint16_t &port, TcpServer *ts): _sock(fd), _clientip(ip), _port(port), _current(ts){}
};class TcpServer
{
public:TcpServer(func_t func, uint16_t port = defaultport) : _port(port), _func(func){}void initServer(){_listensock = socket(AF_INET, SOCK_STREAM, 0);if (_listensock < 0){std::cout << "create socket error" << std::endl;exit(SOCKET_ERR);}struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_addr.s_addr = htonl(INADDR_ANY);local.sin_port = htons(_port);if (bind(_listensock, (struct sockaddr*)&local, sizeof(local)) < 0){std::cout << "create bind error" << std::endl;exit(BIND_ERR);}if (listen(_listensock, backlog) < 0){std::cout << "create listen error" << std::endl;exit(LISTEN_ERR);}}static void* threadRuntinue(void* args){pthread_detach(pthread_self());ThreadData *td = static_cast<ThreadData*>(args);td->_current->server(td->_sock, td->_clientip, td->_port);delete td;return nullptr;}void start(){_quit = false;while (true){struct sockaddr_in client;socklen_t len = sizeof(client);int sock = accept(_listensock, (struct sockaddr*)&client, &len);                                                  if (sock < 0){std::cout << "create accept error" << std::endl;continue;}std::string clientip = inet_ntoa(client.sin_addr);uint16_t clientport = ntohs(client.sin_port);std::cout << "accept success" << clientip << " " << clientport << std::endl;// server(sock, clientip, clientport); 第一个版本// 多线程版本pthread_t tid;ThreadData *threadDate = new ThreadData(sock, clientip, clientport, this);pthread_create(&tid, nullptr, threadRuntinue, threadDate);}}void server(int sock, const std::string ip, uint16_t port){std::string who = ip + "-" + std::to_string(port);char buffer[1024];while (true){size_t s = read(sock, buffer, sizeof(buffer) - 1);if (s > 0){buffer[s] = 0;std::string res = _func(buffer); // 回调传进来的函数std::cout << who << ">>>" << res << std::endl;write(sock, res.c_str(), res.size());}else if (s == 0){close(sock);std::cout << who << "quit, me too" << std::endl;break;}else{close(sock);std::cout << "read error" << std::endl;break;}}}~TcpServer() {}
private:uint16_t _port;int _listensock;bool _quit;func_t _func;
};

TCP 简单的客户端

#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "err.hpp"int main(int argc, char *argv[])
{std::string serverip = argv[1];uint16_t serverport = atoi(argv[2]);int sock = socket(AF_INET, SOCK_STREAM, 0);// 要不要bind? 要// 要不要自己bind? 不要,因为client要让OS自动给用户进行bind// 要不要listen?不要 要不要accept?不需要struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(serverport);inet_aton(serverip.c_str(), &(server.sin_addr));int cnt = 5;while (connect(sock, (struct sockaddr*)&server, sizeof(server)) != 0){sleep(1);std::cout << "正在尝试重新连接" << std::endl;cnt--;if (cnt < 0) break;}if (cnt <= 0) {std::cout << "连接失败" << std::endl;exit(CONNECT_ERR);}char buffer[1024];while (true){std::string line;std::getline(std::cin, line);write(sock, line.c_str(), line.size());size_t s = read(sock, buffer, sizeof(buffer) - 1);if (s > 0){buffer[s] = 0;std::cout << "server echo" << std::endl;}else if (s == 0){std::cout << "server quit" << std::endl;break;} else {std::cout << "read error" << std::endl;break;}}close(sock);return 0;
}

6.1 TCP socket的封装

#pragma once
#include <iostream>
#include <string>
#include <cstdlib>
#include <cstring>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <unistd.h>
#include "Log.hpp"
#include "Err.hpp"static const int gbacklog = 32;
static const int defaultfd = -1;class Sock
{
public:Sock() : _sock(defaultfd) {}void Socket(){_sock = socket(AF_INET, SOCK_STREAM, 0);if (_sock < 0){logMessage(Fatal, "socket error, code : %d", errno);exit(SOCKET_ERR);}}void Bind(const uint16_t &port){struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(port);local.sin_addr.s_addr = INADDR_ANY;if (bind(_sock, (struct sockaddr *)&local, sizeof(local)) < 0){logMessage(Fatal, "bind error, code: %d, errstring: %s", errno, strerror(errno));exit(BIND_ERR);}}void Listen(){if (listen(_sock, gbacklog) < 0){logMessage(Fatal, "listen error, code: %d, errstring: %s", errno, strerror(errno));exit(LISTEN_ERR);}}int Accept(std::string *clientip, uint16_t *clientport){struct sockaddr_in temp;socklen_t len = sizeof(temp);int sock = accept(_sock, (struct sockaddr *)&temp, &len);if (sock < 0){logMessage(Warning, "accept error, code: %d, errstring: %s", errno, strerror(errno));}else{*clientip = inet_ntoa(temp.sin_addr);*clientport = ntohs(temp.sin_port);}return sock;}int Connect(const std::string &serverip, const uint16_t &serverport){struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(serverport);server.sin_addr.s_addr = inet_addr(serverip.c_str());return connect(_sock, (struct sockaddr *)&server, sizeof(server));}int Fd(){return _sock;}~Sock(){if (_sock != defaultfd)close(_sock);}private:int _sock;
};

6.2 TCP协议通讯流程

服务器初始化:

  • 调用socket,创建文件描述符
  • 调用bind,将当前的文件描述符和ip / port 绑定在一起,如果这个端口被占用了,就会bind失败
  • 调用listen,声明这个文件描述符是服务器的文件描述符,为后面的accept做好准备
  • 调用accept并阻塞,等待客户端连接

建立连接的过程:

  • 调用socket,创建文件描述符
  • 调用connect,向服务器发起连接请求
  • connect会发出SYN并阻塞等待服务器应答
  • 服务器收到客户端的SYN,会应答一个SYN-ACK,表示同意建立连接
  • 客户端收到SYN-ACK后会从connect()返回,同时应答一个ACK
    这个建立过程称为三次握手

TCPVSUDP

  • 可靠传输 VS 不可靠传输
  • 有连接 VS 无连接
  • 字节流 VS 数据报

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

php通过各种函数判断0和空php实例

php通过各种函数判断0和空php实例 本文给大家介绍php同各种函数判断0和空的方法&#xff0c;在文章给大家补充介绍了php 语法里0不等于null为空的解决办法 补充&#xff1a;下面给大家介绍下php 语法里0不等于null为空的解决办法 今天遇到这样一个问题是这样的: php 语句里,我…

Blender如何给fbx模型添加材质贴图并导出带有材质贴图的模型

推荐&#xff1a;使用 NSDT场景编辑器快速助你搭建可二次编辑的3D应用场景 此教程适合新手用户&#xff0c;专业人士直接可直接绕路。 本教程中介绍了利用Blender建模软件&#xff0c;只需要简单几步就可以为模型添加材质贴&#xff0c;图&#xff0c;并且导出带有材质的模型文…

uniapp 使用canvas画海报(微信小程序)

效果展示&#xff1a; 项目要求&#xff1a;点击分享绘制海报&#xff0c;并实现分享到好友&#xff0c;朋友圈&#xff0c;并保存 先实现绘制海报 <view class"data_item" v-for"(item,index) in dataList" :key"index"click"goDet…

【OpenVINOSharp】 基于C#和OpenVINO2023.0部署Yolov8全系列模型

基于C#和OpenVINO2023.0部署Yolov8全系列模型 1 项目简介1.1 OpenVINOTM 2 OpenVinoSharp2.1 OpenVINOTM 2023.0安装配置2.2 C 动态链接库2.3 C#构建Core推理类2.4 NuGet安装OpenVinoSharp 3 获取和转换Yolov8模型3.1 安装ultralytics3.2 导出yolov8模型3.3 安装OpenVINOTM Pyt…

iOS链式编程风格 -- 富文本字符串

一、概念 链式编程风格是一种将多个函数调用连接起来&#xff0c;形成一条函数调用链的编程风格。这种风格的代码可以通过返回 self 或某个适当的对象来实现。 1.优点 代码简洁、连贯、易于阅读。可以将一个方法的输出直接作为下一个方法的输入&#xff0c;降低中间变量的使…

工厂方法模式(一):C#实现指南

工厂方法模式是一种创建型设计模式&#xff0c;用于处理对象的创建问题。通过使用工厂方法模式&#xff0c;我们可以将对象的创建过程与使用过程分离&#xff0c;从而增加代码的灵活性和可维护性。 工厂方法模式的定义 工厂方法模式定义了一个创建对象的接口&#xff0c;但由子…

ssm学院党员管理系统源码和论文PPT

ssm学院党员管理系统源码和论文PPT002 开发工具&#xff1a;idea 数据库mysql5.7(mysql5.7最佳) 数据库链接工具&#xff1a;navcat,小海豚等 开发技术&#xff1a;java ssm tomcat8.5 选题意义、价值和目标&#xff1a; 随着鄂尔多斯应用技术学院招生规模的不断扩大&…

qt lamda表达式及捕获变量列表符号说明及示例

问题描述: 最近发现很多人都喜欢用Lamda表达式了,至于他们到底知不知道自己用的是什么意思,那就另说了。 虽然我个人并不太喜欢,因为很多地方没法像以前信号和槽那样清晰了,而且很多生成UML的软件估计也不支持解析转成对应的序列图啥的。 但是这个lamda写法确实挺方便的…

LeetCode209. 长度最小的子数组

题目&#xff1a;LeetCode209. 长度最小的子数组 描述&#xff1a; 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl1, …, numsr-1, numsr] &#xff0c;并返回其长度。如果不存在符合条件的子…

改进DevSecOps框架的 5 大关键技术

Markets and Markets的一项研究显示&#xff0c;全球DevOps的市场规模从2017年的29亿美元增加到2023年的103.1亿美元&#xff0c;预测期的年复合增长率(CAGR)为24.7%。人们对DevOps越来越感兴趣&#xff0c;因为DevOps不仅能够压缩软件的交付周期&#xff0c;还能提高交付的速度…

geeemap学习总结(1)——Anaconda-VSCode-geemap环境安装与配置

配置conda geemap 环境 通过Anaconda配置geemap环境较为方便&#xff0c;首先需在系统中完成 Anaconda安装。创建名为geemap的环境conda create -n geemap切换到新建的环境conda activate geemap安装geemap依赖包conda install -c conda-forge geemap 安装mambaconda install …

【leetcode】349. 两个数组的交集(easy)

给定两个数组 nums1 和 nums2 &#xff0c;返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。 思路&#xff1a; 先遍历nums1将其元素不重复地添加到哈希表a中&#xff1b;建立哈希表dup用于存储b和a重复的元素&#xff1b;遍历nums2…

chrome插件开发实例08- 使用Vue.js开发chrome插件

目录 背景 演示 功能介绍 插件下载 注意写法: 背景 将 下面的两个插件 改写成vue.js , elementui 实现chrome插件开发实例0

Oracle笔记--dblink

概述 1、database link是定义一个数据库到另一个数据库的路径的对象&#xff0c;database link允许你查询远程表及执行远程程序。在任何分布式环境里&#xff0c;database都是必要的。另外要注意的是database link是单向的连接。 2、在创建database link的时候&#xff0c;Ora…

ACM算法竞赛中在编辑器中使用输入输出样例-CPH

通用方法 我们可以在编辑器中创建三个文件&#xff0c;一个是main.cpp,一个是test.in,一个是test.out分别用来写代码&#xff0c;输入输入数据&#xff0c;显示输出数据 这种方法的好处是不需要插件&#xff0c;在任何编辑器中都可以实现&#xff0c;例如Devc,sublime,vscode…

C++单例模式

文章目录 1、什么是单例2、一个好的单例应该具备的条件3、懒汉模式与饿汉模式4、单例实现&#xff1a;线程安全、内存安全的懒汉式单例&#xff08;基于C11的智能指针和互斥锁&#xff09; 1、什么是单例 单例 Singleton 是设计模式的一种&#xff0c;其特点是只提供唯一一个类…

重磅!TikTok将于8月底关闭半闭环

站斧浏览器获悉&#xff1a; 8月4日消息&#xff0c;TikTok或将于8月底正式关闭半闭环&#xff0c;届时卖家将无法在TikTok上放置外链或引导至独立站成交&#xff0c;该消息已经得到TikTok官方人员的确认。 多位业内人士表示&#xff0c;TikTok关闭半闭环一方面是因为其电商业…

【Pyhthon实战】Python对全校电费查询采集并可视化分析

前言 今天,我来说说怎么抓取宿舍电费的过程。我们学校是在完美校园交电费的,我们可以不用取抓包完美校园的数据接口,我们可以直接登录学校的一卡通网站,每个学校都有,大家可以自己找找,这里我为什么要抓包呢,因为学校提供的网站已经打不开了,这里就不介绍怎么抓包了。 …

直播电商赋能跨境业务,Live Market创造全民参与的生态圈

全球疫情的影响让跨境电商业务受到了巨大的冲击&#xff0c;但同时也为跨境电商业务带来了新的机遇和挑战。直播电商作为电子商务行业的新兴业务版块&#xff0c;成为了跨境电商业务的一个重要推动力量。在这个背景下&#xff0c;直播电商成为了跨境电商业务的一个主流业务版块…

php-cgi.exe - FastCGI 进程超过了配置的请求超时时限

解决方案一&#xff1a; 处理(php-cgi.exe - FastCGI 进程超过了配置的请求超时时限)的问题 内容转载&#xff1a; 处理(php-cgi.exe - FastCGI 进程超过了配置的请求超时时限)的问题_php技巧_脚本之家 【详细错误】&#xff1a; HTTP 错误 500.0 - Internal Server Error C:…