c语言实现http协议服务器端和客户端

本代码参考https://blog.csdn.net/ymxyld/article/details/124812731思路,并添加了客户端代码。
代码各个函数注释写的非常详细,仅供学习参考,有任何疑问请留言。

服务器端

代码流程:
请添加图片描述

#define SERVER_PORT 8080 //设置端口号
#include<stdio.h>
#include<string.h>
#include<iostream>
#include <winsock2.h>
#include <ws2tcpip.h> 
#include<string.h>
#ifdef _WIN32
#include <Windows.h>
// Windows 文件操作相关代码
#else
#include <sys/stat.h>
// Unix/Linux 文件操作相关代码
#endif
#pragma comment(lib, "Ws2_32.lib")
struct client_mes {//客户端请求信息结构体char IP[20];	//客户ip地址int PORT;	//客户端口号char method[10];//请求方法char url[1024];	 //请求urlchar version[10];//协议及版本信息
}c_mes;
struct kay_and_value {//每一个键值对结构体char key[10];char value[100];
};
struct url_mes {char path[100];//请求路径//采用结构体数组来存储键值对struct kay_and_value k_v[10];int k_v_len;//实际键值对个数
}u_mes;
//定义http响应行全局变量
char u200[] = "HTTP/1.0 200 OK\r\n";
char u400[] = "HTTP/1.0 400 BAD REQUEST\r\n";
char u404[] = "HTTP/1.0 404 NOT FOUND\r\n";
char u500[] = "HTTP/1.0 500 INTERNAL SERVER ERROR\r\n";
char u501[] = "HTTP/1.0 501 METHOD NOT IMPLEMENTED\r\n";
int main() {void do_http_request(char buf[1024]);//对缓冲区接受到的客户请求信息进行解析int do_http_resolve(char url[1024], int clnt_sock);//对客户端请求进行响应void do_http_url_process(char url[1024]);//对客户端的url进行解析// 初始化键值对结构体数组for (int k = 0; k < 10; k++) {strcpy_s(u_mes.k_v[k].key, "");strcpy_s(u_mes.k_v[k].value, "");}memset(&c_mes, 0, sizeof(c_mes));//将结构体里面的数据清零memset(&u_mes, 0, sizeof(url_mes));// 初始化 Winsock 库WSADATA wsaData;int result = WSAStartup(MAKEWORD(2, 2), &wsaData);if (result != 0) {fprintf(stderr, "WSAStartup failed with error code: %d\n", result);return 1;}/*创建TCP套接字(通过IPv4族进行面向连接的通信)返回值是新创建套接字的文件描述符,调用成功返回一个非负整数,如果调用失败,返回-1第一个参数:地址族			AF_INET表示IPv4地址族第二个参数:套接字类型		通过TCP连接传输第三个参数:传输协议		0默认情况,根据上面两个参数自动选择*/int serv_sock;if ((serv_sock = socket(AF_INET, SOCK_STREAM, 0)) == 0) {perror("socket failed");exit(EXIT_FAILURE);}/*sockaddr_in这是一个存储IPv4地址信息的结构体struct sockaddr_in {short            sin_family;   // 地址族 (AF_INET)unsigned short   sin_port;     // 端口号struct in_addr   sin_addr;     // IPv4 地址char             sin_zero[8];  // 填充 0,保持与 sockaddr 结构体大小的兼容性};*/struct sockaddr_in serv_addr;/*将结构体里面的数据清零,后续再次赋值*/memset(&serv_addr, 0, sizeof(serv_addr));/*指定地址族为IPv4*/serv_addr.sin_family = AF_INET;/*设置了服务器的IP地址INADDR_ANY 是一个特殊的常量,它表示服务器将接受来自任何网络接口的连接请求htonl()函数用于将主机字节序转换成网络字节序,确保在不同架构的计算机上数据的正确的传输主机字节序:大端或者小端网络字节序:默认为大端*/serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);/*设置了服务器的端口号,SERVER_PORT为代码顶部设置的宏*/serv_addr.sin_port = htons(SERVER_PORT);/*绑定将一个套接字与特定的ip地址和端口号关联起来,使服务器能够在该地址上监听来自客户端的请求第一个参数:服务器套接字的文件描述符,通过此文件描述符对服务器套接字进行操作第二个参数:&serv_addr为要绑定到套接字的结构体的指针,由于bind()函数要求的参数类型,所以进行类型转换第三个参数:指定了serv_addr结构体的大小*/int valbind;if ((valbind = bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr))) < 0) {perror("bind failed");fprintf(stderr, "Bind failed with error code: %d\n", WSAGetLastError());exit(EXIT_FAILURE);}//进入监听状态,等待用户发起请求int vallisten;if ((vallisten = listen(serv_sock, 3)) < 0) {perror("listen failed");exit(EXIT_FAILURE);}printf("等待客户端连接...\n");printf("——————————————————————————\n");//接收客户端请求/*定义一个结构体,用于存储客户端的地址信息,包括IP地址和端口号*/struct sockaddr_in clnt_addr;/*定义变量clnt_addr_size用来存储结构体clnt_addr的大小socklen_t 被设计用来表示套接字地址长度的类型遇到问题:若无#include <ws2tcpip.h> socklen_t 会报错*/socklen_t clnt_addr_size = sizeof(clnt_addr);/*accept()函数用于接受客户端的请求,并创建一个新的套接字用于与客户端通信第一个参数:服务器套接字的文件描述符第二个参数:函数调用成功后,客户端的信息将保存在结构体clnt_addr中第三个参数:指定了clnt_addr结构体的大小函数调用成功后返回一个新的文件描述符clnt_sock,代表与客户端通信的套接字*/int clnt_sock;if ((clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size)) < 0) {perror("accept failed");exit(EXIT_FAILURE);}printf("接收客户端请求成功\n");char client_ip[64];char buf[1024] = {0};/*打印客户端ip地址和端口号inet_ntop()函数用于将网络字节序的ip地址转换为可读的字符串格式,它被用来将客户端的 IP 地址从 clnt_addr.sin_addr.s_addr 转换为一个字符串,并将结果存储在 client_ip 数组中ntohs()用来将网络字节序的端口号转换为主机字节序的端口号*/printf("ip地址:%s\t port:%d\n",inet_ntop(AF_INET, &clnt_addr.sin_addr.s_addr, client_ip, sizeof(client_ip)),ntohs(clnt_addr.sin_port));//设置发出请求的客户端的ip地址和端口号strcat_s(c_mes.IP,inet_ntop(AF_INET, &clnt_addr.sin_addr.s_addr, client_ip, sizeof(client_ip)));c_mes.PORT=ntohs(clnt_addr.sin_port);/*读取客户端请求用套接字从clnt_sock中接收数据,并将数据存储到缓冲区buf中,最多接受1024字节的数据如果调用成功返回读取的字节数,否则返回-1*/int valread;if ((valread = recv(clnt_sock, buf, 1024, 0)) < 0) {perror("read failed");exit(EXIT_FAILURE);}printf("——————————————————————————\n");printf("读取数据成功\n");printf("客户端请求为:\n%s\n", buf);//解析客户端请求do_http_request(buf);//检验解析成果printf("——————————————————————————\n");printf("客户端信息解析成功\n");printf("客户端信息解析如下:\nip地址:%s\n端口号:%d\n请求方法:%s  长度:%d\n请求url:%s  长度:%d\n请求协议和方法:%s  长度:%d\n",c_mes.IP, c_mes.PORT, c_mes.method,strlen(c_mes.method), c_mes.url, strlen(c_mes.url), c_mes.version, strlen(c_mes.version));printf("——————————————————————————\n");//实现http响应printf("对客户端请求进行响应\n");do_http_resolve(c_mes.url,clnt_sock);/*发送响应给客户端向已连接的套接字clnt_sock(即客户端)发送数据发送成功返回成功发送的字节数,发送失败返回-1*///关闭套接字closesocket(clnt_sock);//关闭套接字closesocket(serv_sock);// 释放 Winsock 资源WSACleanup();return 0;
}
//对客户端请求进行解析
void do_http_request(char buf[1024]) {//对缓冲区接受到的客户请求信息进行解析int i=0;int j = 0;//获取方法j = 0;while (buf[i]!=' ') {c_mes.method[j++] = buf[i++];}i++;//获取urlif (buf[i] == '/'&&buf[i+1]==' ') {//无urlint j = 0;c_mes.url[j++] = '/';i = i + 1;}else {//有url  i++;//跳过/j = 0;while (buf[i] != ' ') {c_mes.url[j++] = buf[i++];}}i++;//获取协议及版本信息j = 0;while (buf[i] != '\r') {c_mes.version[j++] = buf[i++];}}
//对客户端进行url数据解析并反应(重写get方法)
int do_http_resolve(char url[1024],int clnt_sock) {void not_found(int clnt_sock);void unimplemented(int clnt_sock);int do_http_response(int clnt_sock, const char* path);void do_http_url_process(char url[1024]);//对客户端的url进行解析printf("进入响应函数\n");//判断http请求是get方法if ((strcmp(c_mes.method,"GET"))==0) {printf("客户请求是get方法\n");//对url中的路径和参数进行解析//计算ip地址和端口号的长度int lens = strlen(c_mes.IP) + 4;lens = lens + 1;//冒号长度//http://192.168.10.124:8080/index.html?name=123&psd=1234//解析客户端路径和键值对参数do_http_url_process(url);//输出处理后的数据for (int i = 0; i < u_mes.k_v_len; i++) {printf("客户端path:%s\t键值对数组u_mes[%d].key=%s,u_mes[%d].value=%s\n", u_mes.path, i, u_mes.k_v[i].key, i, u_mes.k_v[i].value);}char paths[20] = {};int i = 0;paths[0] = '.'; paths[1] = '/';while (u_mes.path[i] != '\0') {//printf("u_mes.path[i]=%c,i=%d\n",u_mes.path[i],i);paths[i + 2] = u_mes.path[i];i++;}paths[i + 2] = '\0';printf("拼接后的文件目录地址paths=%s\n", paths);	/*判断文件信息int stat(const char *pathname, struct stat *buf);第一个参数为文件路径名,第二个参数是一个关于文件信息的结构体struct stat {dev_t     st_dev;         // 文件的设备编号ino_t     st_ino;         // 文件的 inode 编号mode_t    st_mode;        // 文件的类型和权限nlink_t   st_nlink;       // 连接数uid_t     st_uid;         // 文件所有者的用户 IDgid_t     st_gid;         // 文件所有者的组 IDdev_t     st_rdev;        // 如果是特殊文件,设备编号off_t     st_size;        // 文件大小(以字节为单位)blksize_t st_blksize;     // 文件系统 I/O 缓冲区大小blkcnt_t  st_blocks;      // 分配的块数time_t    st_atime;       // 最后一次访问时间time_t    st_mtime;       // 最后一次修改时间time_t    st_ctime;       // 最后一次更改时间}*/struct stat filebuf;memset(&filebuf, 0, sizeof(struct stat));if (stat(paths, &filebuf)==-1) {//获取文件信息失败printf("stat %s find fail\n",paths);not_found(clnt_sock);//无法获取文件信息}else {//获取文件信息成功,发送响应//拼接相对文件路径printf("获取文件信息成功\n");			//正常入口do_http_response(clnt_sock,paths);}}else {printf("不是get方法,暂时无法响应!\n");unimplemented(clnt_sock);//服务器不支持的请求方法}return 1;
}
void do_http_url_process(char url[1024]) {int i = 0;int j = 0;//提取pathwhile (url[i] != '?'&& url[i] != '\0') {u_mes.path[j++] = url[i++];}u_mes.path[i] = '\0';//加上结束标志i++;j = 0;int temp = 0;//用来标识是否出现等号int k = 0;//控制键值对数组下标while (url[i] != '\0') {//对键值对进行赋值if (url[i] != '&') {//具体每队键值对的赋值if (url[i] == '=') {temp = 1;//切换到value的赋值j = 0;i++;//跳过=}else if (url[i] != '=') {if (temp == 0) {//对key赋值u_mes.k_v[k].key[j++] = url[i++];}else if (temp == 1) {//对value赋值u_mes.k_v[k].value[j++] = url[i++];}}}else {k++;i++;j = 0;temp = 0;}}u_mes.k_v_len = k;//记录实际参数个数
}
//对客户端请求进行具体响应 char*path和char path[100]等价
int do_http_response(int clnt_sock,const char *path) {//传入请求网页和具体参数	//发送头部int send_message(int clnt_sock, FILE * resource, const char* header);//发送主体printf("进入do_http_response函数\tpath=%s\n",path);//确定http响应状态行char* header = u200;//声明一个文件指针并将其初始化为nullerrno_t err;FILE *resource = NULL;//尝试打开文件,成功后返回文件指针,后续可通过文件指针来操作这个文件err = fopen_s(&resource,path, "r");if (resource == NULL) {printf("找不到请求资源%s\n", path);header = u404;return 0;}if (strcmp(path, "./error.html") == 0) {header = u404;}else if (strcmp(path, "./inner.html") == 0) {header = u500;}else if (strcmp(path, "./unimplemented.html") == 0) {header = u501;}else if (strcmp(path, "./bad_request.html") == 0) {header = u400;}//发送头部及主体内容send_message(clnt_sock, resource, header);printf("——————————————————————————\n");printf("客户端返回数据成功!\n");//关闭文件描述符fclose(resource);
}
//定义函数来实现状态的响应
void not_found(int clnt_sock) {do_http_response(clnt_sock,"./error.html");
}
void inner_error(int clnt_sock) {do_http_response(clnt_sock, "./inner.html");
}
void unimplemented(int clnt_sock) {do_http_response(clnt_sock, "./unimplemented.html");
}
void bad_request(int clnt_sock) {do_http_response(clnt_sock, "./bad_request.html");
}//发送头部及主体内容
int send_message(int clnt_sock, FILE* resource, const char* header) {printf("进入send_headers函数\n");char buff[100] = { 0 };//存放主体信息char mess[1024] = { 0 };char clent_mes[2048] = { 0 };struct stat st;int fileId = _fileno(resource);//获取文件描述符相关的文件标识符if (fstat(fileId,&st)==-1) {printf("inner error\n");inner_error(clnt_sock);return -1;}char buf[1024] = { 0 };//存放头部信息char temp[64];//写入状态行strcat_s(buf,header);//消息报头strcat_s(buf,"Server:Martin Server\r\n");strcat_s(buf,"Content_Type:text/htmll\r\n");strcat_s(buf,"Connection:Close\r\n");/*sprintf()则将数据输出到指定的字符串中*/sprintf_s(temp, "Contene-Length:%d\r\n\r\n", st.st_size);strcat_s(buf,temp);printf("头部信息为:\n%s\n",buf);while (fgets(buff, sizeof(buff), resource) != NULL) {//处理文件内容size_t buf_len = strlen(buff);if (buf_len > 0) {size_t remain_space = sizeof(mess) - sizeof(int) * (strlen(mess) / sizeof(int));if (remain_space >= buf_len) {memcpy((char*)mess + strlen((char*)mess), buff, buf_len);}else {printf("mess中没有剩余空间!\n");break;}}}strcpy_s(clent_mes, buf);strcat_s(clent_mes, mess);printf("发送给客户的信息是:%s\n", clent_mes);//发送给客户端if (send(clnt_sock, clent_mes,strlen(clent_mes),0)<0) {printf("send failed\n");return -1;}return 0;
}

客户端代码

客户端流程:
在这里插入图片描述

#include<stdio.h>
#include <winsock2.h>
#include <ws2tcpip.h> 
#pragma comment(lib, "Ws2_32.lib")
int main() {// 初始化 WinsockWSADATA wsaData;if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {printf("WSAStartup failed\n");return 1;}//创建客户端对象int socket_fd;if ((socket_fd = socket(AF_INET, SOCK_STREAM, 0))== 0){perror("socket failed");exit(EXIT_FAILURE);}struct sockaddr_in sock_addr;	//定义结构体sock_addr.sin_family = AF_INET; //设置为ipv4sock_addr.sin_port = htons(8080); //设置端口号为8080//设置网络ipif (inet_pton(AF_INET, "192.168.10.124", &sock_addr.sin_addr) <= 0) {perror("inet_pton() failed");exit(EXIT_FAILURE);}//连接服务器if ((connect(socket_fd, (struct sockaddr*)&sock_addr, sizeof(sock_addr)) < 0)) {perror("connect failed");exit(EXIT_FAILURE);}printf("对服务器发起连接\n");//准备http请求char http[] = "GET /index.html HTTP/1.1\r\nHost: 192.168.10.124:8080\r\nConnection: keep-alive\r\nCache-Control: max-age=0\r\nUpgrade-Insecure-Requests: 1\r\nMozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36\r\ntext/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9\r\nAccept-Encoding: gzip, deflate\r\nAccept-Language: zh-CN,zh;q=0.9\r\n\r\n";/*char http[1024];memset(&http, 0, sizeof(http));//你不能直接将字符串赋值给字符数组,需要使用strcpy函数strcpy_s(http, "hello server!");*///发送http请求协议if ((send(socket_fd, http, strlen(http),0)) < 0) {perror("send failed");exit(EXIT_FAILURE);}printf("发送成功\n");char buf[2048] = {0};//读取http回应if (recv(socket_fd, buf, sizeof(buf),0)<0) {perror("read failed");exit(EXIT_FAILURE);}printf("读取数据成功\n");printf("服务器端给出的回应是%s\n", buf);//关闭套接字closesocket(socket_fd);WSACleanup();return 0;
}

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

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

相关文章

docker 使用官方镜像搭建 PHP 环境

一、所需环境&#xff1a; 1、PHP&#xff1a;7.4.33-fpm 的版本 2、Nginx&#xff1a;1.25.1 的版本 3、MySQL&#xff1a; 5.7 的版本 4、Redis&#xff1a;7.0 的版本 1.1、拉取官方的镜像 docker pull php:7.4.33-fpm docker pull nginx:1.25.1 docker pull mysql:5.7 do…

ChatGPT发不出消息?GPT发不出消息怎么办?

前言 今天发现&#xff0c;很多人的ChatGPT无法发送信息&#xff0c;我就登陆看一下自己的GPT的情况&#xff0c;结果还真的无法发送消息&#xff0c;ChatGPT 无法发送消息&#xff0c;但是能查看历史的对话&#xff0c;不过通过下面的方法解决了。 第一时间先打开官方的网站&a…

项目实战之跨语言调用api——结合语言优点解决实际问题

前情提要 在一个项目开发中需要后端解析并分析数据得出结果报告&#xff0c;一开始用的Java后端&#xff0c;后面一堆Json数据解析的实在头疼&#xff0c;于是捡起老胶水&#xff1a;Python 辅助开发作为后端的核心算法部分服务接口 Java&#xff1a;SpringBoot 结合 RestTemp…

【Linux】Linux——Centos7安装

目录 虚拟机安装【空壳子】安装VMware Workstation新建虚拟机硬件兼容性(直接下一步)稍后安装操作系统客户及操作系统选择Linux&#xff0c;版本Centos764位给虚拟机命名&#xff0c;并选择安装位置处理器配置&#xff08;默认即可&#xff0c;不够用后面可以调&#xff09;虚拟…

Nexus Repository的搭建

一、前言 Nexus Repository用于管理maven的jar包。java开发程序员每天都在使用&#xff0c;但是自己搭建或者管理的就很少。除非你是系统架构师。因为这一套东西&#xff0c;基本是搭建一次就不需要人来搭建了&#xff0c;日后打开界面维护的机会也很少&#xff0c;我们只需要…

一元函数微分学——刷题(26

目录 1.题目&#xff1a;2.解题思路和步骤&#xff1a;3.总结&#xff1a;小结&#xff1a; 1.题目&#xff1a; 2.解题思路和步骤&#xff1a; 归纳求解&#xff0c;把指数写成负数就比较容易看出来规律 3.总结&#xff1a; 归纳求解&#xff0c;把指数写成负数就比较容易…

资产管理系统有哪些(一体化资产管理平台推荐)

企业资产管理系统是一种关键的工具&#xff0c;旨在帮助企业有效地管理和追踪其资产。 该系统利用计算机系统和相关软件&#xff0c;通过信息化、智能化的方式&#xff0c;对资产进行全面的可视化管理&#xff0c;从而提高管理效率、降低运营成本&#xff0c;并确保资产的安全…

Hadoop配置日志的聚集——jobhistory不显示任务问题

问题&#xff1a; 一开始job history是正常的&#xff0c;配置了日志的聚集以后不管做什么任务都不显示任务&#xff0c;hdfs是正常运行&#xff0c;而且根据配置步骤都重启过了。 下面先po出日志聚集的操作步骤&#xff0c;再讲问题 1.配置yarn-site.xml cd $HADOOP_HOME/e…

经典排序算法之快速排序|c++代码实现|什么是快速排序|如何代码实现快速排序

引言 排序算法c实现系列第6弹——快速排序 文章末尾还有本菜已实现的其他排序算法文章的链接。不过&#xff0c;排序算法这个系列还没更完&#xff0c;争取本周末搞完&#xff01;之后还会有堆排序、桶排序等的代码实现&#xff0c;感兴趣的佳人可以点个赞&收藏&#xff…

Igraph入门指南 4

二、图的创建 图分有向图和无向图&#xff0c;所以图的创建有各自的实现方式。 1、手工创建图&#xff1a; 1-1 通过文本创建&#xff1a;graph_from_literal 通过每项提供两个顶点名&#xff08;或ID号&#xff09;作为一条边的格式&#xff0c;手动创建图&#xff0c;顶点…

【敬伟ps教程】文字处理工具

文章目录 文字工具使用方式文字图层文字工具选项字符面板段落面板文字工具使用方式 文字工具(快捷键T),包含横排和直排两种类型 创建文本两种类型:点式文本、段落文本 创建文字方式 1、在画面上单击,出现文字光标,可输入文字,然后需要在工具栏中点击“√”或者 Ctrl+…

数学建模-动态规划(美赛运用)

动态规划模型的要素是对问题解决的抽象&#xff0c;其可分为&#xff1a; 阶段。指对问题进行解决的自然划分。例如&#xff1a;在最短线路问题中&#xff0c;每进行走一步的决策就是一个阶段。 状态。指一个阶段开始时的自然状况。例如&#xff1a;在最短线路问题中&#xff…

docker 运行异构镜像

概述 关于docker镜像在不同的cpu架构下运行报错的解决办法&#xff0c;作者踩坑验证&#xff0c;在此分享经验 某次工作遇到需要银行内部部署docker镜像&#xff0c;由于行内已经开始走信创的路线&#xff0c;使用鲲鹏系统&#xff0c;arm架构&#xff0c;记过就遇到了standa…

ComfyUI-Flowty-TripoSR

这是一个自定义节点&#xff0c;可让您直接从ComfyUI使用TripoSR。TripoSR 是由 Tripo AI 和 Stability AI 合作开发的最先进的开源模型&#xff0c;用于从单个图像快速前馈 3D 重建。&#xff08;TL;DR 它从图像创建 3d 模型。这篇文章主要介绍了将TripoSR作为ComfyUI节点的配…

华容道问题求解_详细设计(四)之查找算法2_BFS

&#xff08;续上篇&#xff09; 利用BFS查找&#xff0c;会找到最短路径&#xff08;没有权重的图&#xff09;&#xff0c;这个道理比较简单&#xff0c;这是由于寻找路径的方法都是从起点或者接近起点的位置开始的。查找过程如果画出图来&#xff0c;类似于一圈圈的放大&…

Leetcode每日一题】 二维前缀和 - 矩阵区域和(难度⭐⭐)(31)

1. 题目解析 题目链接&#xff1a;1314. 矩阵区域和 题目乍一看很晦涩难懂&#xff0c;又是大于等于又是k的 仔细分析&#xff0c;题目所说的意思就是以[i,j]为中心&#xff0c;求上下左右向外拓展k个单位的矩阵和&#xff0c;放在名为answer的矩阵中&#xff0c;最后返回ans…

Session登陆实践

Session登陆实践 Session登录是一种常见的Web应用程序身份验证和状态管理机制。当用户成功登录到应用程序时&#xff0c;服务器会为其创建一个会话&#xff08;session&#xff09;&#xff0c;并在会话中存储有关用户的信息。这样&#xff0c;用户在与应用程序交互的整个会话…

设计模式 代理模式

代理模式主要使用了 Java 的多态&#xff0c;主要是接口 干活的是被代理类&#xff0c;代理类主要是接活&#xff0c; 你让我干活&#xff0c;好&#xff0c;我交给幕后的类去干&#xff0c;你满意就成&#xff0c;那怎么知道被代理类能不能干呢&#xff1f; 同根就成&#xff…

档案室管理人员有哪些岗位

档案室管理人员的岗位可以分为以下几类&#xff1a; 1. 档案文书管理岗位&#xff1a;负责档案文书的管理、整理、归档和借阅工作&#xff0c;包括档案资料的分类、编目、装订、存储等。 2. 档案数字化管理岗位&#xff1a;负责将纸质档案数字化&#xff0c;进行扫描、转换、存…

mac下终端命令提示补全

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 mac下终端命令提示补全 前言Zsh-autosuggestions原理解析&#xff1a;智能提示的工作方式1. 命令历史分析&#xff1a;2. 智能提示生成&#xff1a;3. 用户交互和选择&#xff1a;4. 配置和个性化&…