Linux网络编程-----协议

1.协议

通信双方约定的一套标准

2.国际网络通信协议标准:

1.OSI协议:(过于冗余)

应用层 发送的数据内容

表示层 数据是否加密

会话层 是否建立会话连接

传输层 数据传输的方式

网络层 数据的路由

数据链路层 局域网内部通信

物理层 物理介质的连接

2.TCP/IP协议模型:

应用层 发送的数据内容

传输层 数据传输的方式

网络层 数据由一台主机到达另一台主机

网络接口层 物理介质连接

1.应用层:

FTP 文件传输协议 (基于TCP )

TFTP 简单文件传输协议 (基于UDP)

HTTP 超文本传输协议

HTTPS 安全超文本传输协议

SMTP 简单邮件传输协议

TELNET 网络终端登录协议 (远程登录一台电脑)

DNS 域名系统

..

2.传输层:

TCP 传输控制协议 (可以严格控制,建立好链接才发送)

UDP 用户数据报协议 (目的地存在就发过去,不存在就丢失掉了)

UDP:不安全、不可靠的传输方式

UDP机制简单

UDP占用的资源开销比较小

TCP:安全、可靠的传输方式

TCP机制复杂

TCP占用的资源开销比较大

三次握手建立连接,确认双方能够通信

通信过程中保障数据传输的完整性

四次挥手断开连接,确保数据传输的完整

TCP:
1.三次握手,来确立链接成功

A---->B : SYN 请求应答信号。

B---->A : ACK+SYN 回复应答,并且请求应答。

A---->B : ACK 回复应答。

2.通信中确保信号的完整

A--->B : PSH 发送数据

B---->A : ACK 收到信号,若是信号不完整,发剩下的信号

3.四次挥手告别

A---->B : FIN 请求终止。

B---->A : ACK 回复请求。

B---->A : 等待B给A发送完后也发起终止信号。

A---->B : ACK 回复。

3. 网络层:

1. IPV4协议

8位,所以最多是2的8次方,范围就是 0~255,最大255,由于0,和1 特殊,所以最多可以用253个

1. IP地址

管理员IP地址形式:192.168.0.167

内存IP地址形式: 11000000.10101000.00000000.10100111

IP地址 = 网络位 + 主机位

网络位:IP地址所属的网段(局域网的编号)

主机位:局域网中的第几台主机

网段号:网络位不变,主机位全为0

广播号:网络位不变, 主机位全为1

子网掩码:每个IP地址都会搭配一个子网掩码,用来区分IP地址的网络位及主机位

子网掩码展开成二进制,1对应的部分就是IP地址的网络位,0对应的部分就是IP地址的主机位

192.168.0.167

255.255.255.0

11000000.10101000.00000000.10100111

11111111.11111111.11111111.00000000

192.168.0.0

192.168.0.255

2. IP地址的划分:

公有地址

私有地址

A类:1.0.0.0 ~ 126.255.255.255

子网掩码:255.0.0.0

管理超大规模型网络

私有地址:10.0.0.0 ~ 10.255.255.255

B类:128.0.0.0 ~ 191.255.255.255

子网掩码:255.255.0.0

管理大中规模型网络

私有地址:172.16.0.0 - 172.31.255.255

C类:192.0.0.0 ~ 223.255.255.255

子网掩码:255.255.255.0

管理中小规模型网络

私有地址:192.168.0.0 ~ 192.168.255.255

D类:224.0.0.0 ~ 239.255.255.255

用于组播:255.255.255.0

E类:240.0.0.0 ~ 255.255.255.255

用于实验和研究:255.255.255.0

3.MAC地址

:设备自带网卡的地址(该地址是唯一的)

4.端口号:

找到同一台主机不同的应用程序

3.设置虚拟机网络模式:

1.ifconfig

2.将虚拟机IP地址设置为桥接模式:

1.点击"虚拟机"

2.点击"设置"

3.选择"网络适配器"

4.点击"桥接模式"

5.点击"确定"

3.将虚拟机桥接到无线网卡上去

1.点击"编辑"

2.点击"虚拟网络编辑器"

3.点击"更改设置"

4.已桥接至选择无线网卡

5.点击"确定"

4.修改网卡配置文件

1.sudo vim /etc/network/interfaces

2.修改文件内容为:

auto lo

iface lo inet loopback

auto ens33

iface ens33 inet dhcp

3.保存退出

:wq

5.重启网络服务

sudo /etc/init.d/networking restart

6.测试与局域网内其余IP地址是否能够连通

ping 192.168.0.167

ping www.baidu.com

4.UDP编程:

1.套接字:

实现Linux系统下的网络通信

套接字:一次通信对象的抽象

2.socket

cpp 复制代码
 //1.创建用来通信的套接字
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (-1 == sockfd)
    {
        perror("fail to socket");
        return -1;
    }   

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

功能:

创建套接字

参数:

domain: AF_INET 表示IPV4协议

type:套接字类型

SOCK_STREAM:流式套接字

SOCK_DGRAM:数据报套接字

SOCK_RAW:原始套接字

protocol:

TCP和UDP协议:0

返回值:

成功返回用来通信的文件描述符

失败返回-1

3.sendto

cpp 复制代码
   //3.为目的地址赋值
    recvaddr.sin_family = AF_INET;                              //协议族
    recvaddr.sin_port = htons(30000);                           //端口号(将本地字节序转换为网络字节序)
    recvaddr.sin_addr.s_addr = inet_addr("192.168.0.171");      //IP地址(将字符串类型转换为二进制地址类型)

    //4.向目的地址发送数据
    nsize = sendto(sockfd, "hello world", 12, 0, (struct sockaddr *)&recvaddr, sizeof(recvaddr));
    if (-1 == nsize)
    {
        perror("fail to sendto");
        return -1;
    }

    printf("发送成功!\n");

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,

const struct sockaddr *dest_addr, socklen_t addrlen);

功能:

发送信息

参数:

sockfd:套接字文件描述符

buf:发送数据空间首地址

len:发送数据长度

flags:发送属性 默认为0

dest_addr:目标地址存放空间首地址

addrlen:目的地址的长度

返回值:

成功返回发送字节数

失败返回-1

struct sockaddr_in {

sa_family_t sin_family; /* address family: AF_INET */

in_port_t sin_port; /* port in network byte order */

struct in_addr sin_addr; /* internet address */

};

/* Internet address. */

struct in_addr {

uint32_t s_addr; /* address in network byte order */

};

如果sendto对应的套接字没有绑定端口,则sendto绑定一个随机端口完成发送功能

4.inet_addr

cpp 复制代码
 sendaddr.sin_addr.s_addr = inet_addr("192.168.0.171");

in_addr_t inet_addr(const char *cp);

功能:

将字符串的IP地址转换为32位的地址类型

5.htons

cpp 复制代码
sendaddr.sin_port = htons(20000);

uint16_t htons(uint16_t hostshort);

功能:

将本地字节序(小端)转换成网络大端字节序

6.bind

cpp 复制代码
 //2.将发送端套接字与IP地址和端口号绑定
    sendaddr.sin_family = AF_INET;
    sendaddr.sin_port = htons(20000);
    sendaddr.sin_addr.s_addr = inet_addr("192.168.0.171");
    ret = bind(sockfd, (struct sockaddr *)&sendaddr, sizeof(sendaddr));
    if (-1 == ret)
    {
        perror("fail to bind");
        return -1;
    }

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

功能:

将套接字与IP地址和端口进行绑定

参数:

addr:绑定地址结构体空间首地址

addrlen:绑定地址空间大小

返回值:

成功返回0

失败返回-1

注意:

只能绑定自己的IP地址

7.recvfrom

cpp 复制代码
    //3.接收数据
    nsize = recvfrom(sockfd, tmpbuff, sizeof(tmpbuff), 0, NULL, NULL);
    if (-1 == nsize)
    {
        perror("fail to recvfrom");
        return -1;
    }

    printf("接收内容: %s\n", tmpbuff);
cpp 复制代码
  
 socklen_t addrlen = sizeof(sendaddr);
nsize = sendto(sockfd, tmpbuff, strlen(tmpbuff)+1, 0, (struct sockaddr *)&sendaddr, sizeof(sendaddr));
        if (-1 == nsize)
        {
            perror("fail to sendto");
            return -1;
        }

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,

struct sockaddr *src_addr, socklen_t *addrlen);

功能:

接收信息

参数:

sockfd:套接字文件描述符

buf:接收数据空间首地址

len:接收数据长度(是最多接收多少,不是实际接受多少,实际接受多少从返回值返回)

flags:接收的属性 默认为0

src_addr:存放发送方地址空间的地址

addrlen: 要接收的发送方地址的长度

返回值:

成功返回实际接收字节数

失败返回-1

发送端流程:

1.创建用来通信的套接字------socket

2.将发送端套接字与IP地址和端口号绑定-------bind

3.为目的地址赋值-------htons,inet_addr

4.向目的地址发送数据-------sendto

5.关闭套接字------close

cpp 复制代码
#include "../head.h"

int main(void)
{
    int sockfd = 0;
    ssize_t nsize = 0;
    int ret = 0;
    struct sockaddr_in recvaddr;
    struct sockaddr_in sendaddr;

    //1.创建用来通信的套接字
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (-1 == sockfd)
    {
        perror("fail to socket");
        return -1;
    }   

    //2.将发送端套接字与IP地址和端口号绑定
    sendaddr.sin_family = AF_INET;
    sendaddr.sin_port = htons(20000);
    sendaddr.sin_addr.s_addr = inet_addr("192.168.0.171");
    ret = bind(sockfd, (struct sockaddr *)&sendaddr, sizeof(sendaddr));
    if (-1 == ret)
    {
        perror("fail to bind");
        return -1;
    }

    //3.为目的地址赋值
    recvaddr.sin_family = AF_INET;                              //协议族
    recvaddr.sin_port = htons(30000);                           //端口号(将本地字节序转换为网络字节序)
    recvaddr.sin_addr.s_addr = inet_addr("192.168.0.171");      //IP地址(将字符串类型转换为二进制地址类型)

    //4.向目的地址发送数据
    nsize = sendto(sockfd, "hello world", 12, 0, (struct sockaddr *)&recvaddr, sizeof(recvaddr));
    if (-1 == nsize)
    {
        perror("fail to sendto");
        return -1;
    }

    printf("发送成功!\n");

    //5.关闭套接字
    close(sockfd);  

    return 0;
}

接收端流程:

1.创建套接字

2.绑定IP和Port

3.接收信息

4.关闭套接字

cpp 复制代码
#include "../head.h"

int main(void)
{
    int ret = 0;
    int sockfd = 0;
    struct sockaddr_in recvaddr;
    char tmpbuff[4096] = {0};
    ssize_t nsize = 0;

    //1.创建套接字 
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (-1 == sockfd)
    {
        perror("fail to socket");
        return -1;
    }

    //2.绑定IP和Port
    recvaddr.sin_family = AF_INET;
    recvaddr.sin_port = htons(30000);
    recvaddr.sin_addr.s_addr = inet_addr("192.168.0.171");
    ret = bind(sockfd, (struct sockaddr *)&recvaddr, sizeof(recvaddr));
    if (-1 == ret)
    {
        perror("fail to bind");
        return -1;
    }

    //3.接收数据
    nsize = recvfrom(sockfd, tmpbuff, sizeof(tmpbuff), 0, NULL, NULL);
    if (-1 == nsize)
    {
        perror("fail to recvfrom");
        return -1;
    }

    printf("接收内容: %s\n", tmpbuff);

    close(sockfd);

    return 0;
}
cpp 复制代码
#ifndef __HEAD_H__
#define __HEAD_H__

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/wait.h>
#include <time.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/socket.h>
#include <netinet/ip.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#endif

作业:

1.自己实现发送端,从终端接收一个字符串发送给接收端

自己实现接收端,从网络中接收到一个字符串并打印

2.编写两个程序,一个发送端,一个接

发:

cpp 复制代码
#include "../head.h"

int main(void)
{
    char tmpbuff[1024] = {0};
    struct sockaddr_in recvaddr;
    ssize_t nsize = 0;

    //1.创建套接字
    int sockfd = 0;

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (-1 == sockfd)
    {
        perror("fail to socket");
        return -1;
    }

    while (1)
    {
        fgets(tmpbuff, sizeof(tmpbuff), stdin);
        tmpbuff[strlen(tmpbuff)-1] = '\0';

        recvaddr.sin_family = AF_INET;
        recvaddr.sin_port = htons(RECV_PORT);
        recvaddr.sin_addr.s_addr = inet_addr(RECV_IP);
        //2.发送信息 
        nsize = sendto(sockfd, tmpbuff, strlen(tmpbuff)+1, 0, (struct sockaddr *)&recvaddr, sizeof(recvaddr));
        if (-1 == nsize)
        {
            perror("fail to sendto");
            return -1;
        }

        nsize = recvfrom(sockfd, tmpbuff, sizeof(tmpbuff), 0, NULL, NULL);
        if (-1 == nsize)
        {
            perror("fail to recvfrom");
            return -1;
        }
        printf("RECV:%s\n", tmpbuff);
    } 

    //3.关闭套接字
    close(sockfd);

    return 0;
}

收:

cpp 复制代码
#include "../head.h"

int main(void)
{
    //1.创建套接字
    int sockfd = 0;
    int ret = 0;
    char tmpbuff[1024] = {0};
    ssize_t nsize = 0;
    struct sockaddr_in recvaddr;
    struct sockaddr_in sendaddr;
    socklen_t addrlen = sizeof(sendaddr);

    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (-1 == sockfd)
    {
        perror("fail to socket");
        return -1;
    }

    //2.绑定IP和端口
    recvaddr.sin_family = AF_INET;
    recvaddr.sin_port = htons(RECV_PORT);
    recvaddr.sin_addr.s_addr = inet_addr(RECV_IP);
    ret = bind(sockfd, (struct sockaddr *)&recvaddr, sizeof(recvaddr));
    if (-1 == ret)
    {
        perror("fail to bind");
        return -1;
    }

    while (1)
    {
        //3.接收数据
        nsize = recvfrom(sockfd, tmpbuff, sizeof(tmpbuff), 0, (struct sockaddr *)&sendaddr, &addrlen);
        if (-1 == nsize)
        {
            perror("fail to recvfrom");
            return -1;
        }

        printf("%s:%d->%s\n", inet_ntoa(sendaddr.sin_addr), ntohs(sendaddr.sin_port), tmpbuff);

        fgets(tmpbuff, sizeof(tmpbuff), stdin);
        tmpbuff[strlen(tmpbuff)-1] = '\0';
        nsize = sendto(sockfd, tmpbuff, strlen(tmpbuff)+1, 0, (struct sockaddr *)&sendaddr, sizeof(sendaddr));
        if (-1 == nsize)
        {
            perror("fail to sendto");
            return -1;
        }
    }   

    //4.关闭套接字
    close(sockfd);

    return 0;
}

5.TCP编程

发端: 收端:

socket socket

bind

listen

connect accept

send recv

recv send

close close

1.函数接口:

1.socket

cpp 复制代码
 sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == sockfd)
    {
        perror("fail to socket");
        return -1;
    }

UDP: socket(AF_INET, SOCK_DGRAM, 0);

TCP: socket(AF_INET, SOCK_STREAM, 0);

2.listen

cpp 复制代码
 ret = listen(sockfd, 10);
    if (-1 == ret)
    {
        perror("fail to listen");
        return -1;
    }

int listen(int sockfd, int backlog);

功能:

监听发送三次握手连接的套接字,并放入等到处理队列中

参数:

sockfd:套接字文件描述符

backlog:等待队列的大小(最多存放尚未被处理的三次握手请求的个数)

返回值:

成功返回0

失败返回-1

3.accept

cpp 复制代码
 confd = accept(sockfd, NULL, NULL);
    if (-1 == confd)
    {
        perror("fail to accept");
        return -1;
    }

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

功能:

处理等待队列中的第一个套接字

参数:

sockfd:套接字文件描述符

addr:存放发送方IP地址的空间首地址

addrlen:存放发送方IP地址的空间大小

返回值:

成功返回一个新的文件描述符(这个描述符是与接收端相对应的新的套接字)

失败返回-1

4.connect

cpp 复制代码
 ret = connect(sockfd, (struct sockaddr *)&recvaddr, sizeof(recvaddr));
    if (-1 == ret)
    {
        perror("fail to connect");
        return -1;
    }

    printf("连接成功\n");

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

功能:

向接收方发起连接请求

参数:

sockfd:套接字文件描述符

addr:接收方的IP地址和端口号

addrlen:接收方的IP地址和端口号的大小

返回值:

成功返回0

失败返回-1

5.send

cpp 复制代码
fgets(tmpbuff, sizeof(tmpbuff), stdin);
    tmpbuff[strlen(tmpbuff)-1] = '\0';
    nsize = send(sockfd, tmpbuff, strlen(tmpbuff), 0);
    if (-1 == nsize)
    {
        perror("fail to send");
        return -1;
    }

    printf("发送成功!\n");

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

功能:

向接收方发送数据

参数:

sockfd:套接字文件描述符

buf:要发送的数据的首地址

len:要发送的数据的长度

flags:标志位

返回值:

成功返回发送字节数

失败返回-1

6.recv

cpp 复制代码
 memset(tmpbuff, 0, sizeof(tmpbuff));
    nsize = recv(sockfd, tmpbuff, sizeof(tmpbuff), 0);
    if (-1 == nsize)
    {
        perror("fail to recv");
        return -1;
    }

    printf("实际接收 %ld个字节, 内容:%s\n", nsize, tmpbuff);

ssize_t recv(int sockfd, void *buf, size_t len, int flags);

功能:

接收发送方发送的数据

参数:

sockfd:套接字文件描述符

buf:接收数据的缓冲区首地址

len:接收数据的缓冲区的大小

flags:标志位

返回值:

成功返回实际接收字节数

失败返回-1

对方关闭返回0

发送端流程:

1.创建用来通信的套接字------socket

2.给接收端(目的地)IP地址和端口号赋值

3.给目的地发送连接请求------connect

4.向目的地址发送数据-------send

5.接收目的地发来的数据-------recv

5.关闭套接字------close

cpp 复制代码
#include "../head.h"

int main(void)
{
    int sockfd = 0;
    int ret = 0;
    struct sockaddr_in recvaddr;
    char tmpbuff[4096] = {0};
    ssize_t nsize = 0;

    recvaddr.sin_family = AF_INET;
    recvaddr.sin_port = htons(RECV_PORT);
    recvaddr.sin_addr.s_addr = inet_addr(RECV_IP);

    //1.创建套接字
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == sockfd)
    {
        perror("fail to socket");
        return -1;
    }

    //2.发送连接请求
    ret = connect(sockfd, (struct sockaddr *)&recvaddr, sizeof(recvaddr));
    if (-1 == ret)
    {
        perror("fail to connect");
        return -1;
    }

    printf("连接成功\n");

    //3.发送数据
    fgets(tmpbuff, sizeof(tmpbuff), stdin);
    tmpbuff[strlen(tmpbuff)-1] = '\0';
    nsize = send(sockfd, tmpbuff, strlen(tmpbuff), 0);
    if (-1 == nsize)
    {
        perror("fail to send");
        return -1;
    }

    printf("发送成功!\n");

    //4.接收数据
    memset(tmpbuff, 0, sizeof(tmpbuff));
    nsize = recv(sockfd, tmpbuff, sizeof(tmpbuff), 0);
    if (-1 == nsize)
    {
        perror("fail to recv");
        return -1;
    }

    printf("实际接收 %ld个字节, 内容:%s\n", nsize, tmpbuff);

    //5.关闭
    close(sockfd);

    return 0;
}

接收端流程:

1.创建套接字 -----socket

2.给自己的IP和端口赋值

2.绑定套接字(只能绑定自己的)IP和Port -------bind

3.监听是否收到连接请求------listen

4.处理连接请求 ---------accept

5.接收数据---------recv

6.发送数据--------send

. 7.关闭俩个------confd和sockfd

cpp 复制代码
#include "../head.h"

int main(void)
{
    int sockfd = 0;
    int confd = 0;
    int ret = 0;
    char tmpbuff[4096] = {0};
    struct sockaddr_in recvaddr;
    ssize_t nsize = 0;

    recvaddr.sin_family = AF_INET;
    recvaddr.sin_port = htons(RECV_PORT);
    recvaddr.sin_addr.s_addr = inet_addr(RECV_IP);
    
    //1.创建套接字
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == sockfd)
    {
        perror("fail to socket");
        return -1;
    }

    //2.绑定IP地址和端口号
    ret = bind(sockfd, (struct sockaddr *)&recvaddr, sizeof(recvaddr));
    if (-1 == ret)
    {
        perror("fail to bind");
        return -1;
    }

    //3.监听
    ret = listen(sockfd, 10);
    if (-1 == ret)
    {
        perror("fail to listen");
        return -1;
    }

    //4.处理连接请求
    confd = accept(sockfd, NULL, NULL);
    if (-1 == confd)
    {
        perror("fail to accept");
        return -1;
    }

    //5.收发
    nsize = recv(confd, tmpbuff, sizeof(tmpbuff), 0);
    if (-1 == nsize)
    {
        perror("fail to recv");
        return -1;
    }

    printf("RECV:%s\n", tmpbuff);

    memset(tmpbuff, 0, sizeof(tmpbuff));
    fgets(tmpbuff, sizeof(tmpbuff), stdin);
    tmpbuff[strlen(tmpbuff)-1] = '\0';

    nsize = send(confd, tmpbuff, strlen(tmpbuff), 0);
    if (-1 == nsize)
    {
        perror("fail to recv");
        return -1;
    }

    //6.关闭
    close(confd);
    close(sockfd);

    return 0;
}

sendto 和 send的比较:

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

这里send不需要写参数接收端的IP和端口信息是因为在之前就已经建立 了连接。

recvfrom 和 recv的比较:

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

这里recv不需要写参数接收端的IP和端口信息是因为在之前就已经建立 了连接。

相关推荐
换个号退隐江湖i1 小时前
LLM 调试日志刷屏怎么办?用 tee 边看边存
linux
@insist1231 小时前
系统集成项目管理工程师-信息和文档管理
网络·数据库·软考·系统集成项目管理工程师·软考中项·软件水平考试
极客互动API1 小时前
企业微信 iPad 协议服务搭建与 AI 回调实战
网络·汇编·人工智能·微信·企业微信·ipad
DYWorker0011 小时前
搭建嵌入式Linux虚拟机开发环境
linux
QH139292318801 小时前
Keysight N5183B N5183B 微波模拟信号发生器
网络·单片机·嵌入式硬件·集成测试·信息与通信
weipt1 小时前
从ollama到llama.cpp
linux·服务器·llama
LabVIEW开发1 小时前
基于原始套接字实现LabVIEW网络Ping检测的编程方法
网络·labview·labview知识·labview功能·labview程序
加油码2 小时前
动静态库的链接底层原理(进阶版)
linux·c++
东莞和裕包装2 小时前
如何管控广州定制纸箱生产中的啤切精度与印刷色差质量问题
大数据·运维·网络·人工智能
小飞侠在吗3 小时前
Windows 下 Nginx 访问 127.0.0.1 报 50x 错误:根因是路径里 \t 被当成 Tab
运维·windows·nginx