linux下编程epoll实现将GPS定位信息上报到服务器

时间:2022-09-29 12:26:32

操作系统:CentOS

开发板:fl2440

开发模块:A7(GPS/GPRS),RT3070(无线网卡)

****************************************************************************************************************************************************************************************

前言:本博文实现的功能是:fl2440开发板运行客户端程序,将GPS的定位信息通过串口读取出来,然后将定位信息发送到服务器上。

(当然服务器上跑的是自己编写的服务器端程序)这个就有点类似共享单车上面装的GPS定位系统,然后公司就可以根据其共享单车的地理位置信息进行定位管理。不过我这个只是一个小程序,功能比较单一,只是初步学习网络socket编程,不敢妄加定论,如有不对的地方,谢请指正。

对于初次学习网络socket编程的人来说,了解epoll的原理是必要的,网上有大量文章介绍epoll的原理以及它的用法,所以本文不做过多的赘述,只是简单的分析。

1.什么是epoll?

我的理解是:epoll是linux网络编程多路复用中的一种新的事件触发机制,相比于select,epoll最大的好处在于它不会随着监听fd数目的增长而降低效率,不过实现的原理大致相同,都是通过监听客户端套接字fd,也就是如果有多个客户端程序连接服务器,然后服务器端就将监听到的套接字fd存放在一个集合里,如果发现客户端套接字fd发生可读,可写,以及错误事件时,服务器端就进行相应的处理。不过epoll与poll及select不同的是,epoll采用的是基于事件的就绪通知方式。

在select/poll中,进程只有在调用一定的函数后,内核才对所有监视的文件描述符进行扫描,而epoll事先通过epoll_ctl()来注册一个文件描述符,一旦基于某个文件描述符就绪时,内核会采用类似callback的回调机制,迅速激活这个文件描述符,当进程调用epoll_wait()时便得到通知。

epoll实现一共就三个函数:

(1). int epoll_create(int size);

创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大。这个参数不同于select()中的第一个参数,给出最大监听的fd+1的值。需要注意的是,当创建好epoll句柄后,它就会占用一个fd值,在linux下如果查看/proc/进程id/fd/,是能够看到这个fd的,所以在使用完epoll后,必须调用close()关闭,否则可能导致fd被耗尽。

(2).int epoll_ctl(int epfd, int op, int fd, struct
epoll_event *event);

epoll的事件注册函数,它不同与select()是在监听事件时告诉内核要监听什么类型的事件,而是在这里先注册要监听的事件类型。第一个参数是epoll_create()的返回值,第二个参数表示动作,用三个宏来表示:

EPOLL_CTL_ADD:注册新的fd到epfd中;

EPOLL_CTL_MOD:修改已经注册的fd的监听事件;

EPOLL_CTL_DEL:从epfd中删除一个fd;

第三个参数是需要监听的fd,第四个参数是告诉内核需要监听什么事.

struct epoll_event结构如下:

typedef union epoll_data {

void *ptr;

int fd;

__uint32_t u32;

__uint64_t u64;

} epoll_data_t;

struct epoll_event {

__uint32_t events; /* Epoll
events */

epoll_data_t data; /* User
data variable */

};

events可以是以下几个宏的集合:

EPOLLIN :表示对应的文件描述符可以读(包括对端SOCKET正常关闭);

EPOLLOUT:表示对应的文件描述符可以写;

EPOLLPRI:表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);

EPOLLERR:表示对应的文件描述符发生错误;

EPOLLHUP:表示对应的文件描述符被挂断;

EPOLLET: 将EPOLL设为边缘触发(Edge
Triggered)模式,这是相对于水平触发(Level
Triggered)来说的。

EPOLLONESHOT:只监听一次事件,当监听完这次事件之后,如果还需要继续监听这个socket的话,需要再次把这个socket加入到EPOLL队列里.

(3).int epoll_wait(int epfd, struct
epoll_event * events, int maxevents, int timeout);

等待事件的产生,类似于select()调用。参数events用来从内核得到事件的集合,maxevents告之内核这个events有多大,这个
maxevents的值不能大于创建epoll_create()时的size,参数timeout是超时时间(毫秒,0会立即返回,-1将不确定,也有说法说是永久阻塞)。该函数返回需要处理的事件数目,如返回0表示已超时。

2.如何使用epoll?

通过在包含一个头文件#include <sys/epoll.h> 以及几个简单的API将可以大大的提高你的网络服务器的支持人数。

首先通过create_epoll(int maxfds)来创建一个epoll的句柄,其中maxfds为你epoll所支持的最大句柄数。

这个函数会返回一个新的epoll句柄,之后的所有操作将通过这个句柄来进行操作。在用完之后,记得用close()来关闭这个创建出来的epoll句柄。

之后在你的网络主循环里面,每一帧的调用epoll_wait(int epfd, epoll_event
events, int max
events, int timeout)来查询所有的网络接口,看哪一个可以读

哪一个可以写了。基本的语法为:

nfds = epoll_wait(kdpfd, events, maxevents, -1);

其中kdpfd为用epoll_create创建之后的句柄,events是一个epoll_event*的指针,当epoll_wait这个函数操作成功之后,epoll_events里面将储存所有的读写

件。max_events是当前需要监听的所有socket句柄数。最后一个timeout是 epoll_wait的超时,为0的时候表示马上返回,为-1的时候表示一直等下去,直到有事件

围,为任意正整数的时候表示等这么长的时间,如果一直没有事件,则返回。一般如果网络主循环是单独的线程的话,可以用-1来等,这样可以保证一些效率,如果是和主

辑在同一个线程的话,则可以用0来保证主循环的效率。epoll_wait返回之后应该是一个循环,遍利所有的事件。

epoll简单分析之后,进入正题:

一.前期准备
  (1).在测试程序之前我们得保证我们的开发板能够成功上网,当然我是直接在我的开发板上插上一块无线网卡,通过相应的命令连接路由器上网,前提是开发板要使能相应的驱动,具体怎么使能,及相应的操作命令是啥?我的这篇博客有总结,可以参考一下:点击打开链接
  (2).由于我的A7模块是同时具有GPS与GPRS功能,所以打开GPS需要通过AT+GPS=1命令开启,使用microcom命令监听串口,查看输入AT命令之后,串口是否会有OK回显,具体操作我的博客有总结,参考这篇博客:点击打开链接
 
(3).硬件连线我使用的是两根USB转串口线,一根连接开发板,通过串口方式来连接开发板,一根连接GPS模块,当然也可以通过ssh代理远程登陆开发板,不过开发板能够与PC通信(两种方法:1.网线有线连接使其开发板与PC相连。2.插上无线网卡,开发板能上网,无线连接开发板)
二.网络编程
客户端代码:
client.c:
  1. /*********************************************************************************
  2. * Copyright: (C) 2017 zoulei
  3. * All rights reserved.
  4. *
  5. * Filename: client.c
  6. * Description: This file
  7. *
  8. * Version: 1.0.0(2017年06月21日)
  9. * Author: zoulei <zoulei121@gmail.com>
  10. * ChangeLog: 1, Release initial version on "2017年06月21日 19时17分40秒"
  11. *
  12. ********************************************************************************/
  13. #include <sys/types.h>
  14. #include <sys/stat.h>
  15. #include <fcntl.h>
  16. #include <stdio.h>
  17. #include <string.h>
  18. #include <sys/types.h>
  19. #include <sys/socket.h>
  20. #include <arpa/inet.h>
  21. #include <unistd.h>
  22. #include <netinet/in.h>
  23. #include <errno.h>
  24. #include <stdlib.h>
  25. #include "gps.h"
  26. #define GPS_LEN 1024
  27. #define PORT 9997
  28. int set_serial(int fd,int nSpeed, int nBits, char nEvent, int nStop);
  29.  
  30. int main (int argc, char **argv)
  31. {
  32. int fd=0;
  33. int n=0;
  34. int i=0;
  35. int sockfd;
  36. int rec_len;
  37. GPRMC gprmc;
  38. char sendbuf[1024] ;
  39. char buff[GPS_LEN];
  40. char *str=NULL;
  41. char *dev_name="/dev/ttyUSB0";
  42. struct sockaddr_in servaddr;
  43. /*打开"/dev/ttyUSB0"设备*/
  44. if((fd=open(dev_name,O_RDWR|O_NOCTTY|O_NDELAY))<0)
  45. {
  46. perror("Can't Open the ttyUSB0 Serial Port");
  47. return -1;
  48. }
  49. set_serial( fd,9600,8,'N',1);//串口配置函数
  50. /* 判断命令端输入的参数是否正确 */
  51. if( argc != 2)
  52. {
  53. printf("usage: ./client <ipaddress>\n");
  54. exit(0);
  55. }
  56. /* 创建客户端套接字--IPv4协议,面向连接通信,TCP协议*/
  57. if(( sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
  58. {
  59. perror("socket");
  60. exit(0);
  61. }
  62. /* 初始化 */
  63. memset(&servaddr,0,sizeof(servaddr)); /* 数据初始化-清零 */
  64. servaddr.sin_family = AF_INET; /* 设置IPv4通信 */
  65. servaddr.sin_port = htons(PORT);/* 设置服务器端口号 */
  66. /* IP地址转换函数,将点分十进制转换为二进制 */
  67. if( inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
  68. {
  69. printf("inet_pton error for %s\n",argv[1]);
  70. exit(0);
  71. }
  72. /* 将套接字绑定到服务器的网络地址上*/
  73. if( connect( sockfd, (struct sockaddr *)&servaddr, sizeof(servaddr))<0)
  74. {
  75. perror("connected failed");
  76. exit(0);
  77. }
  78. while(1)
  79. {
  80. sleep(2);
  81. /*读串口设备获取GPS定位信息*/
  82. if((n=read(fd,buff,sizeof(buff)))<0)
  83. {
  84. perror("read error");
  85. return -1;
  86. }
  87. /*将GPS定位信息发送到服务器端*/
  88. if(send(sockfd,buff,strlen(buff),0)< 0 )
  89. {
  90. printf("send the gps datas error:%s(errno: %d)\n", strerror(errno), errno);
  91. exit(0);
  92. }
  93.  
  94. printf("read buff:%s\n",buff);
  95.  
  96. }
  97. close(sockfd);
  98. close(fd);
  99. return 0;
  100. }
串口配置程序及gps.h文件,在我上篇博客:点击打开链接
makefile:
  1. CC=/opt/buildroot-2012.08/arm920t/usr/bin/arm-linux-gcc
  2.  
  3. objs=uart1.o client.o
  4. srcs=uart1.c client.c
  5.  
  6. client_test: $(objs)
  7. $(CC) -o client_test $(objs)
  8. @make clean
  9.  
  10. client.o: $(srcs) gps.h
  11. $(CC) -c $(srcs)
  12.  
  13. uart1.o: uart1.c
  14. $(CC) -c uart1.c
  15.  
  16. clean:
  17. rm *.o

make编译之后生成client_test可执行文件,然后将其烧录到开发板。赋予可执行,可读,可写权限。

服务器代码:
sev.c:
  1. /*********************************************************************************
  2. * Copyright: (C) 2017 zoulei.
  3. * All rights reserved.
  4. *
  5. * Filename: sev.c
  6. * Description: This file
  7. *
  8. * Version: 1.0.0(06/22/2017)
  9. * Author: zoulei <zoulei121@gmail.com>
  10. * ChangeLog: 1, Release initial version on "06/22/2017 11:25:16 AM"
  11. *
  12. ********************************************************************************/
  13. #include <string.h>
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16. #include <unistd.h>
  17. #include <sys/select.h>
  18. #include <sys/time.h>
  19. #include <sys/socket.h>
  20. #include <netinet/in.h>
  21. #include <arpa/inet.h>
  22. #include <sys/epoll.h>
  23. #include <errno.h>
  24. #define OPEN_MAX 100
  25.  
  26. typedef unsigned int UINT;
  27. typedef int BYTE;
  28.  
  29. typedef struct __gprmc__
  30. {
  31. UINT time;/* gps定位时间 */
  32. char pos_state;/*gps状态位*/
  33. float latitude;/*纬度 */
  34. float longitude;/* 经度 */
  35. float speed; /* 速度 */
  36. float direction;/*航向 */
  37. UINT date; /*日期 */
  38. float declination; /* 磁偏角 */
  39. char dd;
  40. char mode;/* GPS模式位 */
  41.  
  42. }GPRMC;
  43.  
  44. int gps_analyse (char *buff,GPRMC *gps_data)
  45. {
  46. char *ptr=NULL;
  47. if(gps_data==NULL)
  48. {
  49. return -1;
  50. }
  51. if(strlen(buff)<10)
  52. {
  53. return -1;
  54. }
  55. if(NULL==(ptr=strstr(buff,"$GPRMC")))
  56. {
  57. return -1;
  58. }
  59. sscanf(ptr,"$GPRMC,%d.000,%c,%f,N,%f,E,%f,%f,%d,,,%c*",&(gps_data->time),&(gps_data->pos_state),&(gps_data->latitude),&(gps_data->longitude),&(gps_data->speed),&(gps_data->direction),&(gps_data->date),&(gps_data->mode));
  60. return 0;
  61. }
  62.  
  63. int print_gps (GPRMC *gps_data)
  64. {
  65. printf(" \n");
  66. printf(" \n");
  67. printf("===========================================================\n");
  68. printf("== 全球GPS定位导航模块 ==\n");
  69. printf("== Author:zoulei ==\n");
  70. printf("== Email:zoulei121@gmail.com ==\n");
  71. printf("== Platform:fl2440 ==\n");
  72. printf("===========================================================\n");
  73. printf(" \n");
  74. printf("===========================================================\n");
  75. printf("== GPS state bit : %c [A:有效状态 V:无效状态] \n",gps_data->pos_state);
  76. printf("== GPS mode bit : %c [A:自主定位 D:差分定位] \n", gps_data->mode);
  77. printf("== Date : 20%02d-%02d-%02d \n",gps_data->date%100,(gps_data->date%10000)/100,gps_data->date/10000);
  78. printf("== Time : %02d:%02d:%02d \n",(gps_data->time/10000+8)%24,(gps_data->time%10000)/100,gps_data->time%100);
  79. printf("== 纬度 : 北纬:%d度%d分%d秒 \n", ((int)gps_data->latitude) / 100, (int)(gps_data->latitude - ((int)gps_data->latitude / 100 * 100)), (int)(((gps_data->latitude - ((int)gps_data->latitude / 100 * 100)) - ((int)gps_data->latitude - ((int)gps_data->latitude / 100 * 100))) * 60.0));
  80. printf("== 经度 : 东经:%d度%d分%d秒 \n", ((int)gps_data->longitude) / 100, (int)(gps_data->longitude - ((int)gps_data->longitude / 100 * 100)), (int)(((gps_data->longitude - ((int)gps_data->longitude / 100 * 100)) - ((int)gps_data->longitude - ((int)gps_data->longitude / 100 * 100))) * 60.0));
  81. printf("== 速度 : %.3f m/s \n",gps_data->speed);
  82. printf("== \n");
  83. printf("============================================================\n");
  84.  
  85. return 0;
  86. }
  87. int main(int argc, char *argv[])
  88. { int max = 0 ;
  89. int i = 0 ;
  90. int len = 0 ;
  91. int sockfd ;
  92. int epfd ;
  93. int connfd ;
  94. int ret ;
  95. int fd[OPEN_MAX];
  96. char buff[512];
  97. GPRMC gprmc;
  98. struct epoll_event event; // 告诉内核要监听什么事件
  99. struct epoll_event wait_event; //内核监听完的结果
  100. struct sockaddr_in server_addr;
  101.  
  102. /* AF_INET 表示采用TCP/IP协议族 SOCK_STREAM 表示采用TCP协议 */
  103. if(( sockfd = socket(AF_INET, SOCK_STREAM, 0))<0)
  104. {
  105. perror("creat socket error");
  106. return -1;
  107. }
  108. memset(&server_addr,0,sizeof(server_addr));
  109. server_addr.sin_family = AF_INET;
  110. server_addr.sin_port = htons(9997);
  111. server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  112. /* 将socket绑定到某个IP和端口(IP标识主机,端口标识通信进程) */
  113. if(( bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)))<0)
  114. {
  115. perror("bind error");
  116. return -1;
  117. }
  118. /* 将socket设置为监听模式,10表示等待连接队列的最大长度 */
  119. if( listen(sockfd, 10) < 0)
  120. {
  121. perror("listen error");
  122. return -1;
  123. }
  124.  
  125. memset(fd,-1, sizeof(fd));
  126. fd[0] = sockfd;
  127. epfd = epoll_create(10); // 创建一个 epoll 的句柄,参数要大于 0, 不然没有太大意义
  128. if( -1 == epfd )
  129. {
  130. perror ("epoll_create error");
  131. return -1;
  132. }
  133.  
  134. event.data.fd = sockfd; //监听套接字
  135. event.events = EPOLLIN; // 表示对应的文件描述符可以读
  136. /*事件注册函数,将监听套接字描述符 sockfd 加入监听事件 */
  137. if(( ret = epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &event)) == -1)
  138. {
  139. perror("epoll_ctl");
  140. return -1;
  141. }
  142.  
  143. while(1)
  144. {
  145. /* 监视并等待多个文件描述符的属性变化(是否可读)
  146. 没有属性变化,这个函数会阻塞,直到有变化才往下执行,这里没有设置超时.*/
  147. ret = epoll_wait(epfd, &wait_event, max+1, -1);
  148. /*监测sockfd(监听套接字)是否存在连接 */
  149. if(( sockfd == wait_event.data.fd ) && ( EPOLLIN == wait_event.events & EPOLLIN ))
  150. {
  151. struct sockaddr_in cli_addr;
  152. int clilen = sizeof(cli_addr);
  153. /* 从tcp完成连接中提取客户端*/
  154. if(( connfd = accept(sockfd, (struct sockaddr *)&cli_addr, &clilen)) < 0)
  155. {
  156. perror("accept faild");
  157. return -1;
  158. }
  159. /* 将提取到的connfd放入fd数组中,以便下面轮询客户端套接字 */
  160. for(i=1; i<OPEN_MAX; i++)
  161. {
  162. if(fd[i] < 0)
  163. {
  164. fd[i] = connfd;
  165. event.data.fd = connfd; //监听套接字
  166. event.events = EPOLLIN; // 表示对应的文件描述符可以读
  167.  
  168. /* 事件注册函数,将监听套接字描述符 connfd 加入监听事件 */
  169. ret = epoll_ctl(epfd, EPOLL_CTL_ADD, connfd, &event);
  170. if(-1 == ret)
  171. {
  172. perror("epoll_ctl");
  173. return -1;
  174. }
  175. break;
  176. }
  177. }
  178. /* max更新 */
  179. if(i > max)
  180. max = i;
  181. /* 如果没有就绪的描述符,就继续epoll监测,否则继续向下看*/
  182. if(--ret <= 0)
  183. continue;
  184.  
  185. }
  186. for(i=1; i<=max; i++)
  187. {
  188. if(fd[i] < 0)
  189. continue;
  190.  
  191. if(( fd[i] == wait_event.data.fd ) && ( EPOLLIN == wait_event.events & (EPOLLIN|EPOLLERR) ))
  192. {
  193. /*接受客户端数据 */
  194. if((len = recv(fd[i], buff, sizeof(buff), 0)) < 0)
  195. {
  196. if(errno == ECONNRESET)//tcp连接超时、RST
  197. {
  198. close(fd[i]);
  199. fd[i] = -1;
  200. }
  201. else
  202. perror("read error:");
  203. }
  204. else if(len == 0)//客户端关闭连接
  205. {
  206. close(fd[i]);
  207. fd[i] = -1;
  208. }
  209. else //正常接收到客户端的数据
  210. buff[len]='\0';
  211. printf("receive the data:%s \n",buff);
  212. memset(&gprmc, 0 , sizeof(gprmc));
  213. gps_analyse(buff,&gprmc);
  214. print_gps(&gprmc);
  215. /*所有的就绪描述符处理完了,就退出当前的for循环,继续epoll监测 */
  216. if(--ret <= 0)
  217. break;
  218. }
  219. }
  220. }
  221. close(sockfd);
  222. close(epfd);
  223. return 0;
  224. }

测试结果:

客户端(开发板):
linux下编程epoll实现将GPS定位信息上报到服务器
服务器端:

linux下编程epoll实现将GPS定位信息上报到服务器

linux下编程epoll实现将GPS定位信息上报到服务器的更多相关文章

  1. Linux下编程学习一

    本篇主要记录一些在学习LINUX下编程时,, C和C++语言的一些基础的常识, 一. 函数指针 void MyFun(int x); 函数声明 void (*FunP)(int ); 函数指针声明 下 ...

  2. Linux下 高性能、易用、免费的ASP&period;NET服务器

    Linux下 高性能.易用.免费的ASP.NET服务器 http://www.jexus.org/#

  3. linux下保存下位机输出的串口信息为文件

    linux下保存下位机输出的串口信息为文件 1.stty -F /dev/ttyUSB0 raw (转换成raw模式) 2.stty -F /dev/ttyUSB0 speed 115200 (设置波 ...

  4. 在linux下有没有什么软件可以连接windows上的MSSQL SERVER

    在linux下有没有什么软件可以连接windows上的MSSQL SERVER GUI的http://dbeaver.jkiss.org/ http://bbs.csdn.net/topics/391 ...

  5. Linux下如何通过命令检查网卡是否插上网线

    How To:Linux下如何通过命令检查网卡是否插上网线   主要工具为ethtool来检查,主要关注的字段为"Link detected",注意如下的输出,其中em4实际物理上 ...

  6. Linux下使用fstatfs&sol;statfs查询系统相关信息

    Linux下使用fstatfs/statfs查询系统相关信息 1.   功能 #include < sys/statfs.h > int statfs(const char *path, ...

  7. Linux 下编程

    关于Linux 下的C语言编译命令和编程要点! https://www.cnblogs.com/wfwenchao/p/3985153.html?utm_source=tuicool&utm_ ...

  8. Linux下编程获取本地IP地址的常见方法

    转载于:http://blog.csdn.net/k346k346/article/details/48231933 在进行linux网络编程时,经常用到本机IP地址.本文罗列一下常见方法,以备不时之 ...

  9. linux 下如何查看和踢除正在登陆的其它用户 &equals;&equals;&gt&semi;Linux下用于查看系统当前登录用户信息的4种方法

    在linux系统中用pkill命令踢出在线登录用户 由于linux服务器允许多用户登录,公司很多人知道密码,工作造成一定的障碍 所以需要有时踢出指定的用户 1/#who   查出当前有那些终端登录(用 ...

随机推荐

  1. vs2015 企业版 专业版 密钥

    亲测可用 专业版:HMGNV-WCYXV-X7G9W-YCX63-B98R2企业版:HM6NR-QXX7C-DFW2Y-8B82K-WTYJV

  2. eclipse 查看变量或方法在什么地方被调用的快捷键

    选中方法名,点鼠标右键,菜单里有个”打开调用层次结构 ( Open Call Hierarchy )“,选中或者按下快捷键Ctrl+Alt+H,就在下面栏目里能看到调用的树形结构了. 或者: 1.双击 ...

  3. apache高负载性能调优

    先阅读apache配置优化建议如下,再对相关参数进行调整,观察服务器状况.Apache配置优化建议:进入/usr/local/apache2/conf/extra 目录下Apache优化,经过上述操作 ...

  4. h5新增标签兼容性

    <address> 标签定义文档或文章的作者/拥有者的联系信息.  兼容所有浏览器 <area> 标签定义图像映射中的区域(注:图像映射指得是带有可点击区域的图像).兼容所有浏 ...

  5. Android开发之旅:android架构

    本篇将站在*的高度——架构,来看android.我开篇就说了,这个系列适合0基础的人且我也是从0开始按照这个步骤来 学的,谈架构是不是有点螳臂挡车,自不量力呢?我觉得其实不然,如果一开始就对整个an ...

  6. docker install for centos7

    CentOS Docker runs on CentOS 7.X. An installation on other binary compatible EL7 distributions such ...

  7. AutoTile 自动拼接(一) 学习与实践

    恩,大家好,这两天江苏冷空气袭击,下了今年 第一场第二场雪. 不过今天我要说的 ,和 上面的 屁关系都没有. 今天要说的是 2d无缝自动拼接.大家有没有玩过  RPG Maker VX Ace. 类似 ...

  8. Asp&period;Net Core基于JWT认证的数据接口网关Demo

    近日,应一位朋友的邀请写了个Asp.Net Core基于JWT认证的数据接口网关Demo.朋友自己开了个公司,接到的一个升级项目,客户要求用Aps.Net Core做数据网关服务且基于JWT认证实现对 ...

  9. 资深程序员整理出来的Python面试题

    转载链接:https://www.cnblogs.com/fcxwz/p/9225791.html

  10. spring boot集成ehcache 2&period;x 用于hibernate二级缓存

    https://www.jianshu.com/p/87b2c309b776 本文将介绍如何在spring boot中集成ehcache作为hibernate的二级缓存.各个框架版本如下 spring ...