嵌入式学习笔记DAY33(网络编程——TCP)

news2025/9/22 21:09:51

一、网络架构

C/S (client/server  客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务器返回的结果;服务器端是提供服务的程序,一般部署在性能较强的计算机上,负责处理数据存储、业务逻辑计算等,监听特定端口等待客户端请求,接收到请求后进行处理并返回结果。

B/S(browser/server  浏览器/服务器):基于浏览器和服务器,客户端通过通用的浏览器(如 Chrome、Firefox、等)来访问应用程序。服务器端包括 Web 服务器和数据库服务器等,负责处理业务逻辑、存储和管理数据。

P2P(peer to peer 点对点):是一种去中心化的网络架构,网络中的节点(计算机)地位对等,不存在专门的中心服务器。每个节点既可以作为客户端向其他节点请求服务或资源,也可以作为服务器为其他节点提供服务或资源。

二、C/S 和 B/S 对比:

C/S架构B/S架构
客户端需要安装专用的客户端软件无需安装客户端,直接通过浏览器访问
协议可自定义私有协议(如游戏使用二进制协议),灵活性高。基于HTTP/HTTPS 协议,需遵循 Web 标准(如 RESTful 接口)。
功能
资源客户端分担部分计算压力,服务器专注数据处理,适合高并发、大数据量场景。所有逻辑在服务器端执行,需应对大量 HTTP 请求,对服务器性能要求高。

三、C/S通信流程

                       

服务器端流程

  • 创建套接字(socket ()):调用 socket() 函数创建一个套接字,这是进行网络通信的基础。它会返回一个套接字描述符,后续操作将基于这个描述符进行。该函数确定通信的协议族(如 AF_INET 表示 IPv4)、套接字类型(如 SOCK_STREAM 表示 TCP 流套接字)和协议(通常为 0,由系统根据前两个参数自动选择合适协议)。
  • 绑定地址和端口(bind ()):使用 bind() 函数将创建的套接字与特定的 IP 地址和端口号绑定。这样服务器就能明确在哪个地址和端口上监听客户端的连接请求。需要提供套接字描述符、指向包含 IP 地址和端口号信息的结构体指针,以及该结构体的大小。
  • 监听连接(listen ()):调用 listen() 函数使服务器进入监听状态,它会为套接字创建一个等待连接的队列,参数包括套接字描述符和队列的最大长度。这一步告诉系统开始接受客户端的连接请求。
  • 接受连接(accept ()):accept() 函数会阻塞(暂停执行),直到有客户端发起连接请求。一旦有客户端连接,它会创建一个新的套接字描述符用于与该客户端进行通信,同时返回客户端的地址信息。原来监听的套接字仍然保持监听状态,继续接受其他客户端的连接。
  • 读取数据(read ()):服务器通过新创建的与客户端通信的套接字描述符,使用 read() 函数读取客户端发送过来的数据。read() 函数从套接字接收数据并存储到指定的缓冲区中,返回实际读取的字节数。
  • 处理请求:服务器对读取到的数据进行相应的处理,例如解析请求内容、查询数据库、进行业务逻辑计算等。
  • 写入数据(write ()):处理完请求后,服务器使用 write() 函数将处理结果(响应数据)通过套接字发送回客户端。write() 函数将缓冲区中的数据写入套接字,发送给客户端。
  • 再次读取数据(read ()):服务器可能再次调用 read() 函数,等待接收客户端后续可能发送的数据,比如新的请求或确认信息等。
  • 关闭连接(close ()):通信结束后,服务器调用 close() 函数关闭与客户端通信的套接字,释放相关资源。

客户端流程

  • 创建套接字(socket ()):与服务器端一样,客户端首先调用 socket() 函数创建一个套接字,用于后续的网络通信,返回套接字描述符。
  • 建立连接(connect ()):客户端使用 connect() 函数尝试与服务器建立连接。需要指定要连接的服务器的 IP 地址和端口号,以及套接字描述符。如果服务器处于监听状态并且接受连接,连接就会成功建立;否则可能会返回错误。
  • 写入数据(write ()):连接建立后,客户端调用 write() 函数向服务器发送请求数据,将请求内容写入套接字,发送给服务器。
  • 读取数据(read ()):客户端调用 read() 函数从套接字读取服务器返回的响应数据,将数据存储到指定的缓冲区中。
  • 关闭连接(close ()):客户端完成与服务器的通信后,调用 close() 函数关闭套接字,释放资源。

整体交互过程

  • 服务器端先完成初始化(创建套接字、绑定、监听),进入等待客户端连接的状态。
  • 客户端创建套接字并尝试连接服务器,连接成功后,客户端向服务器发送请求数据。
  • 服务器接收请求数据,处理后向客户端发送响应数据。
  • 客户端接收响应数据,双方通信结束后各自关闭套接字,释放资源。

 注意:pid(进程ID)只能在本机范围内发送,两台主机间无法直接发送

四、TCP通信的特点

  • 有链接:并非一上来就直接进行传输,要先通过网络结点进行直接或者间接的连接,然后通过创建一些函数,连接起来(这条链路在通信过程中一直建立着)。
  • TCP是一种可靠传输:
  1. 应答机制:在 TCP 通信里,每次接收方收到数据,都会给发送方发送一个应答报文(ACK) 。TCP 通过给每个数据段编号(序列号),接收方根据收到的数据段序列号,在应答报文中用确认序号告知发送方哪些数据已正确接收。例如发送方发送了编号为 1 - 1000 的字节数据,接收方若正确收到,就在应答报文中带上确认序号 1001(表示期望接收的下一个字节编号),告知发送方 1 - 1000 已正确接收 。
  2. 超时重传:发送方发送数据后,会设定一个超时时间,若在该时间内未收到接收方的 ACK 应答报文,不管是数据包丢失还是 ACK 确认应答丢失,发送方都认为数据传输失败,会重新发送数据 。比如网络拥堵导致数据包在传输途中滞留,超过超时时间仍未到达接收方,或者接收方发送的 ACK 在返回途中丢失,发送方都感知不到数据已被接收,就会触发超时重传 
  • 全双工:通信双方都具备独立的发送和接收通道,发送数据同时可接收数据。比如在网络通信中,网卡支持全双工,数据发送线和接收线各自独立工作 ;发送和接收操作瞬时同步进行。以电话通信为例,通话时双方说话和听到对方声音同步,语音信号在两个方向同时传输 。
  • 连续:TCP 将数据视为无边界的连续字节流,发送方按顺序逐字节传输,接收方按序列号重组为连续的数据流。
  • 有顺序:TCP 为每个字节数据分配唯一序列号,接收方按序列号重组数据,确保字节流顺序正确;接收方通过 ACK 报文告知发送方已收到的数据序号,发送方仅传输未确认的分组,避免乱序。(udp本身不保证)。

五、“三次握手四次挥手 ”

                               

 三次握手:

       三次握手其实就是指建立一个TCP连接时,需要客户端和服务器总共发送3个包。进行三次握手的主要作用就是为了确认双方的接收能力和发送能力是否正常、指定自己的初始化序列号为后面的可靠性传送做准备。实质上其实就是连接服务器指定端口,建立TCP连接,并同步连接双方的序列号和确认号,交换TCP窗口大小信息。

刚开始客户端处于 Closed 的状态,服务端处于 Listen 状态。


在socket编程中,客户端执行connect()时,将触发三次握手:

  • 第一次握手:客户端给服务端发一个 SYN 报文,并指明客户端的初始化序列号 ISN。此时客户端处于 SYN_SENT 状态 。首部的同步位SYN=1,初始序号seq=x,SYN=1的报文段不能携带数据,但要消耗掉一个序号。
  • 第二次握手:服务器收到客户端的 SYN 报文之后,会以自己的 SYN 报文作为应答,并且也是指定了自己的初始化序列号 ISN(s)。同时会把客户端的 ISN + 1 作为ACK 的值,表示自己已经收到了客户端的 SYN,此时服务器处于 SYN_RCVD 的状态。在确认报文段中SYN=1,ACK=1,确认号ack=x+1,初始序号seq=y。
  • 第三次握手:客户端收到 SYN 报文之后,会发送一个 ACK 报文,当然,也是一样把服务器的 ISN + 1 作为 ACK 的值,表示已经收到了服务端的 SYN 报文,此时客户端处于 ESTABLISHED 状态。服务器收到 ACK 报文之后,也处于 ESTABLISHED 状态,此时,双方已建立起了连接。确认报文段ACK=1,确认号ack=y+1,序号seq=x+1(初始为seq=x,第二个报文段所以要+1),ACK报文段可以携带数据,不携带数据则不消耗序号。

 

四次挥手: 

        建立一个连接需要三次握手,而终止一个连接要经过四次挥手(也有将四次挥手叫做四次握手的)。这由TCP的半关闭(half-close)造成的。所谓的半关闭,其实就是TCP提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力。

TCP 连接的拆除需要发送四个包,因此称为四次挥手(Four-way handshake),客户端或服务端均可主动发起挥手动作,

刚开始双方都处于ESTABLISHED 状态,假如是客户端先发起关闭请求。四次挥手的过程如下,在socket编程中,任何一方执行close()操作即可产生挥手操作:

  • 第一次挥手:客户端发送一个 FIN 报文,报文中会指定一个序列号。此时客户端处于 FIN_WAIT1 状态。

        即发出连接释放报文段(FIN=1,序号seq=u),并停止再发送数据,主动关闭TCP连            接,进入FIN_WAIT1(终止等待1)状态,等待服务端的确认。

  • 第二次挥手:服务端收到 FIN 之后,会发送 ACK 报文,且把客户端的序列号值 +1 作为 ACK 报文的序列号值,表明已经收到客户端的报文了,此时服务端处于 CLOSE_WAIT 状态。

       即服务端收到连接释放报文段后即发出确认报文段(ACK=1,确认号ack=u+1,序号             seq=v),服务端进入CLOSE_WAIT(关闭等待)状态,此时的TCP处于半关闭状态,         客户端到服务端的连接释放。客户端收到服务端的确认后,进入FIN_WAIT2(终止等待          2)状态,等待服务端发出的连接释放报文段。

  • 第三次挥手:如果服务端也想断开连接了,和客户端的第一次挥手一样,发给 FIN 报文,且指定一个序列号。此时服务端处于 LAST_ACK 的状态。

       即服务端没有要向客户端发出的数据,服务端发出连接释放报文段(FIN=1,ACK=1,         序号seq=w,确认号ack=u+1),服务端进入LAST_ACK(最后确认)状态,等待客户         端的确认。

  • 第四次挥手:客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答,且把服务端的序列号值 +1 作为自己 ACK 报文的序列号值,此时客户端处于 TIME_WAIT 状态。需要过一阵子以确保服务端收到自己的 ACK 报文之后才会进入 CLOSED 状态,服务端收到 ACK 报文之后,就处于关闭连接了,处于 CLOSED 状态。

       即客户端收到服务端的连接释放报文段后,对此发出确认报文段(ACK=1,seq=u+1,         ack=w+1),客户端进入TIME_WAIT(时间等待)状态。此时TCP未释放掉,需要经过

       时间等待计时器设置的时间2MSL后,客户端才进入CLOSED状态。

特性: tcp也叫流式套接字        

六、TCP服务端相关函数

socket()    创建套接字:

               

  • 参数
    • domain:协议族(如AF_INET表示 IPv4,AF_INET6表示 IPv6)。
    • type:套接字类型(如SOCK_STREAM表示 TCP 流式套接字,SOCK_DGRAM表示 UDP 数据报套接字)。
    • protocol:通常为 0,表示自动选择对应协议(如 TCP 对应IPPROTO_TCP)。
  • 返回值:成功返回套接字描述符(非负整数),失败返回-1并设置errno

bind()    绑定地址和端口 :

           

  • 参数
    • sockfdsocket()返回的套接字描述符。
    • addr:指向地址结构的指针(如struct sockaddr_in)。
    • addrlen:地址结构的长度(如sizeof(struct sockaddr_in))。
  • 返回值:成功返回0,失败返回-1

 listen()     监听连接:

            

  • 参数
    • sockfd:已绑定的套接字描述符。
    • backlog:未处理连接队列的最大长度(如5SOMAXCONN)。
  • 返回值:成功返回0,失败返回-1
  • 说明:将套接字从主动模式转为被动模式,等待客户端连接。

 accept()   接受客户端连接:

      

  • 参数
    • sockfd:监听套接字描述符(由listen()创建)。
    • addr:存储客户端地址的结构体指针(可为NULL)。
    • addrlen:地址结构体长度的指针(需初始化为结构体大小)。
  • 返回值:成功返回新的客户端套接字描述符,失败返回-1
  • 说明
    • 阻塞直到有客户端连接到达。
    • 返回的新套接字用于与客户端通信,原监听套接字继续监听

 connect()    连接服务器:

       

  • 参数
    • sockfd:客户端套接字描述符(由socket()创建)。
    • addr:服务器地址结构体指针。
    • addrlen:地址结构体长度。
  • 返回值:成功返回0,失败返回-1
  • 说明
    • 客户端调用此函数发起与服务器的连接(触发三次握手)。
    • 若连接失败(如服务器未监听),需重新调用connect()

 send()    发送数据:

         

  • 参数
    • sockfd:已连接的套接字描述符。
    • buf:待发送数据的缓冲区指针。
    • len:数据长度(字节)。
    • flags:通常为0,或设置特殊标志(如MSG_DONTWAIT表示非阻塞)。
  • 返回值:成功返回实际发送的字节数,失败返回-1
  • 说明
    • 数据可能未立即发送,而是存入发送缓冲区。
    • 返回值可能小于len(如网络拥塞),需循环发送剩余数据。

 

recv()    接收数据 :

          

  • 参数
    • sockfd:已连接的套接字描述符。
    • buf:存储接收数据的缓冲区指针。
    • len:缓冲区最大长度。
    • flags:通常为0,或设置特殊标志(如MSG_PEEK表示查看但不取出数据)。
  • 返回值
    • 成功返回实际接收的字节数。
    • 返回0表示对方已关闭连接(FIN 包)。
    • 返回-1表示出错(如连接断开)。
  • 说明
    • 若无数据且未关闭连接,recv()默认阻塞。
    • 需循环读取直至数据全部接收(尤其对于大文件)。

 close()    关闭套接字:

                  

  • 参数
    • fd:套接字描述符。
  • 返回值:成功返回0,失败返回-1
  • 说明
    • 关闭套接字并释放资源。
    • 触发 TCP 四次挥手断开连接(若为主动关闭方)。

七、代码实现 

  • 服务端
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h> /* See NOTES */
#include <time.h>
#include <unistd.h>
typedef struct sockaddr*(SA);
int main(int argc, char** argv)
{
  //监听套接字
  int listfd = socket(AF_INET, SOCK_STREAM, 0);
  if (-1 == listfd)
    {
      perror("socket");
      return 1;
    }
  // man 7 ip
  struct sockaddr_in ser, cli;
  bzero(&ser, sizeof(ser));
  bzero(&cli, sizeof(cli));
  ser.sin_family = AF_INET;
  ser.sin_port = htons(50000);
  ser.sin_addr.s_addr = inet_addr("127.0.0.1");
  int ret = bind(listfd, (SA)&ser, sizeof(ser));
  if (-1 == ret)
    {
      perror("bind");
      return 1;
    }
  // 三次握手的排队数  ,
  listen(listfd, 3);
  socklen_t len = sizeof(cli);
  //通信套接字
  int conn = accept(listfd, (SA)&cli, &len);
  if (-1 == conn)
    {
      perror("accept");
      return 1;
    }
  while (1)
    {
      char buf[256] = {0};
      // ret >0 实际收到的字节数
      //==0  表示对方断开
      // -1 出错。
      ret = recv(conn, buf, sizeof(buf), 0);
      if(ret<=0)
      {
        break;
      }
      printf("cli:%s\n",buf);
      time_t tm;
      time(&tm);
      struct tm * info = localtime(&tm);
      sprintf(buf,"%s %d:%d:%d\n",buf, info->tm_hour,info->tm_min,info->tm_sec);
      send(conn,buf,strlen(buf),0);
    }
  close(conn);
  close(listfd);

  // system("pause");
  return 0;
}
  • 客户端 
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h> /* See NOTES */
#include <time.h>
#include <unistd.h>
typedef struct sockaddr*(SA);
int main(int argc, char** argv)
{
  
  int conn = socket(AF_INET, SOCK_STREAM, 0);
  if (-1 == conn)
    {
      perror("socket");
      return 1;
    }
  // man 7 ip
  struct sockaddr_in ser, cli;
  bzero(&ser, sizeof(ser));
  bzero(&cli, sizeof(cli));
  ser.sin_family = AF_INET;
  ser.sin_port = htons(50000);
  ser.sin_addr.s_addr = inet_addr("127.0.0.1");
  int ret = connect(conn,(SA)&ser,sizeof(ser));
  if(-1 == ret)
  {
    perror("connect");
    return 1;
  }
  while (1)
    {
      char buf[256] = {0};
      strcpy(buf,"this is tcp test");
      send(conn,buf,strlen(buf),0);
      ret = recv(conn, buf, sizeof(buf), 0);
      if(ret<=0)
      {
        break;
      }
      printf("ser:%s",buf);
      fflush(stdout);
      sleep(1);
      
    }
  close(conn);
  

  // system("pause");
  return 0;
}

八、黏包问题 

1.什么是粘包问题?

答:粘包问题是指在TCP通信中,发送方发送的多个独立消息在接收方被合并成一个消息接收的现象。换句话说,发送方发送的多条消息在接收方被“粘”在一起,导致接收方无法直接区分消息的边界。

2.粘包问题成因?
  • TCP是面向流的协议,它将数据视为一个连续的字节流,不保留消息的边界。
  • 发送方发送的多个消息可能被合并到同一个TCP包中发送。
  • 接收方在读取数据时,无法直接知道哪些字节属于哪条消息。
3.粘包问题的影响?
  • 接收方无法正确解析消息,可能导致数据解析错误。
  • 系统的健壮性和可靠性降低,尤其是在需要严格消息边界的应用中。
4.如何解决?
  • 添加分隔符:在每条消息末尾添加特殊分隔符(如\n\r\n),接收方通过分隔符来解析消息。
  • 固定大小:每条消息的长度固定,接收方根据固定长度来解析消息。
  • 自定义协议

九、练习

客户端-服务端  传送文件

  • 服务端
#include <arpa/inet.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h> /* See NOTES */
#include <time.h>
#include <unistd.h>
typedef struct sockaddr*(SA);
typedef struct
{
  char filename[256];
  char buf[1024];
  int buf_len;
  int total_len;
} PACK;
int main(int argc, char** argv)
{
  //监听套接字
  int listfd = socket(AF_INET, SOCK_STREAM, 0);
  if (-1 == listfd)
    {
      perror("socket");
      return 1;
    }
  // man 7 ip
  struct sockaddr_in ser, cli;
  bzero(&ser, sizeof(ser));
  bzero(&cli, sizeof(cli));
  ser.sin_family = AF_INET;
  ser.sin_port = htons(50000);
  ser.sin_addr.s_addr = inet_addr("127.0.0.1");
  int ret = bind(listfd, (SA)&ser, sizeof(ser));
  if (-1 == ret)
    {
      perror("bind");
      return 1;
    }
  // 三次握手的排队数  ,
  listen(listfd, 3);
  socklen_t len = sizeof(cli);
  //通信套接字
  int conn = accept(listfd, (SA)&cli, &len);
  if (-1 == conn)
    {
      perror("accept");
      return 1;
    }
  int first_flag = 0;
  int fd = -1;
  int current_size = 0;
  int total_size = 0;
  while (1)
    {
      PACK pack;
      bzero(&pack, sizeof(pack));
      ret = recv(conn, &pack, sizeof(pack), 0);
      if (0 == first_flag)
        {
          first_flag = 1;
          fd = open(pack.filename, O_WRONLY | O_TRUNC | O_CREAT, 0666);
          if (-1 == fd)
            {
              perror("open");
              return 1;
            }
          total_size = pack.total_len;
        }
      if (0 == pack.buf_len)
        {
          break;
        }

      write(fd, pack.buf, pack.buf_len);
      current_size += pack.buf_len;
      printf("%d/%d\n", current_size, total_size);
      bzero(&pack, sizeof(pack));
      strcpy(pack.buf,"go on");
     // send(conn,&pack,sizeof(pack),0);
    }
  close(conn);
  close(listfd);
  close(fd);

  // system("pause");
  return 0;
}
  • 客户端
#include <arpa/inet.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h> /* See NOTES */
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

typedef struct sockaddr*(SA);
typedef struct
{
  char filename[256];
  char buf[1024];
  int buf_len;
  int total_len;
} PACK;
int main(int argc, char** argv)
{
  int conn = socket(AF_INET, SOCK_STREAM, 0);
  if (-1 == conn)
    {
      perror("socket");
      return 1;
    }
  // man 7 ip
  struct sockaddr_in ser, cli;
  bzero(&ser, sizeof(ser));
  bzero(&cli, sizeof(cli));
  ser.sin_family = AF_INET;
  ser.sin_port = htons(50000);
  ser.sin_addr.s_addr = inet_addr("127.0.0.1");
  int ret = connect(conn, (SA)&ser, sizeof(ser));
  if (-1 == ret)
    {
      perror("connect");
      return 1;
    }
  PACK pack;
  strcpy(pack.filename, "1.png");  // /home/linux/1.png
  struct stat st;
  ret = stat("/home/linux/1.png", &st);
  if (-1 == ret)
    {
      perror("stat");
      return 1;
    }
  pack.total_len = st.st_size;  // total size
  int fd = open("/home/linux/1.png", O_RDONLY);
  if (-1 == fd)
    {
      perror("open");
      return 1;
    }
  while (1)
    {
      pack.buf_len = read(fd, pack.buf, sizeof(pack.buf));
      send(conn, &pack, sizeof(pack), 0);
      if (pack.buf_len <= 0)
        {
          break;
        }
        bzero(&pack,sizeof(pack));
        //recv(conn,&pack,sizeof(pack),0);
        usleep(1000*10); //10ms
    }
  close(conn);
  close(fd);

  // system("pause");
  return 0;
}

 

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

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

相关文章

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖

在Vuzix M400 AR智能眼镜的助力下&#xff0c;卢森堡罗伯特舒曼医院&#xff08;the Robert Schuman Hospitals, HRS&#xff09;凭借在无菌制剂生产流程中引入增强现实技术&#xff08;AR&#xff09;创新项目&#xff0c;荣获了2024年6月7日由卢森堡医院药剂师协会&#xff0…

【VLNs篇】07:NavRL—在动态环境中学习安全飞行

项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战&#xff0c;克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…

R语言速释制剂QBD解决方案之三

本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…

基于Java+MySQL实现(GUI)客户管理系统

客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息&#xff0c;对客户进行统一管理&#xff0c;可以把所有客户信息录入系统&#xff0c;进行维护和统计功能。可通过文件的方式保存相关录入数据&#xff0c;对…

springboot整合VUE之在线教育管理系统简介

可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生&#xff0c;小白用户&#xff0c;想学习知识的 有点基础&#xff0c;想要通过项…

算法:模拟

1.替换所有的问号 1576. 替换所有的问号 - 力扣&#xff08;LeetCode&#xff09; ​遍历字符串​&#xff1a;通过外层循环逐一检查每个字符。​遇到 ? 时处理​&#xff1a; 内层循环遍历小写字母&#xff08;a 到 z&#xff09;。对每个字母检查是否满足&#xff1a; ​与…

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf

FTP 客服管理系统 实现kefu123登录&#xff0c;不允许匿名访问&#xff0c;kefu只能访问/data/kefu目录&#xff0c;不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…

Linux 内存管理实战精讲:核心原理与面试常考点全解析

Linux 内存管理实战精讲&#xff1a;核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用&#xff0c;还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…

Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战

说明&#xff1a;这是一个机器学习实战项目&#xff08;附带数据代码文档&#xff09;&#xff0c;如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下&#xff0c;风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…

【分享】推荐一些办公小工具

1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由&#xff1a;大部分的转换软件需要收费&#xff0c;要么功能不齐全&#xff0c;而开会员又用不了几次浪费钱&#xff0c;借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…

推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)

推荐 github 项目:GeminiImageApp(图片生成方向&#xff0c;可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…

面向无人机海岸带生态系统监测的语义分割基准数据集

描述&#xff1a;海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而&#xff0c;目前该领域仍面临一个挑战&#xff0c;即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…

使用Spring AI和MCP协议构建图片搜索服务

目录 使用Spring AI和MCP协议构建图片搜索服务 引言 技术栈概览 项目架构设计 架构图 服务端开发 1. 创建Spring Boot项目 2. 实现图片搜索工具 3. 配置传输模式 Stdio模式&#xff08;本地调用&#xff09; SSE模式&#xff08;远程调用&#xff09; 4. 注册工具提…

C++:多态机制详解

目录 一. 多态的概念 1.静态多态&#xff08;编译时多态&#xff09; 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1&#xff09;.协变 2&#xff09;.析构函数的重写 5.override 和 final关键字 1&#…

人机融合智能 | “人智交互”跨学科新领域

本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…

七、数据库的完整性

七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式

今天是关于AI如何在教学中增强学生的学习体验&#xff0c;我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育&#xff0c;这并非炒作&#xff0c;而是已经发生的巨大变革。教育机构和教育者不能忽视它&#xff0c;试图简单地禁止学生使…

安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲

文章目录 前言第一部分&#xff1a;体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分&#xff1a;体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的

修改bug思路&#xff1a; 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑&#xff1a;async await 等接口返回后再开始下一个接口查询 ________________________________________________________…

排序算法总结(C++)

目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指&#xff1a;同样大小的样本 **&#xff08;同样大小的数据&#xff09;**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…