1、项目介绍

  HTTP协议是应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。协议的详细内容,前面一篇HTTP协议详解已经详细介绍了,这里不再赘述。

  项目总体描述:HTTP支持客户端/服务器模式,终端用户可通过浏览器或网络爬虫与服务器建立连接,所以首先需要自主实现服务器Server端,具体由头文件httpd.h、main函数文件httpd.c、模块功能函数文件httpd.c组成,主要实现客户端与服务器通过socket建立通信机制。首先由用户主动发起一个到服务器上指定端口(默认端口为80)的请求,服务器则在那个端口监听客户端发送过来的请求。服务器一行一行读取请求,通过请求信息判断用户请求资源的方法和路径,若方法和路径没有问题,则方法和路径通过CGI模式或非CGI向用户提供不同的HTML网页信息。处理完请求客户端向用户发送响应,包括状态行如:“HTTP/1.1 200 OK”、响应报头、消息正文,消息体即为服务器上的资源。

实现功能一:静态首页展示(图片、文字文字信息);

实现二:支持表单提交,可以借助浏览器或telnet工具使用GET、POST方法访问服务器,实现数据的简单计算功能;

实现三:引入MYSQL,用户可通过页面表单进行数据操作,服务器拿到客户提交的数据后,会把数据存入到远端数据库,客户端也可请求查看数据库信息。

整个项目的文件目录:

目录:
conf:配置文件,存放需要绑定的服务器的ip和port ;
log:shell的日志文件以及http错误处理的日志文件 ;
sql_client:mysql部分的API及CGI实现;
thread_pool:线程池实现;
wwwroot:web服务器工作的根目录,包含各种资源页面(例如默认的index.html页面,差错处理的404页面),以及执行cgi的可执行程序。下面还有一个 cgi-bin目录,是存放CGI脚本的地方。这些脚本使WWW服务器和浏览器能运行外部程序,而无需启动另一个程序。它是运行在Web服务器上的一个程序,并由来自于浏览者的输入触发。

整个项目的框架图:

2、各模块功能介绍

头文件httpd.h,包含该项目代码所使用的全部函数的头文件以及宏定义,和函数声明;

  1. #ifndef _HTTPD_
  2. #define _HTTPD_
  3.  
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <sys/socket.h>
  7. #include <sys/types.h>
  8. #include <netinet/in.h>
  9. #include <arpa/inet.h>
  10. #include <fcntl.h>
  11. #include <errno.h>
  12. #include <string.h>
  13. #include <unistd.h>
  14. #include <sys/stat.h>
  15. #include <sys/wait.h>
  16.  
  17. #define SUCCESS 0
  18. #define NOTICE 1
  19. #define WARNING 2
  20. #define ERROR 3
  21. #define FATAL 4
  22.  
  23. #define SIZE 1024
  24.  
  25. void print_log(char *msg, int level); //打印日志
  26. int startup(const char *ip, int port); //创建监听套接字
  27. void *handler_request(void *arg); //处理请求
  28.  
  29. #endif

main函数文件main.c实现主要通信逻辑,通过socket建立连接的,监听和接受套接字,然后创建新线程处理请求。

  1. #include <pthread.h>
  2. #include "httpd.h"
  3.  
  4. static void usage(const char *proc)
  5. {
  6. printf("Usage: %s [local_ip] [local_port]\n", proc);
  7. }
  8.  
  9. int main(int argc, char *argv[])
  10. {
  11. if(argc != ){
  12. usage(argv[]);
  13. return ;
  14. }
  15.  
  16. int listen_sock = startup(argv[], atoi(argv[]));//监听套接字
  17. //daemon(0, 0);
  18. while(){
  19. struct sockaddr_in client;
  20. socklen_t len = sizeof(client);
  21. int new_sock = accept(listen_sock, (struct sockaddr*)&client, &len);//接收套接字
  22. if(new_sock < ){
  23. print_log(strerror(errno), NOTICE);
  24. continue;
  25. }
  26.  
  27. printf("get client [%s:%d]\n",\
  28. inet_ntoa(client.sin_addr),\
  29. ntohs(client.sin_port)); //链接到一个客户端之后打印其IP及端口号
  30.  
  31. pthread_t id;
  32. int ret = pthread_create(&id, NULL,\ //创建新线程
  33. handler_request, (void *)new_sock);
  34. if(ret != ){
  35. print_log(strerror(errno), WARNING);
  36. close(new_sock);
  37. }else{
  38. pthread_detach(id); //将子线程分离,该线程结束后会自动释放所有资源
  39. }
  40. }
  41. close(listen_sock);
  42. return ;
  43. }

模块功能函数在httpd.c文件

  1. #include "httpd.h"
  2.  
  3. void print_log(char *msg, int level)
  4. {
  5. #ifdef _STDOUT_
  6. const char * const level_msg[]={
  7. "SUCCESS",
  8. "NOTICE",
  9. "WARNING",
  10. "ERROR",
  11. "FATAL",
  12. };
  13. printf("[%s][%s]\n", msg, level_msg[level%]);
  14. #endif
  15. }
  16.  
  17. int startup(const char *ip, int port) //
  18. {
  19. int sock = socket(AF_INET, SOCK_STREAM, ); //创建套接字
  20. if(sock < ){
  21. print_log(strerror(errno), FATAL); //strerror()将错误码转换为对应的错误码描述
  22. exit();
  23. }
  24.  
  25. int opt = ;
  26. setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); //将该套接字设置为地址复用状态,若服务器挂掉可实现立即重启
  27.  
  28. struct sockaddr_in local;
  29. local.sin_family = AF_INET;
  30. local.sin_port = htons(port); //端口号转换
  31. local.sin_addr.s_addr = inet_addr(ip); //ip转换
  32. if(bind(sock, (struct sockaddr*)&local, sizeof(local)) < ){ //绑定
  33. print_log(strerror(errno), FATAL);
  34. exit();
  35. }
  36. if(listen(sock, ) < ){ //监听
  37. print_log(strerror(errno), FATAL);
  38. exit();
  39. }
  40. return sock;
  41. }
  42.  
  43. //ret > 1, line != '\0'读成功,正常字符; ret=1&line='\n' ret<=0&&line=='\0'
  44. static int get_line(int sock, char line[], int size) //得到一行请求内容
  45. {
  46. // read 1 char , one by one
  47. char c = '\0';
  48. int len = ;
  49. while( c != '\n' && len < size-){
  50. int r = recv(sock, &c, , );
  51. if(r > ){
  52. if(c == '\r'){
  53. //窥探,只把缓冲区的东西拿出来看看
  54. int ret = recv(sock, &c, , MSG_PEEK);
  55. if(ret > ){
  56. if(c == '\n'){
  57. recv(sock, &c, , );
  58. }else{
  59. c = '\n';
  60. }
  61. }
  62. }// \r->\n \r\n -> \n
  63. line[len++] = c;
  64. }else{
  65. c = '\n';
  66. }
  67. }
  68. line[len]='\0';
  69. return len;
  70. }
  71. //不同平台下\n、\r、\n+\r,意义不同,这里将其统一成\n
  72.  
  73. static void echo_string(int sock)
  74. {}
  75.  
  76. static int echo_www(int sock, char *path, int size)
  77. {
  78. int fd = open(path, O_RDONLY);
  79. if(fd < ){
  80. echo_string(sock);
  81. print_log(strerror(errno), FATAL);
  82. return ;
  83. }
  84.  
  85. const char *echo_line="HTTP/1.0 200 OK\r\n"; //状态行
  86. send(sock, echo_line, strlen(echo_line), );
  87. const char *null_line="\r\n";
  88. send(sock, null_line, strlen(null_line), ); //空行
  89.  
  90. if(sendfile(sock, fd, NULL, size) < ){//在内核区实现两个文件描述符的拷贝,不用定义临时变量,省略两次数据拷贝,效率提高
  91. echo_string(sock);
  92. print_log(strerror(errno), FATAL);
  93. return ;
  94. }
  95.  
  96. close(fd);
  97. return ;
  98. }
  99.  
  100. static void drop_header(int sock)
  101. {
  102. char line[];
  103. int ret = -;
  104. do{
  105. ret = get_line(sock, line, sizeof(line));
  106. }while(ret> && strcmp(line, "\n"));
  107. }
  108.  
  109. static int exe_cgi(int sock, char *method, \
  110. char *path, char *query_string)
  111. {
  112. int content_len = -;
  113. char method_env[SIZE/];
  114. char query_string_env[SIZE];
  115. char content_len_env[SIZE/];
  116.  
  117. if( strcasecmp(method, "GET") == ){//忽略大小写的字符比较,此处为判断请求资源的方法是否为GET方法
  118. drop_header(sock);//如果是GET方法则已从URL中知道用户请求资源所传参数
  119. }else{//POST
  120. char line[];
  121. int ret = -;
  122. do{
  123. ret = get_line(sock, line, sizeof(line));
  124. if(ret > &&\
  125. strncasecmp(line,"Content-Length: ", )== ){
  126. content_len = atoi(&line[]);//消息正文字长描述
  127. }
  128. }while(ret> && strcmp(line, "\n"));
  129. if(content_len == -){
  130. echo_string(sock);
  131. return ;
  132. }
  133. }
  134. const char *echo_line="HTTP/1.0 200 OK\r\n"; //状态行
  135. send(sock, echo_line, strlen(echo_line), );
  136. const char *type="Content-Type:text/html;charset=ISO-8859-1\r\n";
  137. send(sock, type, strlen(type), );
  138. const char *null_line="\r\n";
  139. send(sock, null_line, strlen(null_line), ); //空行
  140.  
  141. printf("query_string: %s\n", query_string);
  142. //path-> exe
  143. int input[];
  144. int output[];
  145. if(pipe(input) < || pipe(output) < ){
  146. echo_string(sock);
  147. return ;
  148. }
  149. pid_t id = fork();
  150. if(id < ){
  151. echo_string(sock);
  152. return ;
  153. }else if(id == ){//child
  154. close(input[]);
  155. close(output[]);
  156. sprintf(method_env, "METHOD=%s", method);
  157. putenv(method_env);
  158.  
  159. if(strcasecmp(method, "GET") == ){
  160. sprintf(query_string_env, "QUERY_STRING=%s", query_string);
  161. putenv(query_string_env);
  162. }else{ // POST
  163. sprintf(content_len_env, "CONTENT_LENGTH=%d", content_len);
  164. putenv(content_len_env);
  165. }
  166. dup2(input[], );//重定向
  167. dup2(output[], );
  168. execl(path, path, NULL); //第一个参数:路径及名字,第二个参数:怎么执行,传什么参数
  169. printf("execl error!\n");
  170. exit();
  171. }else{
  172. close(input[]);
  173. close(output[]);
  174.  
  175. int i = ;
  176. char c = '\0';
  177. if(strcasecmp(method, "POST") == ){
  178. for( ; i < content_len; i++ ){
  179. recv(sock, &c, , );
  180. write(input[], &c, );
  181. }
  182. }
  183.  
  184. c='\0';
  185. while(read(output[], &c, ) > ){
  186. send(sock, &c, , );
  187. }
  188.  
  189. waitpid(id, NULL, );
  190. close(input[]);
  191. close(output[]);
  192. }
  193. }
  194.  
  195. //thread
  196. void *handler_request(void *arg)
  197. {
  198. int sock = (int)arg;
  199. #ifdef _DEBUG_ //测试代码
  200. char line[];
  201. do{
  202. int ret = get_line(sock, line, sizeof(line));
  203. if(ret > ){
  204. printf("%s", line);
  205. }else{
  206. printf("request ...... done!\n");
  207. break;
  208. }
  209. }while();
  210. #else
  211. int ret = ;
  212. char buf[SIZE]; //读到的请求内容
  213. char method[SIZE/]; //请求资源的方法
  214. char url[SIZE]; //统一资源标识符
  215. char path[SIZE]; //有效资源路径
  216. int i, j;
  217. int cgi = ; //设置CGI模式
  218. char *query_string = NULL; //请求资源字符串(URL中问号后的内容)
  219. if(get_line(sock, buf, sizeof(buf)) <= ){ //获得一行请求内容
  220. echo_string(sock);
  221. ret = ;
  222. goto end;
  223. }
  224. i=;//method ->index
  225. j=;//buf -> index
  226.  
  227. while( !isspace(buf[j]) &&\
  228. j < sizeof(buf) &&\
  229. i < sizeof(method)-){
  230. method[i]=buf[j];
  231. i++, j++;
  232. }
  233. method[i] = ;
  234. if(strcasecmp(method, "GET") &&\ //忽略大小写的字符比较,此处为判断请求资源的方法是否为GET方法或POST方法
  235. strcasecmp(method, "POST") ){
  236. echo_string(sock);
  237. ret = ;
  238. goto end;
  239. }
  240. if(strcasecmp(method, "POST") == ){ //如果使用POST方法必定是CGI模式
  241. cgi = ;
  242. }
  243. //buf -> "GET / http/1.0"
  244. while(isspace(buf[j]) && j < sizeof(buf)){
  245. j++;
  246. }
  247. i=;
  248. while(!isspace(buf[j]) && j < sizeof(buf) && i < sizeof(url)-){
  249. url[i] = buf[j];
  250. i++, j++;
  251. }
  252. url[i] = ;
  253. printf("method: %s, url: %s\n", method, url);
  254. query_string = url;
  255. while(*query_string != '\0'){
  256. if(*query_string == '?'){//如果是GET方法且传参,必定是CGI模式
  257. *query_string = '\0';
  258. query_string++;
  259. cgi = ;
  260. break;
  261. }
  262. query_string++;
  263. }
  264. sprintf(path, "wwwroot%s", url);
  265. //method, url, query_string, cgi
  266. if(path[strlen(path)-] == '/'){ // '/'
  267. strcat(path, "index.html");//如果是GET方法且无参,拼接上首页信息
  268. }
  269. struct stat st;
  270. if(stat(path, &st) != ){
  271. echo_string(sock);
  272. ret = ;
  273. goto end;
  274. }else{
  275. if(S_ISDIR(st.st_mode)){ //如果是目录,则拼接上首页信息,默认任何目录下都可以访问首页
  276. strcat(path, "/index.html");
  277. }else if( (st.st_mode & S_IXUSR) || \ //如果是二进制文件
  278. (st.st_mode & S_IXGRP) || \
  279. (st.st_mode & S_IXOTH) ){
  280. cgi=;
  281. }else{
  282. }
  283. //ok->cgi=?, path, query_string, method
  284. if(cgi){
  285. printf("enter CGI\n"); //进入CGI模式处理
  286. exe_cgi(sock, method, path, query_string);
  287. }else{//非CGI处理
  288. printf("method: %s, url: %s, path: %s, cgi: %d, query_string: %s\n", method, url, path, cgi, query_string);
  289. drop_header(sock); //!!!!!!!!!!!!!!清除信息(不关心的内容)
  290. echo_www(sock, path, st.st_size);//非CGI模式时的响应
  291. }
  292. }
  293.  
  294. end:
  295. printf("quit client...\n"); //出错退出
  296. close(sock);
  297. return (void*)ret;
  298. #endif
  299. }

3、相关技术解释:

(1)CGI:通用网关接口

  基本原理:通用网关接口是一个Web服务器主机提供信息服务的标准接口。通过CGI接口,Web服务器根据客户端提交的资源请求信息,转交给服务器端对应的CGI程序进行处理,最后返回结果给客户端。简单来说就是HTTP服务器与客户端进行“交谈”的一种工具,其程序须运行在网络服务器上。

  组成CGI通信系统的是两部分:一部分是html页面,就是在用户端浏览器上显示的页面。另一部分则是运行在服务器上的Cgi程序。绝大多数的CGI程序被用来解释处理来自表单的输入信息,并在服务器产生相应的处理,或将相应的信息反馈给浏览器。CGI程序使网页具有交互功能。

  CGI在客户端与服务器通讯中的处理步骤:

  1)通过Internet把用户请求送到服务器;

  2)服务器接收用户请求并交给相应CGI程序处理;

  3)CGI程序把处理结果传送给服务器;

  4)服务器把结果返回给用户。

  前面已经介绍过服务器和客户端之间的通信,实际上是客户端的浏览器和服务器端的http服务器之间的HTTP通信,我们只需要知道浏览器请求执行服务器上哪个CGI程序就可以了,其他不必深究细节,因为这些过程不需要程序员去操作。服务器和CGI程序之间的通讯才是我们关注的。一般情况下,服务器和CGI程序之间是通过标准输入输出来进行数据传递的,而这个过程需要环境变量的协作方可实现。在服务器端执行步骤:1)服务器将URL指向一个应用程序  2)服务器为应用程序执行做准备  3)应用程序执行,读取标准输入和有关环境变量  4)应用程序进行标准输出。

(2)CGI关于环境变量

对于CGI程序来说,它继承了系统的环境变量。CGI环境变量在CGI程序启动时初始化,在结束时销毁。

当一个CGI程序不是被HTTP服务器调用时,它的环境变量几乎是系统环境变量的复制。

当这个CGI程序被HTTP服务器调用时,它的环境变量就会多了以下关于HTTP服务器、客户端、CGI传输过程等项目。

CONTENT_TYPE:如application/x-www-form-urlencoded,表示数据来自HTML表单,并且经过了URL编码。

ACCEPT:客户机所支持的MIME类型清单,内容如:”image/gif,image/jpeg”

REQUEST_METHOD:本项目涉及常见的两种方法:POST和GET,但我们写CGI程序时,最后还要考虑其他的情况。

  环境变量是一个保存用户信息的内存区。当客户端的用户通过浏览器发出CGI请求时,服务器就寻找本地的相应CGI程序并执行它。在执行CGI程序的同时,服务器把该用户的信息保存到环境变量里。接下来,CGI程序的执行流程是这样的:查询与该CGI程序进程相应的环境变量:第一步是request_method,如果是POST,就从环境变量的len,然后到该进程相应的标准输入取出len长的数据。如果是GET,则用户数据就在环境变量的QUERY_STRING里。

(3)POST/GET传输方式详解

1)POST方法

  如果采用POST方法,那么客户端发送的用户数据将存放在CGI进程的标准输入中,即消息正文内,较为隐蔽,且一般没有上限。同时将用户数据的长度赋予环境变量中的CONTENT_LENGTH。客户端用POST方式发送数据有一个相应的MIME类型(通用Internet邮件扩充服务:Multi-purpose Internet Mail Extensions)。目前,MIME类型一般是:application/x-wwww-form-urlencoded,该类型表示数据来自HTML表单。该类型记录在环境变量CONTENT_TYPE中,CGI程序应该检查该变量的值。

2)GET方法

  在该方法下,CGI程序无法直接从服务器的标准输入(用户发送的消息正文)中获取数据,因为服务器把它从标准输入接收到得数据编码到环境变量QUERY_STRING(或PATH_INFO)。

  采用GET方法提交HTML表单数据的时候,客户机将把这些数据附加到由ACTION标记命名的URL的末尾,用一个包括把经过URL编码后的信息与CGI程序的名字分开:http://www.mycorp.com/hello.html?name=hgq$id=1,QUERY_STRING的值为name=hgq&id=1(?左侧为要请求的资源,右侧为参数,参数形式一般为name=value形式,以“&”连接)。或者使用nomal形式的GET方法,无参数,不带正文,只有请求行+消息报头+空行。有些程序员不愿意采用GET方法,因为在他们看来,把动态信息附加在URL的末尾有违URL的出发点:URL作为一种标准用语,一般是用作网络资源的唯一定位标示。

3)POST与GET的区别

以 GET方式接收的数据是有长度限制,而用 POST方式接收的数据是没有长度限制的。并且,以 GET方式发送数据,可以通过 URL的形式来发送,但 POST方式发送的数据必须要通过 Form才到发送。

CGI程序示例 mathcgi.h :

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3.  
  4. void mymath(char *arg)
  5. {
  6. //data1=1000&data2=2000
  7. char *argv[];
  8. int i = ;
  9. char *start = arg;
  10. while(*start){
  11. if(*start == '='){
  12. start++;
  13. argv[i++] = start;
  14. continue;
  15. }
  16. if(*start== '&'){
  17. *start = '\0';
  18. }
  19. start++;
  20. }
  21. argv[i] = NULL;
  22. int data1 = atoi(argv[]);
  23. int data2 = atoi(argv[]);
  24. printf("<html><body><h1>");
  25. printf("%d + %d = %d<br/>", data1, data2, data1 + data2);
  26. printf("%d - %d = %d<br/>", data1, data2, data1 - data2);
  27. printf("%d * %d = %d<br/>", data1, data2, data1 * data2);
  28. printf("%d / %d = %d<br/>", data1, data2, data2==? : data1 / data2);
  29. printf("%d %% %d = %d<br/>", data1, data2, data2==? : data1 % data2);
  30. printf("</h1></body></html>");
  31. }
  32.  
  33. int main()
  34. {
  35. char *method = NULL;
  36. char *query_string = NULL;
  37. char *string_arg = NULL;
  38. int content_len = -;
  39. char buf[];
  40. if((method=getenv("METHOD"))){
  41. if(strcasecmp(method, "GET") == ){
  42. if((query_string=getenv("QUERY_STRING"))){
  43. string_arg = query_string;
  44. }
  45. }else{
  46. if(getenv("CONTENT_LENGTH")){
  47. content_len = atoi(getenv("CONTENT_LENGTH"));
  48. int i = ;
  49. for(; i < content_len; i++){
  50. read(, &buf[i], );
  51. }
  52. buf[i] = '\0';
  53. string_arg = buf;
  54. }
  55. }
  56. }
  57.  
  58. mymath(string_arg);
  59. return ;
  60. }

(2)HTML

本项目中只是使用了一些基本的HTML知识,下面是简单的 index.html :

  1. <html>
  2. <head>hello http</head>
  3. <body>
  4. <h1>Hello My Web!</h1>
  5. <img src="imag/mgh.jpg" alt="default" width="" height="">
  6. <a href="cgi-bin/select_cgi">select</a>
  7. <!--<form action="/cgi-bin/math_cgi" method="POST">
  8. First data:<br>
  9. <input type="text" name="data1" value="">
  10. <br>
  11. second data:<br>
  12. <input type="text" name="data2" value="">
  13. <br><br>
  14. <input type="submit" value="Submit">
  15. </form>--!>
  16. </body>
  17. </html>

到这里就基本可以访问网页信息了:

4、本机进行环回测试,用的IP是127.0.0.1,Http协议的TCP连接默认端口号为80:

图片自己选择,此页面实现的是两个数的加减乘除,当点击submit时跳转页面如下:

此时跳转到cgi_bin目录下的可执行文件debug_cgi,显示加减乘除的结果。

一个简陋的http服务器就完成了。后面还需要一些其它的扩展,再更新……

5、遇到的一些问题:

1)本地环回测试ok,Linux下的浏览器测试也可以,但不能接外部的浏览器访问(没有设置桥接模式)嗯~要是在外部浏览器测试的话千万别忘记关闭防火墙。

解决:切换超级用户:$service iptables stop

2)服务器应答时,没有将html格式的页面发送,而是将底层的实现代码展示在浏览器,并且在调试时将本来要打印的调试信息会打印到网页上(在回应空行时将send期望发送的数值写的太大,本来只需要发送两个字节的内容)
解决:先检查代码,思路正确,在容易出现问题的地方加入调试信息,最后将问题定位在echo_www()函数内 。

3)不能显示图片(这个问题是没有将所有发送的情况考虑完全,只考虑到目录、可执行程序,但没有考虑到如果请求的是一个路径明确的普通文件)
解决:测试请求一个路径明确的test.html文件,加入调试信息 ,将问题定位在:如果请求的资源存在,应该如何处理。对于普通文件,找到后并回显给浏览器;如果是目录,应答的是默认页面;如果是可执行程序,执行后返回结果

4)能显示图片后,但显示的不完整(原因:echo_www中,期望读取一行信息的line值太小,不能存下一张图片)

5)运行cgi模式时,每次提交数据并进行submit后都会自动出现提醒下载的页面
原因:在响应报头中,将Content-Type中的”text”写成”test”。而浏览器对于不能识别或解析的实体,都会提醒用户下载。

HTTP服务器的更多相关文章

  1. App开发:模拟服务器数据接口 - MockApi

    为了方便app开发过程中,不受服务器接口的限制,便于客户端功能的快速测试,可以在客户端实现一个模拟服务器数据接口的MockApi模块.本篇文章就尝试为使用gradle的android项目设计实现Moc ...

  2. 闰秒导致MySQL服务器的CPU sys过高

    今天,有个哥们碰到一个问题,他有一个从库,只要是启动MySQL,CPU使用率就非常高,其中sys占比也比较高,具体可见下图. 注意:他的生产环境是物理机,单个CPU,4个Core. 于是,他抓取了CP ...

  3. 闲来无聊,研究一下Web服务器 的源程序

    web服务器是如何工作的 1989年的夏天,蒂姆.博纳斯-李开发了世界上第一个web服务器和web客户机.这个浏览器程序是一个简单的电话号码查询软件.最初的web服务器程序就是一个利用浏览器和web服 ...

  4. SignalR系列续集[系列8:SignalR的性能监测与服务器的负载测试]

    目录 SignalR系列目录 前言 也是好久没写博客了,近期确实很忙,嗯..几个项目..头要炸..今天忙里偷闲.继续我们的小系列.. 先谢谢大家的支持.. 我们来聊聊SignalR的性能监测与服务器的 ...

  5. 使用 Nodejs 搭建简单的Web服务器

    使用Nodejs搭建Web服务器是学习Node.js比较全面的入门教程,因为要完成一个简单的Web服务器,你需要学习Nodejs中几个比较重要的模块,比如:http协议模块.文件系统.url解析模块. ...

  6. 通过ProGet搭建一个内部的Nuget服务器

    .NET Core项目完全使用Nuget 管理组件之间的依赖关系,Nuget已经成为.NET 生态系统中不可或缺的一个组件,从项目角度,将项目中各种组件的引用统统交给NuGet,添加组件/删除组件/以 ...

  7. 谈谈如何使用Netty开发实现高性能的RPC服务器

    RPC(Remote Procedure Call Protocol)远程过程调用协议,它是一种通过网络,从远程计算机程序上请求服务,而不必了解底层网络技术的协议.说的再直白一点,就是客户端在不必知道 ...

  8. 游戏服务器菜鸟之C#初探一游戏服务

    本人80后程序猿一枚,原来搞过C++/Java/C#,因为工作原因最后选择一直从事C#开发,因为读书时候对游戏一直比较感兴趣,机缘巧合公司做一个手游的项目,我就开始游戏服务器的折腾之旅. 游戏的构架是 ...

  9. 无法向会话状态服务器发出会话状态请求。请确保 ASP.NET State Service (ASP.NET 状态服务)已启动,并且客户端端口与服务器端口相同。如果服务器位于远程计算机上,请检查。。。

    异常处理汇总-服 务 器 http://www.cnblogs.com/dunitian/p/4522983.html 无法向会话状态服务器发出会话状态请求.请确保 ASP.NET State Ser ...

  10. SQL Server 无法连接到服务器。SQL Server 复制需要有实际的服务器名称才能连接到服务器。请指定实际的服务器名称。

    异常处理汇总-数据库系列  http://www.cnblogs.com/dunitian/p/4522990.html SQL性能优化汇总篇:http://www.cnblogs.com/dunit ...

随机推荐

  1. Android - Fragment (一)定义

    什么是Fragment,为什么要用Fragment? Fragment,直译为碎片.是Android UI的一种. Fragment加载灵活,替换方便.定制你的UI,在不同尺寸的屏幕上创建合适的UI, ...

  2. 动态语言的灵活性是把双刃剑 -- 以Python语言为例

    本文有些零碎,总题来说,包括两个问题:(1)可变对象(最常见的是list dict)被意外修改的问题,(2)对参数(parameter)的检查问题.这两个问题,本质都是因为动态语言(动态类型语言)的特 ...

  3. java数组排序(冒泡、直排)反转

    package lianxi; public class maopao { public static void main(String[] args){ int[] i=new int[]{45,6 ...

  4. maven简介及基础使用

    一.Maven简介 Maven可译为"知识的积累"."专家",主要服务于基于Java平台的项目构建.依赖管理和项目信息管理. 1.Maven-项目构建工具    ...

  5. Docker安装及基础使用

    Docker 安装 在 Mac OS X 系统中,首先你要下载安装包安装:Docker Toolbox 安装过程中,可以选择是否安装 Docker Machine,Docker Compose 等,默 ...

  6. java泛型(整理)

    1 泛型基础知识 泛型需要理解两个关键点:1)类型擦除 2)类型转换 1)类型擦除 泛型有个很重要的概念,是类型擦除.正确理解泛型概念的首要前提是理解类型擦除(type erasure). Java中 ...

  7. TCP/IP协议之IP层

    TCP/IP协议的结构参见下图.有应用层,运输层,网络层,链路层. 但是如果更细化的话,其实还有几层没在这上面体现出来. 1 表示层:数据格式化,代码转换,加密. 没有协议 2 会话层:解除或者建立与 ...

  8. 构建 MariaDB Galera Cluster 分布式数据库集群(二)

    MariaDB的安装 构建 MariaDB Galera Cluster之前,首先安装MariaDB,本文使用的版本是10.1 1.环境准备 主机: MariaDB01(192.168.56.102) ...

  9. Java 三目运算符表达式的一些问题

    最近在处理一个需求,需求描述如下:对数据库中查询出来的数据的某一个字段做一个简单处理.处理方式是:如果该字段的值(取值范围0~4,有可能为null)等于0,那么默认处理成1. 测试代码如下: publ ...

  10. Android与NativeC传递数据不正确问题

    操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Android studio 2.3.3 这两天一直在调试一个BUG,具体为通过 NativeC 来处理上层Android ...