C++跨平台socket编程

C++跨平台socket编程

  • 一、概述
    • 1.1 TCP协议
      • 1.1 TCP 的主要特性
      • 1.2 TCP报文格式
    • UDP报文格式
    • IP协议
    • 使用windows编辑工具直接编辑Linux上代码
  • 二、系统socket库
    • 1.windows上加载socket库
    • 2.创建socket
      • 2.1 windows下
      • 2.2 linux下
    • 3.网络字节序
    • 4.bind端口
    • 5.listen监听并设置最大连接数
    • 6.accept读取用户连接信息
    • 7.服务器通过recv接收客户端发送信息
      • 服务器循环接收客户端数据
    • 8.服务器send回应客户端数据
    • 9.服务器开启多线程并发处理客户端连接
      • 9.1 windows下
      • 9.2 linux下
  • 三、封装跨平台XTcp类
    • 1.windows下测试
    • 2.linux下测试
    • 3.创建并测试XTcp的dll动态链接库
      • 3.1 创建
      • 3.2 测试
    • 4.创建并测试XTcp的so动态链接库
      • 4.1 创建
      • 4.2 测试
  • 四、TcpClient编写与tcp编程总结
    • 1.connect与三次握手的过程
      • 1.1 为什么需要三次握手?
    • 2 connect客户端连接服务器
    • 3.tcp编程总结
  • 五、高并发服务器开发和测试
    • 1.Windows中设置socket阻塞和非阻塞
    • 2.Linux中设置socket阻塞和非阻塞
    • 3.通过select实现connect的超时处理
      • 为什么使用select实现connect的超时处理?
    • 4.并发测试工具ab
    • 5.基于epoll的高性能服务器
      • 注意
    • 6.epoll、select 和阻塞 accept 之间的关系
      • 1.监听套接字队列
      • 2.**accept 函数**
      • 3.I/O 多路复用机制
  • 六、http协议
    • 1.请求格式
    • 2.响应格式
    • 3.接受浏览器发送的请求
    • 4.响应浏览器的请求
    • 5.通过正则表达式分析浏览器请求
      • 5.1正则表达式
    • 6.获取请求文件大小并生成http协议头
    • 7.发送用户请求的页面和图片
    • 8.完成HTTP1.1协议并移植到windows
    • 9.通过正则表达式获取请求页面文件后缀php
    • 10.调用php-cgi完成php脚本解析
    • 11.完成php脚本解析并返回给浏览器
    • 12.支持给php传递基于URLGET变量
    • 13.封装XhttpServer
    • 14.封装XhttpClient处理单个请求的接口
    • 15.封装Xhttp协议响应类XHttpResponse

一、概述

image-20240613090208126

1.1 TCP协议

TCP协议提供可靠、有序、且无差错的数据传输服务。

1.1 TCP 的主要特性

面向连接

  • 在传输数据之前,TCP 需要在通信双方之间建立一个连接,这个过程称为三次握手(Three-way Handshake)。

可靠传输

  • TCP 通过确认(ACK)、序列号、超时重传等机制保证数据的可靠传输。

有序传输

  • TCP 确保数据按序到达目标,即使数据包乱序到达,TCP 也会重新排序。

流量控制

  • TCP 使用滑动窗口机制来控制数据流量,避免发送方发送数据过快,超过接收方的处理能力。

拥塞控制

  • TCP 有内置的拥塞控制算法,通过调整发送速率避免网络拥塞。

1.2 TCP报文格式

image-20240613090623626

image-20240613090657390

image-20240613090713341

image-20240613090855493

image-20240613091114833

UDP报文格式

image-20240613091800963

image-20240613091815421

image-20240613091906618

image-20240613092003392

IP协议

image-20240613095801498

image-20240613095904615

image-20240613100200476

image-20240613100319161

image-20240613101134185

image-20240613101444935

image-20240613102411601

使用windows编辑工具直接编辑Linux上代码

ubuntu: apt-get install samba

centos7:sudo yum install samba samba-client samba-common -y

配置环境:

vim ../etc/samba/smb.conf

文件末尾加入共享目录:

image-20240613112503982

重启一下服务:

image-20240613112541070

在根目录下创建code目录:

image-20240613112626849

设置权限:

image-20240613113536311

添加Samba用户,设置密码:

image-20240613114027279

检查服务是否都启动了(ubuntu跳过):

image-20240613114111312

如果服务没有启动(centos):

sudo systemctl enable smb nmb
sudo systemctl start  smb nmb

如果服务没有启动(ubuntu):

sudo service smbd restart

在windows系统中连接共享目录:

image-20240613114126145

image-20240613114131475

image-20240613114204383

image-20240613114308287

如果再次连接samba,并尝试进入共享文件夹,出现报错你没有权限访问,可能是防火墙问题,输入命令:

image-20240614090951999

sudo setenforce 0

sudo iptables -F

二、系统socket库

套接字(Socket)是网络编程中用于在计算机之间进行通信的端点。它提供了一种进程间通信的机制,可以在同一台计算机上或不同计算机之间进行数据交换。套接字的概念和实现是网络通信的重要基础。

套接字是一个主机本地应用程序创建的,被操作系统所控制的接口(“门”)。

应用程序通过这个接口,使用传输层提供的服务,跨网络发送(接收)消息到(从)其他应用进程。

C/S模式的通信接口——套接字接口。

1.windows上加载socket库

在Windows中,Winsock库不是自动加载的。在使用网络功能之前,程序必须显式地初始化Winsock库,以便系统可以分配资源并为网络通信做好准备。初始化之后,程序就可以创建套接字并使用网络功能了。

image-20240613132121538

每次编写涉及套接字编程的应用程序时,都需要在程序的开头部分添加这两行代码,并在程序结束时调用 WSACleanup() 来清理资源。

2.创建socket

2.1 windows下

image-20240613133102728

socket(AF_INET, SOCK_STREAM, 0):创建一个套接字。

AF_INET 指定使用IPv4地址族,SOCK_STREAM 指定使用流式套接字(TCP),0 表示使用默认的协议(对于 SOCK_STREAM 即TCP协议)。socket 函数成功时返回一个非负整数表示创建的套接字,失败时返回 -1

2.2 linux下

因为Linux下各种库和windows不一样,个别函数也不一样,因此使用条件编译的方法。

image-20240613141234085

image-20240613141241423

image-20240613141319406

我们一次性创建两千个socket。

在windows下,可以正常创建。

image-20240613142043791

在Linux下,因为系统默认一个进程最多创建1024个socket,因此1024之后的创建不了。

image-20240613142206232

我们可以通过ulimit -n修改最大可创建socket个数。

此时再运行就可以了。

image-20240613142305788

3.网络字节序

网络字节序(Network Byte Order)是指在网络传输中,数据的字节排列顺序。它采用的是大端字节序(Big-Endian),即最高有效字节(Most Significant Byte,MSB)在前,最低有效字节(Least Significant Byte,LSB)在后。

image-20240613145510909

在这里插入图片描述

#include <arpa/inet.h>//将主机字节序转换为网络字节序unit32_t htonl (unit32_t hostlong);unit16_t htons (unit16_t hostshort);//将网络字节序转换为主机字节序unit32_t ntohl (unit32_t netlong);unit16_t ntohs (unit16_t netshort);

4.bind端口

获取端口号

image-20240613150623865

配置地址结构

image-20240613150655433

绑定端口

image-20240613151309287

image-20240613151254967

image-20240613151321306

在Linux中编译发现报错,说明有头文件不一致。
image-20240613151408675

image-20240613151427482

image-20240613151440546

image-20240613151638056

image-20240613151722048

image-20240613151714876

5.listen监听并设置最大连接数

int listen(int sockfd, int backlog);

参数说明

  • sockfd:这是一个已经通过socketbind函数创建并绑定到特定地址和端口的套接字描述符。
  • backlog:指定等待连接队列的最大长度,即在accept函数被调用之前,可以有多少个连接请求处于等待状态。

当一个服务器套接字处于监听状态时,它会创建一个队列来存储那些尚未被服务器接受(accept)的传入连接请求。当完全连接队列已满且有新的连接到达时,新连接可能会被拒绝(客户端将收到一个错误)。这个队列实际上包含两个部分:

  • 半连接队列:存放已经完成三次握手中的第一步(SYN)但尚未完成整个握手过程的连接请求。

  • 完全连接队列:存放已经完成三次握手的连接请求,等待应用程序调用 accept

在实际应用中,backlog 的值会影响服务器在高并发环境下的表现。设置过低的 backlog 值可能导致拒绝合法的连接请求,而设置过高的值可能会消耗更多的系统资源。

image-20240613153606265

6.accept读取用户连接信息

accept 函数是套接字编程中用于从监听队列中提取第一个连接请求的系统调用。它会创建一个新的套接字用于与客户端进行通信。accept 函数通常与服务器端的监听套接字配合使用,在客户端发起连接请求并经过 listen 函数处理后,由 accept 函数接受该请求。

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

参数说明

  • sockfd:已绑定到本地地址并处于监听状态的套接字描述符。
  • addr:指向 sockaddr 结构体的指针,用于存储客户端的地址信息。可以是 NULL
  • addrlen:指向一个 socklen_t 变量的指针,用于存储 addr 结构体的大小。调用前应设置为 addr 结构体的大小,调用后包含实际的地址长度。可以是 NULL

返回值

  • 成功:
    • accept 函数成功时返回一个新的套接字描述符,这个新的套接字用于与客户端进行通信。
    • 返回值是一个非负整数,表示新的连接套接字。
  • 失败:
    • accept 函数失败时返回 -1,并设置 errno 以指示错误类型。

image-20240613161329417

image-20240613161907894

image-20240613161856217

7.服务器通过recv接收客户端发送信息

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

参数说明

  • sockfd:一个已经连接的套接字描述符。
  • buf:指向用于存放接收到的数据的缓冲区。
  • len:缓冲区的长度,即可以接收的数据的最大字节数
  • flags:接收操作的标志,可以是0或以下值的组合:
    • MSG_OOB:接收带外数据。
    • MSG_PEEK:窥视操作,将数据复制到缓冲区但不从输入队列中删除。
    • MSG_WAITALL:等待完整的数据,直到缓冲区满或发生错误。
    • MSG_DONTWAIT:非阻塞操作,如果没有数据可读,立即返回。

image-20240613163439267

image-20240613163516966

服务器循环接收客户端数据

image-20240613165850994

image-20240613165709525

不过现在有一个新的问题,如果有第二个、第三个…多个客户端连接,只有当第一个客户端退出,服务器才能收到其他客户端的消息,这是因为我们是在单线程中处理消息。

8.服务器send回应客户端数据

image-20240614094115205

image-20240614094238801

9.服务器开启多线程并发处理客户端连接

image-20240614094858199

image-20240614094922904

9.1 windows下

image-20240614094823358

9.2 linux下

image-20240614100011903

image-20240614095954386

三、封装跨平台XTcp类

为什么需要XTcp类?

与系统相关的头文件、socket的初始化、绑定等函数容易出错,同时写起来很麻烦,因此包装成一个类,直接调用类的方法。

image-20240614102940041

image-20240614103013506

image-20240614142423415

  • 构造函数
    • image-20240614142500436
  • 创建和关闭socket
    • image-20240614142513832
    • 关于为什么提供CloseSocket而不是直接析构中调用closesocket?
      • image-20240614143446275
      • 析构中调用WSACleanup()同理,目前先不做析构。
  • 绑定与监听
    • image-20240614142531557
  • 接收客户端连接
    • image-20240614142557319
  • 发送、接收数据
    • image-20240614142613638
  • main 和 Tcpserver
    • image-20240614142758820
    • image-20240614142903928

1.windows下测试

image-20240614143612055

2.linux下测试

image-20240614143919626

image-20240614144132686

3.创建并测试XTcp的dll动态链接库

3.1 创建

image-20240614145600444

image-20240614145723975

image-20240614145949052

注意,在Windows系统上,套接字编程需要链接 Ws2_32.lib 库。确保在项目设置中包含这个库。
image-20240614150730908

image-20240614151632011

image-20240614151637950

image-20240614151758866

image-20240614151850867

如果程序需要执行,则要配置工作目录为dll所在目录。

image-20240614151939723

image-20240614152000819

image-20240614152006743

3.2 测试

image-20240614153014783

image-20240614153039050

image-20240614153106262

image-20240614153152787

image-20240614153518313

4.创建并测试XTcp的so动态链接库

4.1 创建

image-20240614154413141

运行makefile会发现有报错,windows的导出宏在linux没有,还有头文件只能包含一次也没有。
image-20240614154528775

image-20240614154844373

image-20240614154907370

4.2 测试

image-20240614155003044

再把测试程序也编译一下。

image-20240614155910293

image-20240614155919032

根据提示修改makefile文件

image-20240614160025436

image-20240614160040524

image-20240614160455057

image-20240614160535892

在Linux中,运行程序时确实需要确保动态库的路径在系统的库搜索路径中,否则程序将无法找到并加载所需的动态库。

image-20240614160630703

image-20240614160814607

四、TcpClient编写与tcp编程总结

1.connect与三次握手的过程

image-20240614165430057

1.1 为什么需要三次握手?

确保双方都有能力发送和接收数据:通过三次握手,客户端和服务器可以确认双方都能够发送和接收数据包。

同步初始序列号:三次握手的过程还可以确保双方同步初始序列号,以防止因网络传输延迟或重传而导致的数据包混乱。

防止旧的重复连接初始化:三次握手可以防止旧的、重复的连接请求在网络中滞留并意外创建连接。

如果只进行两次,客户端收到服务器发送的ack后,知道服务器能收到客户端消息,所以客户端能保证服务器收到正确数据。但是如果客户端不回复服务器这一次的syn,那么服务器不知道客户端能否正确收到服务器的消息。

2 connect客户端连接服务器

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

参数说明:

  • sockfd:由socket函数返回的套接字文件描述符。它标识一个套接字。
  • addr:指向struct sockaddr类型的指针,包含了要连接的服务器的IP地址和端口号。
  • addrlen:结构体sockaddr的长度。

返回值:

  • 成功:返回0。
  • 失败:返回-1,并设置errno以指示错误原因。

image-20240618192407353

image-20240618192425373

image-20240618192441366

image-20240618192648879

3.tcp编程总结

image-20240618192747991

五、高并发服务器开发和测试

1.Windows中设置socket阻塞和非阻塞

阻塞模式:

  • 套接字在创建时默认是阻塞模式。
  • 调用套接字函数(如 acceptconnectrecvsend 等)时,如果操作无法立即完成,函数会阻塞(即挂起执行)直到操作完成或发生错误。

非阻塞模式:

  • 调用套接字函数时,如果操作无法立即完成,函数立即返回一个错误(如 EWOULDBLOCK),而不是阻塞等待。
  • 程序通常需要轮询套接字状态或使用事件驱动机制(如 selectpollepoll)时使用非阻塞模式。

我们的目的是:在建立连接的时候是非阻塞模式,在接收数据的时候是阻塞模式,因为接收数据我们是多线程方式,所以设置为阻塞不影响别的线程运行。

ioctlsocket 是 Windows 操作系统中用于控制套接字行为的函数。

int ioctlsocket(SOCKET s,long cmd,u_long *argp
);

参数说明:

  • s: 套接字的描述符。
  • cmd: 控制命令,指定要执行的操作。
  • argp: 命令参数,根据 cmd 指定不同的含义。

FIONBIO: 设置或清除非阻塞模式。

  • 如果 *argp 为非零值,则套接字设置为非阻塞模式。
  • 如果 *argp 为零,则套接字设置为阻塞模式。

返回值:

  • 如果函数成功,返回 0。

image-20240618194404408

2.Linux中设置socket阻塞和非阻塞

image-20240618194912296

image-20240618194929822

image-20240618200049033

3.通过select实现connect的超时处理

为什么使用select实现connect的超时处理?

  1. 阻塞 connect 无法设置超时
    • connect 是一个阻塞操作,当服务器不可达时,可能会阻塞很长时间。这会导致程序响应变慢,尤其是在高并发环境下。
  2. 非阻塞 connect
    • 通过将套接字设置为非阻塞模式,connect 会立即返回并设置 errnoEINPROGRESS,表示连接正在进行中。此时,程序不会被阻塞,可以继续处理其他任务。

在非阻塞模式下使用 select 实现 connect 的超时处理是一个常见的做法,因为直接使用阻塞的 connect 函数无法设置超时。

select是一个用于多路复用的系统调用,用来监视多个文件描述符,等待其中的一个或多个文件描述符变为"就绪"状态,也就是可以进行I/O操作(如读或写)而不会阻塞。

在 select 函数中,有三个文件描述符集合用于监听不同类型的事件:

  1. 读集合(readfds):用于监听可读事件。读事件是指文件描述符上有数据可以读取,或者连接已经关闭,或有一个新的连接请求(对于监听套接字)。当某个文件描述符触发读事件时,select会在 readfds 集合中设置该文件描述符。
  2. 写集合(writefds):用于监听可写事件。写事件是指文件描述符可以执行写操作而不会阻塞。通常在以下情况下触发写事件:当套接字的发送缓冲区有空间时,select会在 writefds 集合中设置该文件描述符。
  3. 异常集合(exceptfds):用于监听异常事件。

文件描述符集合:

select 中,文件描述符集合使用 fd_set 结构。可以通过以下宏来操作文件描述符集合:

  • FD_ZERO(fd_set *set):清空集合。
  • FD_SET(int fd, fd_set *set):将文件描述符加入集合。
  • FD_CLR(int fd, fd_set *set):将文件描述符从集合中删除。
  • FD_ISSET(int fd, fd_set *set):检查文件描述符是否在集合中。

我们可以将需要监听的套接字放入套接字文件描述符集合,由该集合负责帮我们监听该文件描述符表中这些套接字文件描述符对应的套接字的缓冲区中是否有数据需要处理。

这个监听集合的大小为1024(默认最大值),但需要注意的是,虽然这个集合的大小为1024,但实际能帮我们监听的客户端套接字只有1020个,因为前1-3个分别用于监听标准输入、标准输出和标准出错,第四个用于存放服务器套接字。通过这个监听集合,我们就可以实现对多个socket的同时监听。

在非阻塞模式下调用 connect 函数时,它不会阻塞等待连接完成,而是立即返回。如果连接不能立即完成,errno 会被设置为 EINPROGRESS,表示连接正在进行中。

当套接字连接成功或失败时,套接字会变为可写,因此应该监听写事件。

image-20240618215849407

代码如下:
image-20240618223010258

image-20240618223120134

4.并发测试工具ab

image-20240618223309231

image-20240618223710142

image-20240618223912645

image-20240619084846741

5.基于epoll的高性能服务器

epoll 是 Linux 特有的 I/O 多路复用机制,适用于处理大量并发连接。它比传统的 selectpoll 更高效,能够处理更高的并发数。

epoll 支持两种触发模式:边缘触发(Edge Triggered, ET)和水平触发(Level Triggered, LT)。

  • 水平触发(LT):

    这是 epoll 的默认模式,类似于 selectpoll 的工作方式。当文件描述符处于就绪状态时,epoll_wait 会返回该文件描述符,每次调用 epoll_wait 都会返回,直到事件被处理。

    • 优点:

      简单直接,每次都有数据时都会通知。

    • 缺点:

      当不处理事件时,epoll_wait 会不断返回就绪状态,可能导致重复处理同一事件。

  • 边缘触发(ET):

    在边缘触发模式下,当文件描述符状态发生变化(如从不可读变为可读)时,epoll_wait 会返回该文件描述符。事件只会在状态变化时触发,处理后不会再次触发,直到状态再次变化。

    • 优点:

      高效,减少了不必要的系统调用。

      适用于处理高并发连接,避免重复处理同一事件。

    • 缺点:

      复杂,需要确保每次事件处理时读取或写入尽可能多的数据,避免丢失事件。

    水平触发(level-trggered)

    • 只要文件描述符关联的读内核缓冲区非空,有数据可以读取,就一直发出可读信号进行通知。
    • 当文件描述符关联的内核写缓冲区不满,有空间可以写入,就一直发出可写信号进行通知。

    边缘触发(edge-triggered)

    • 当文件描述符关联的读内核缓冲区由空转化为非空的时候,则发出可读信号进行通知。

    • 当文件描述符关联的内核写缓冲区由满转化为不满的时候,则发出可写信号进行通知。

      水平触发是只要读缓冲区有数据,就会一直触发可读信号,而边缘触发仅仅在空变为非空的时候通知一次。

epoll的主要组件:

  1. epoll_create:创建一个 epoll 实例。
  2. epoll_ctl:控制 epoll 实例,注册、修改或删除感兴趣的文件描述符事件。
  3. epoll_wait:等待事件的发生,并返回已经准备好的文件描述符。

image-20240619171908775

image-20240619171939405

image-20240619171958914

image-20240619172023047

image-20240619172030330

注意

使用apache测试工具进行测试,发现进度卡在900条。

image-20240619172210965

主要问题在于accept部分,如果同时有多个连接到来,并且都是请求与服务器进行的连接,我们只accept一次,其他的连接就丢失了,因为我们是边缘触发,只通知一次。
image-20240619172431658

但是accept是阻塞函数,如果调用的时候没有新的连接到来了,就会一直阻塞着,因此我们需要首先设置套接字为非阻塞。

image-20240619173127635

image-20240619173141024

image-20240619162715351
处理速度是5000多次每秒,是之前的八倍。

image-20240619173048237

在if(events[i].data.fd == tcp.sock)判断条件下,可能会有多个客户端同时请求服务器,但是最开始只accept一次,会导致一些请求被丢失或者延迟处理。关于为什么设置tcp.sock为非阻塞,因为没法直接判断监听套接字队列是否为空,因此通过不停调用accept来间接判断,如果tcp.sock是阻塞,那么当队列为空,就会阻塞整个流程,导致后续只有新连接到来才会停止阻塞,反而tcp.sock为非阻塞,可以根据accept的返回值判断是否队列为空。

6.epoll、select 和阻塞 accept 之间的关系

1.监听套接字队列

当服务器调用 listen 函数将套接字设置为监听模式时,内核会为该套接字分配一个连接队列。

这个队列包含所有已完成三次握手但尚未被服务器 accept 的客户端连接。

队列与 I/O 多路复用机制(如 epoll、select 或阻塞的 accept)是独立的。

2.accept 函数

accept 函数用于从监听队列中提取一个已完成的连接,并为该连接创建一个新的套接字。

如果队列为空,阻塞模式下的 accept 函数会阻塞,直到有新的连接可用。非阻塞模式下的 accept 会立即返回,并设置 errnoEAGAINEWOULDBLOCK

3.I/O 多路复用机制

I/O 多路复用机制(如 selectpollepoll)用于监视多个文件描述符,查看它们是否准备好进行 I/O 操作(如读或写)。

六、http协议

HTTP/1.0(短连接):

  • 每个请求/响应对都要创建一个新的TCP连接,服务器在发送完响应后立即关闭连接。

HTTP/1.1(持续连接):

  • 支持持久连接,即默认情况下,TCP连接会保持打开,允许在同一个连接上发送多个请求和响应。
  • 支持分块传输编码,可以在响应主体的长度未知时逐块传输。

HTTP/2(二进制协议):

  • HTTP/2 使用二进制格式传输数据,而不是HTTP/1.1 的文本格式。
  • 在一个TCP连接上可以发送多个请求和响应,彼此互不干扰。
  • 使用HPACK压缩算法减少头部大小,降低带宽消耗。

1.请求格式

请求方法 请求地址 协议版本 回车

image-20240619190636294

image-20240619190534133

2.响应格式

image-20240619191200406

image-20240619191513064

3.接受浏览器发送的请求

image-20240619223341268

image-20240619223929096

image-20240619205431421

image-20240619205341101
image-20240619205354517

image-20240619205414688

4.响应浏览器的请求

image-20240619225009577

image-20240619210127227
image-20240619210043990

如果将协议长度改为6呢?
image-20240619225037387

image-20240619210225604

image-20240619210205110

5.通过正则表达式分析浏览器请求

5.1正则表达式

image-20240620085856298

贪婪模式

image-20240620092633833

非贪婪模式

image-20240620092756660

image-20240620093900330

image-20240620094048345

image-20240620112800589

image-20240620112734682

image-20240620112906774

6.获取请求文件大小并生成http协议头

int fseek(FILE *stream, long int offset, int whence);

参数:

  • FILE *stream:指向文件的指针,该文件是通过 fopen 打开的。
  • long int offset:偏移量,表示从 whence 开始移动的字节数。
  • int whence:决定偏移的起始位置,有三个可能的值:
    • SEEK_SET:文件开头。
    • SEEK_CUR:当前位置。
    • SEEK_END:文件末尾。

返回值:

fseek 成功时返回 0,失败时返回非零值。

long int ftell(FILE *stream);

ftell 是 C 标准库中的一个函数,用于获取文件指针的当前位置。它与 fseek 配合使用,可以实现对文件的随机访问和定位。

参数:

  • FILE *stream:指向文件的指针,该文件是通过 fopen 打开的。

返回值:

  • 成功时:返回当前文件指针的位置(相对于文件开头的字节数)。
  • 失败时:返回 -1L,并设置 errno
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

fread 是 C 标准库中的一个函数,用于从文件中读取数据。它在文件指针当前位置开始读取指定数量的数据块,并将数据存储到指定的内存位置。

参数:

  • void *ptr:指向存储读取数据的内存位置的指针。
  • size_t size:每个数据块的大小(以字节为单位)。
  • size_t nmemb:要读取的数据块的数量。
  • FILE *stream:指向文件的指针,该文件是通过 fopen 打开的。

返回值:

  • 成功时:返回实际读取的数据块数量(nmemb)。
  • 失败时:如果读取时发生错误或到达文件末尾,则返回值可能小于 nmemb

image-20240620140026524

image-20240620140047982

image-20240620140100166

7.发送用户请求的页面和图片

image-20240620143522347

image-20240620143540858

image-20240620140841937

image-20240620141424835

试试请求一个完整的网页。
image-20240620144002332

image-20240620144054881

8.完成HTTP1.1协议并移植到windows

我们目前的程序是一次连接发送一次数据,如果想再次请求页面,就要重新建立连接。

image-20240620142725714

image-20240620142832052

在创建线程执行Main之前都是没问题的,但是Main里我们每次处理完一次连接请求就会退出线程,因此需要改为不停地保持连接处理直到客户端单方面关闭连接。

image-20240620143055748

这里将所有处理代码放入循环中,同时在buf的有效数据最后一位加入’\0’,以免上一次读的数据影响这一次结果。

image-20240620143316998

image-20240620143431699

9.通过正则表达式获取请求页面文件后缀php

image-20240620152307862

image-20240620152322888

image-20240620151630174

10.调用php-cgi完成php脚本解析

image-20240620153014252

image-20240620153000459

image-20240620153736550

11.完成php脚本解析并返回给浏览器

在上一节我们实现的php脚本解析会将消息报头部分也发送给客户端,在这一节我们修改一下程序只发送正文部分。

image-20240620200846832

image-20240620200906266

12.支持给php传递基于URLGET变量

当我们想给php传递参数的时候,目前的程序是不支持的,我们需要先修改正则表达式,然后修改发送给php的命令。

image-20240620201615826

image-20240620202047256

image-20240620213715720

image-20240620213744345

image-20240620213301107

13.封装XhttpServer

关于之前与创建服务器相关的代码可以封装到XhttpServer类中。

image-20240621091657647

14.封装XhttpClient处理单个请求的接口

15.封装Xhttp协议响应类XHttpResponse

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

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

相关文章

css布局之flex应用

/*父 100*/.parent-div {/* 这里添加你想要的属性 */display: flex;flex-direction: row; //行justify-content: space-between; //左右对齐align-items: center;flex-wrap: wrap; //换行}/*中 90 10 */.middle-div {/* 这里添加你想要的属性 */display: flex;flex-direction:…

Redis 入门篇

文章目录 Redis简介关系型数据库:非关系型数据库 Redis应用场景Redis下载和安装Redis 数据类型Redis 常用命令字符串 string 操作命令哈希 hash 操作命令列表 list 操作命令集合 set 操作命令有序集合 sorted set 操作命令通用命令 Jedis 快速入门配置依赖建立连接 / 操作 Jedi…

使用python下载图片且批量将图片插入word文档

最近有一个小的功能实现&#xff0c;从小某书上下载指定帖子的图片们&#xff0c;然后批量插入到word文档中&#xff0c;便于打印。于是有了以上需求。 一、下载图片 1、首先获取图片们的链接img_urls 首先&#xff0c;获取到的指定帖子的所有信息可以存入一个json文件中&am…

NeRF从入门到放弃4: NeuRAD-针对自动驾驶场景的优化

NeuRAD: Neural Rendering for Autonomous Driving 非常值得学习的一篇文章&#xff0c;几乎把自动驾驶场景下所有的优化都加上了&#xff0c;并且也开源了。 和Unisim做了对比&#xff0c;指出Unisim使用lidar指导采样的问题是lidar的垂直FOV有限&#xff0c;高处的东西打不…

深入源码设计!Vue3.js核心API——Computed实现原理

如果您觉得这篇文章有帮助的话&#xff01;给个点赞和评论支持下吧&#xff0c;感谢~ 作者&#xff1a;前端小王hs 阿里云社区博客专家/清华大学出版社签约作者/csdn百万访问前端博主/B站千粉前端up主 此篇文章是博主于2022年学习《Vue.js设计与实现》时的笔记整理而来 书籍&a…

【AI技术】GPT-4o背后的语音技术猜想

前言&#xff1a; 本篇文章全文credit 给到 台大的李宏毅老师&#xff0c;李宏毅老师在机器学习上风趣幽默、深入浅出的讲解&#xff0c;是全宇宙学AI、讲中文学生的福音&#xff0c;强力推荐李宏毅老师的机器学习课程和深度学习 人工智能导论&#xff1b; 李宏毅老师的个人长…

wordpress教程自动采集并发布工具

随着互联网的快速发展&#xff0c;越来越多的人开始关注网络赚钱。而对于许多人来说&#xff0c;拥有一个自己的个人网站是一个不错的选择。然而&#xff0c;要让自己的个人网站内容丰富多样&#xff0c;就需要不断地进行更新。那么&#xff0c;有没有一种方法可以让我们轻松地…

1-Wire的使用

代码&#xff1a; ds18b20.c /*《AVR专题精选》随书例程3.通信接口使用技巧项目&#xff1a;1-Wire 单总线的使用文件&#xff1a;ds1820.c说明&#xff1a;DS18B20驱动文件。为了简单&#xff0c;没有读取芯片地址&#xff0c;也没有计算校验作者&#xff1a;邵子扬时间&…

实现文件分片合并功能并使用Github Actions自动编译Release

一、编译IOS镜像 1.1 编译 起因是公司电脑使用的Win11 23H2的预览版&#xff0c;这个预览版系统的生命周期只到2024-09-18&#xff0c;到期后就会强制每两小时重启。这是Windows强制升级系统的一种手段。 虽然公司里的台式电脑目前用不到&#xff0c;但是里面还保留许多旧项…

Why RAG is slower than LLM?

I used RAG with LLAMA3 for AI bot. I find RAG with chromadb is much slower than call LLM itself. Following the test result, with just one simple web page about 1000 words, it takes more than 2 seconds for retrieving: 我使用RAG&#xff08;可能是指某种特定的…

【大数据 复习】第8章 Hadoop架构再探讨

一、概念 1.Hadoop1.0的核心组件&#xff08;仅指MapReduce和HDFS&#xff0c;不包括Hadoop生态系统内的Pig、Hive、HBase等其他组件&#xff09;&#xff0c;主要存在以下不足&#xff1a; &#xff08;1&#xff09;抽象层次低&#xff0c;需人工编码 &#xff08;2&#xf…

md5在ida中的识别

ida中 识别md5 ,先右键转为hex 或者按h _DWORD *__fastcall MD5Init(_DWORD *result) {*result 0;result[1] 0;result[2] 1732584193;result[3] -271733879;result[4] -1732584194;result[5] 271733878;return result; }在ida中当然也可以使用搜索 search imdate-value …

xss.haozi.me靶场通关参考

url&#xff1a;https://xss.haozi.me/ 文章目录 0x000x010x020x030x040x050x060x070x080x090x0A0x0B0x0C00xD00xE00xF0x100x110x12 0x00 先看js代码&#xff0c;第一关给你热热手&#xff0c;没给你加过 payload&#xff1a; <script>alert(1)</script>0x01 这…

【大数据】—量化交易实战案例(基础策略)

声明&#xff1a;股市有风险&#xff0c;投资需谨慎&#xff01;本人没有系统学过金融知识&#xff0c;对股票有敬畏之心没有踏入其大门&#xff0c;所以只能写本文来模拟炒股。 量化交易&#xff0c;也被称为算法交易&#xff0c;是一种使用数学模型和计算机算法来分析市场数…

FlinkCDC pipeline模式 mysql-to-paimon.yaml

flinkcdc 需要引入&#xff1a; source端&#xff1a; flink-cdc-pipeline-connector-mysql-xxx.jar、mysql-connector-java-xxx.jar、 sink端&#xff1a; flink-cdc-pipeline-connector-paimon-xxx.jar flinkcdc官方提供connect包下载地址&#xff0c;pipeline模式提交作业和…

Python 函数注解,给函数贴上小标签

目录 什么是函数注解? 为什么使用函数注解? 如何编写函数注解? 实战演练 与类型提示(Type Hints)的关系 类型安全的运算器 什么是函数注解? 函数注解(Function Annotations)是Python 3中新增的一个特性,它允许为函数的参数和返回值指定类型。 这些注解不会改变…

Paimon Trino Presto的关系 分布式查询引擎

Paimon支持的引擎兼容性矩阵&#xff1a; Trino 是 Presto 同项目的不同版本&#xff0c;是原Faceboo Presto创始人团队核心开发和维护人员分离出来后开发和维护的分支&#xff0c;Trino基于Presto&#xff0c;目前 Trino 和 Presto 都仍在继续开发和维护。 参考&#xff1a;

win10 安装openssl并使用openssl创建自签名证书

win10创建自签名证书 下载安装配置openssl 下载地址&#xff1a; https://slproweb.com/download/Win64OpenSSL-3_3_1.exe https://slproweb.com/products/Win32OpenSSL.html 完成后安装&#xff0c;一路next&#xff0c;到达选位置的之后选择安装的位置&#xff0c;我这里选…

已成功见刊检索的国际学术会议论文海报展示(2)

【先投稿先送审】第四届计算机、物联网与控制工程国际学术会议&#xff08;CITCE 2024) 大会官网&#xff1a;www.citce.org 时间地点&#xff1a;2024年11月1-3日&#xff0c;中国-武汉 收录检索&#xff1a;EI Compendex&#xff0c;Scopus 主办单位&#xff1a;四川师范…

计算机组成原理 —— 存储系统(主存储器基本组成)

计算机组成原理 —— 存储系统&#xff08;主存储器基本组成&#xff09; 0和1的硬件表示整合结构寻址按字寻址和按字节寻址按字寻址按字节寻址区别总结 字寻址到字节寻址转化 我们今天来看一下主存储器的基本组成&#xff1a; 0和1的硬件表示 我们知道一个主存储器是由存储体…