网络编程——实现HTTP服务器端

2023-10-27

参考

  1. 《TCP/IP网络编程》 尹圣雨

Web服务器端

概述

Web服务器端是,基于HTTP(Hypertext Transfer Protocol)协议,将网页对应文件传输给客户端的服务器端。

Hypertext(超文本)是可以根据客户端请求而跳转的结构化信息。

HTTP协议是以超文本传输为目的而设计的应用层协议,同样基于TCP/IP实现。

浏览器属于基于套接字的客户端,因为连接到任意Web服务器端时,浏览器内部也会创建套接字。只是浏览器将服务器端传输的HTML格式的超文本解析为可读性较强的视图。

HTTP协议特点

无状态的Stateless协议

服务器端响应客户端请求后立即断开连接,即服务器端不会维持客户端状态。即使同一客户端再次发送请求,服务器还是以相同的方式处理新请求

为此,Web编程中通常会使用Cookie和Session技术

请求消息的结构

请求消息可以分为请求行、消息头、消息体等

(1)请求行

GET /index.html HTTP/1.1

含义是,请求(GET)index.html文件,希望以1.1版本的HTTP协议进行通信

请求行只能通过1行发送。请求行含有请求方式(目的)信息。典型的请求方式有GET和POST,GET主要用于请求数据,POST主要用于传输数据

(2)消息头

User Agent : Mozilla/5.0
Accept: image/gif, image/jpeg
......

消息头中包含发送请求的浏览器信息、用户认证信息等关于HTTP消息的附加信息
(3)空行
避免边界问题
(4)消息体
消息体仅在以POST方式请求时插入

消息体中装有客户端向服务器端传输的数据

响应消息的结构

响应消息由状态行、头信息、消息体等3个部分构成

(1)状态行

HTTP/1.1 200 OK

含义是,我想用HTTP1.1版本进行响应,你的请求已正确处理(200 OK)

状态行含有关于客户端请求的处理结果。表示“客户端请求的执行结果”的数字称为状态码,例如:

  1. 200 OK:成功处理了请求
  2. 404 Not Found:请求的文件不存在
  3. 400 Bad Request:请求方式错误,请检查

(2)消息头

Server : SimpleWebServer
Content-type : text/html
Content-length : 2048
......

含义是,服务器端名为SimpleWebServer,传输的数据类型为text/html,数据长度不超过2048字节

消息头中含有传输的数据类型和长度等信息

(3)空行
(4)消息体

<html>
......
<body>
......
</html>

通过消息体发送客户端请求的文件数据

实现Web服务器端

Web服务器端采用HTTP协议,即使用IOCP或epoll模型也不会大幅提升性能,因为客户端和服务器端交换1次数据后将立即断开连接,没有足够时间发挥IOCP或epoll的优势。在服务器端和客户端保存较长连接的前提下频繁发送大小不一的消息时(如网游服务器端),才能发挥这2种模型的优势

下面通过多线程模型实现Web服务器端。客户端每次请求时,都创建1个新线程响应
(1)基于Windows

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <WinSock2.h>
#include <process.h>
#include <WS2tcpip.h>
#define BUF_SIZE 2048
#define BUF_SMALL 100

unsigned WINAPI RequestHandler(void* arg);
char* ContentType(char* file);
void SendData(SOCKET sock, char* ct, char* fileName);
void SendErrorMSG(SOCKET sock);
void ErrorHandling(char* message);

int main(int argc, char* argv[])
{
	WSADATA wsaData;
	SOCKET hServSock, hClntSock;
	SOCKADDR_IN servAdr, clntAdr;

	HANDLE hThread;
	DWORD dwThreadID;
	int clntAdrSize;
	char tmpbuf[20] = { 0 };

	if (argc != 2)
	{
		printf("Usage : %s <port>\n", argv[0]);
		exit(1);
	}

	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
	{
		ErrorHandling("WSAStartup() error!");
	}

	hServSock = socket(PF_INET, SOCK_STREAM, 0);
	memset(&servAdr, 0, sizeof(servAdr));
	servAdr.sin_family = AF_INET;
	servAdr.sin_addr.s_addr = htonl(INADDR_ANY);
	servAdr.sin_port = htons(atoi(argv[1]));

	if (bind(hServSock, (SOCKADDR*)&servAdr, sizeof(servAdr)) == SOCKET_ERROR)
	{
		ErrorHandling("bind() error");
	}
	if (listen(hServSock, 5) == SOCKET_ERROR)
	{
		ErrorHandling("listen() error");
	}

	/*请求及响应*/
	while (1)
	{
		clntAdrSize = sizeof(clntAdr);
		hClntSock = accept(hServSock, (SOCKADDR*)&clntAdr, &clntAdrSize);
		printf("Connection Request : %s:%d\n", inet_ntop(AF_INET, &clntAdr.sin_addr, tmpbuf, sizeof(tmpbuf)), ntohs(clntAdr.sin_port));
		hThread = (HANDLE)_beginthreadex(NULL, 0, RequestHandler, (void*)hClntSock, 0, (unsigned*)&dwThreadID);
	}
	closesocket(hServSock);
	WSACleanup();
	return 0;
}

unsigned WINAPI RequestHandler(void* arg)
{
	printf("RequestHandler\n");
	SOCKET hClntSock = (SOCKET)arg;
	char buf[BUF_SIZE];
	char method[BUF_SMALL];
	char ct[BUF_SMALL];
	char fileName[BUF_SMALL];
	char* tmp = NULL;

	recv(hClntSock, buf, BUF_SIZE, 0);
	if (strstr(buf, "HTTP/") == NULL)                         // 查看是否为HTTP提出的请求
	{
		SendErrorMSG(hClntSock);
		closesocket(hClntSock);
		return 1;
	}

	strcpy_s(method, BUF_SMALL, strtok_s(buf, " /", &tmp));
	if (strcmp(method, "GET"))                                // 查看是否为GET方式请求 
	{
		SendErrorMSG(hClntSock);
	}

	strcpy_s(fileName, BUF_SMALL, strtok_s(NULL, " /", &tmp));// 查看请求文件名
	strcpy_s(ct, BUF_SMALL, ContentType(fileName));           // 查看Content-type
	SendData(hClntSock, ct, fileName);                        // 响应
	return 0;
}

void SendData(SOCKET sock, char* ct, char* fileName)
{
	printf("SendData\n");
	printf("filename:%s\n", fileName);
	char protocol[] = "HTTP/1.0 200 OK\r\n";
	char servName[] = "Server:simple web server\r\n";
	char cntLen[] = "Content-length:2048\r\n";
	char cntType[BUF_SMALL];
	char buf[BUF_SIZE];
	FILE* sendFile;

	sprintf_s(cntType, BUF_SMALL, "Content-type:%s\r\n\r\n", ct);
	if ((fopen_s(&sendFile, fileName, "r")) != 0)
	{
		SendErrorMSG(sock);
		return;
	}

	/*传输头信息*/
	send(sock, protocol, strlen(protocol), 0);
	send(sock, servName, strlen(servName), 0);
	send(sock, cntLen, strlen(cntLen), 0);
	send(sock, cntType, strlen(cntType), 0);
	/*传输请求数据*/
	while (fgets(buf, BUF_SIZE, sendFile) != NULL)
	{
		send(sock, buf, strlen(buf), 0);
	}

	closesocket(sock);                               // 由HTTP协议响应后断开
}

void SendErrorMSG(SOCKET sock)                       // 查看错误时传递信息
{
	char protocol[] = "HTTP/1.0 400 Bad Request\r\n";
	char servName[] = "Server:simple web server\r\n";
	char cntLen[] = "Content-length:2048\r\n";
	char cntType[] = "Content-type:text/html\r\n\r\n";
	char content[] = "<html><head><title>NETWORK</title></head>"
		"<body><font size=+5><br> 发生错误!查看请求文件名和请求方式!"
		"</font></body></html>";
	send(sock, protocol, strlen(protocol), 0);
	send(sock, servName, strlen(servName), 0);
	send(sock, cntLen, strlen(cntLen), 0);
	send(sock, cntType, strlen(cntType), 0);
	send(sock, content, strlen(content), 0);
	closesocket(sock);
}

char* ContentType(char* file)                        // 区分Conteent-type
{
	char extension[BUF_SMALL];
	char fileName[BUF_SMALL];
	char* tmp;

	strcpy_s(fileName, BUF_SMALL, file);
	strtok_s(fileName, ".", &tmp);
	strcpy_s(extension, BUF_SMALL, strtok_s(NULL, ".", &tmp));
	if (!strcmp(extension, "html") || !strcmp(extension, "htm"))
	{
		return "text/html";
	}
	else
	{
		return "text/plain";
	}
}

void ErrorHandling(char* message)
{
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}

(2)基于Linux

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <pthread.h>

#define BUF_SIZE 1024
#define SMALL_BUF 100

void* request_handler(void* arg);
void send_data(FILE* fp, char* ct, char* file_name);
char* content_type(char* file);
void send_error(FILE* fp);
void error_handling(char* message);

int main(int argc, char* argv[])
{
	int serv_sock, clnt_sock;
	struct sockaddr_in serv_adr, clnt_adr;
	int clnt_adr_size;
	char buf[BUF_SIZE];
	pthread_t t_id;
	if (argc != 2)
	{
		printf("Usage : %s <port>\n", argv[0]);
		exit(1);
	}

	serv_sock = socket(PF_INET, SOCK_STREAM, 0);
	memset(&serv_adr, 0, sizeof(serv_adr));
	serv_adr.sin_family = AF_INET;
	serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
	serv_adr.sin_port = htons(atoi(argv[1]));
	if (bind(serv_sock, (struct sockaddr*)&serv_adr, sizeof(serv_adr)) == -1)
	{
		error_handling("bind() error");
	}
	if (listen(serv_sock, 20) == -1)
	{
		error_handling("listen() error");
	}

	while (1)
	{
		clnt_adr_size = sizeof(clnt_adr);
		clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_adr, &clnt_adr_size);
		printf("Connection Request : %s:%d\n", inet_ntoa(clnt_adr.sin_addr), ntohs(clnt_adr.sin_port));
		pthread_create(&t_id, NULL, request_handler, &clnt_sock);
		pthread_detach(t_id);
	}
	close(serv_sock);
	return 0;
}

void* request_handler(void* arg)
{
	int clnt_sock = *((int*)arg);
	char req_line[SMALL_BUF];
	FILE* clnt_read;
	FILE* clnt_write;

	char method[10];
	char ct[15];
	char file_name[30];

	clnt_read = fdopen(clnt_sock, "r");
	clnt_write = fdopen(dup(clnt_sock), "w");
	fgets(req_line, SMALL_BUF, clnt_read);
	if (strstr(req_line, "HTTP/") == NULL)
	{
		send_error(clnt_write);
		fclose(clnt_read);
		fclose(clnt_write);
		return;
	}

	strcpy(method, strtok(req_line, " /"));
	strcpy(file_name, strtok(NULL, " /"));
	strcpy(ct, content_type(file_name));
	if (strcmp(method, "GET") != 0)
	{
		send_error(clnt_write);
		fclose(clnt_read);
		fclose(clnt_write);
		return;
	}

	fclose(clnt_read);
	send_data(clnt_write, ct, file_name);
}

void send_data(FILE* fp, char* ct, char* file_name)
{
	char protocol[] = "HTTP/1.0 200 OK\r\n";
	char server[] = "Server:Linux Web Server \r\n";
	char cnt_len[] = "Content-length:2048\r\n";
	char cnt_type[SMALL_BUF];
	char buf[BUF_SIZE];
	FILE* send_file;

	sprintf(cnt_type, "Content-type:%s\r\n\r\n", ct);
	send_file = fopen(file_name, "r");
	if (send_file == NULL)
	{
		send_error(fp);
		return;
	}

	/*传输头信息*/
	fputs(protocol, fp);
	fputs(server, fp);
	fputs(cnt_len, fp);
	fputs(cnt_type, fp);

	/*传输请求数据*/
	while (fgets(buf, BUF_SIZE, send_file) != NULL)
	{
		fputs(buf, fp);
		fflush(fp);
	}
	fflush(fp);
	fclose(fp);
}

char* content_type(char* file)
{
	char extension[SMALL_BUF];
	char file_name[SMALL_BUF];
	strcpy(file_name, file);
	strtok(file_name, ".");
	strcpy(extension, strtok(NULL, "."));

	if (!strcmp(extension, "html") || !strcmp(extension, "htm"))
	{
		return "text/html";
	}
	else
	{
		return "text/plain";
	}
}

void send_error(FILE* fp)
{
	char protocol[] = "HTTP/1.0 400 Bad Request\r\n";
	char server[] = "Server:Linux Web Server \r\n";
	char cnt_len[] = "Content-length:2048\r\n";
	char cnt_type[] = "Content-type:text/html\r\n\r\n";
	char content[] = "<html><head><title>NETWORK</title></head>"
		"<body><font size=+5><br>发生错误!查看请求文件名和请求方式"
		"</font></body></html>";

	fputs(protocol, fp);
	fputs(server, fp);
	fputs(cnt_len, fp);
	fputs(cnt_type, fp);
	fflush(fp);
}

void error_handling(char* message)
{
	fputs(message, stderr);
	fputc('\n', stderr);
	exit(1);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

网络编程——实现HTTP服务器端 的相关文章

  • 在 grails 中编写代理

    我正在使用 Gralis 1 3 7 我正在编写一个控制器 需要从另一台服务器获取 PDF 文件并将其返回给客户端 我想以某种相当有效的方式来做到这一点 例如 class DocController def view URL source
  • 当响应有正文时,内容长度或传输编码是必需的吗

    如果响应有正文 可以有正文 即状态代码不是 204 或 304 则响应标头中是否应始终具有内容长度或传输编码 在规范中 它不是很清楚 在我的场景中 我的主体没有内容长度或传输编码标头 因此卷曲继续等待no chunk no close no
  • 在 Go 中读取请求负载?

    我正在使用文件上传器 需要请求负载中的详细信息来裁剪它 func Upload w http ResponseWriter r http Request reader err r MultipartReader if err nil htt
  • 如何在 Laravel 中禁用 JSON 响应的分块编码?

    我从 Laravel 中的控制器方法返回一个数组 Laravel 将此解释为我想发送 JSON 这很好 但它没有设置Content Length并改为使用Transfer Encoding chunked 我的回复很小 所以我不想把它们分块
  • 通用开源 REST 客户端? [关闭]

    就目前情况而言 这个问题不太适合我们的问答形式 我们希望答案得到事实 参考资料或专业知识的支持 但这个问题可能会引发辩论 争论 民意调查或扩展讨论 如果您觉得这个问题可以改进并可能重新开放 访问帮助中心 help reopen questi
  • Tomcat 7 停止接收 HTTP 请求

    我有一个Tomcat 7接收大量数据的服务器GET 要求 这种方法在一段时间内效果很好 然后突然停止工作 7 8 小时后 当它停止工作时 我收到此错误 五月 06 2015 12 47 58 AM org apache coyote htt
  • 从 Django 基于类的视图的 form_valid 方法调用特殊(非 HTTP)URL

    如果你这样做的话 有一个 HTML 技巧 a href New SMS Message a 点击新短信打开手机的本机短信应用程序并预 先填写To包含所提供号码的字段 在本例中为 1 408 555 1212 以及body与提供的消息 Hel
  • 如何给所有HttpClient请求方法添加参数?

    我正在编写一些使用 Apache 的 Java 代码HttpClient版本4 2 2使用 RESTful 第三方 API 该 API 具有利用 HTTP 的方法GET POST PUT and DELETE 需要注意的是 我使用的是 4
  • 如何自定义解析错误的 HTTP 400 响应?

    我编写了一个 REST API 服务 要求所有响应均为 JSON 但是 当 Go HTTP 请求解析器遇到错误时 它会返回 400 作为纯文本响应 而不会调用我的处理程序 例子 gt curl i H Authorization Basic
  • REST URL 结构建议

    我正在尝试为我正在开发的网站的愿望清单部分敲定一个宁静的网址结构 这是一个非常简单的模型 用户可以有许多愿望清单 每个愿望清单可以包含许多产品 目前我有明显的 CRUD URL 来操作愿望清单本身 GET account wishlists
  • 使用传输编码分块的 HTTP 响应中的最大块大小是多少?

    The w3 org RFC2616 http www w3 org Protocols rfc2616 rfc2616 sec3 html sec3 6 1似乎没有定义块的最大大小 但是如果没有最大块大小 则没有空间用于块扩展 必须有一个
  • 我首次设置 AWS EB 时创建的默认安全组是什么?

    我对几个组所扮演的角色感到困惑 这些组似乎已自动添加到我的 AWS 安全组列表中 并以我收集的默认配置进行连接 并且想知道它们是如何工作的 以及它们的安全性如何 改变 具体来说 有三个神秘之处 launch wizard 1其中有一条入站规
  • 如何在C++中使用Curl获取HTTP响应字符串

    我对 HTTP 命令和 libcurl 库非常陌生 我知道如何获取 HTTP 响应代码 但不知道如何获取 HTTP 响应字符串 以下是我为获取响应代码而编写的代码片段 任何有关如何获取响应字符串的帮助将不胜感激 curl easy seto
  • GET 和 POST 方法的单独 Flask 路由

    在 Flask 中定义路由时 最好的做法是使用由多个 HTTP 方法定义的单个路由 并在该单个路由中使用显式逻辑处理不同的 HTTP 方法 例如 app route api users methods GET POST def users
  • 如何使用独立的 Jetty 进行服务器推送

    我正在尝试使用独立的 Jetty 在静态网站上测试服务器推送功能 我的网站由一个index html 1个CSS 一堆图像组成 目录结构为 Album index html style css images image 1 png a se
  • Angular JS 在调用新的 $http 之前取消 $http 调用

    在 Angular JS 1 1 5 中 您可以取消之前启动的 http 调用 这两个link1 https stackoverflow com questions 16962232 in angularjs how to stop ong
  • 是否可以修改 $_SESSION 变量?

    恶意用户是否可以将 SESSION 在 php 中 变量设置为他想要的任何值 很大程度上取决于您的代码 有一点非常明显 SESSION username REQUEST username
  • 以 RESTful 方式增加资源计数器:PUT 与 POST

    我有一个带有计数器的资源 为了举例 我们将该资源称为profile 计数器是数量views对于该配置文件 Per the 休息维基 http rest blueoxen net cgi bin wiki pl HttpMethods PUT
  • 返回重定向作为对 Ajax(fetch、XHR 等)请求的响应

    如果浏览器收到对 ajax 请求的重定向响应 会发生什么 如果浏览器收到对 ajax 请求的重定向响应 会发生什么 如果服务器发送重定向 又名 302 响应加上 Location 标头 浏览器将自动遵循重定向 对此的回应second请求 假
  • 当会话令牌无效时,我应该使用什么状态代码?

    创建 Web 服务 RESTful 时 当会话令牌无效时我应该使用什么状态代码 目前我公司的人给我发了一个404 未找到 但我认为这是不正确的 因为资源存在 也许我应该使用 401 Unauthorized 你怎么认为 您建议我在这种情况下

随机推荐

  • 【AD21】keepout层和机械1层怎么相互转换

    1 从keepout转换为Mechanical 1层 见下图 图1 图2 图3 图4 2 从keepout转换为Mechanical 1层 见下图 如果想要将keepout层转换成机械1层 可以先全选中想要转换的keepoutz层 然后在c
  • Docker之旅:在Docker容器中创建第一个程序

    Docker的概念 Docker是开发人员和系统管理员 使用容器开发 部署和运行应用程序的平台 使用Linux容器来部署应用程序称为集装箱化 容器不是新的事物 但它们用于轻松部署应用程序 一 测试一下Docker的版本 1 查看Docker
  • 实用工具推荐,浏览器必备宝藏插件:Wetab新标签页

    打开浏览器 你的起始页是否充满了广告和各种乱七八糟的信息呢 或者过于单调 而失去了某些你想要的功能 这里给大家推荐一个在Chrome浏览器和edge浏览器上都能愉快使用的浏览器插件 Wetab新标签页 没有广告 页面干净美观且具备各种实用功
  • android live 电视 源码,GitHub - mxiaoguang/LivePlayback: Android TV直播电视节目 ,包含各央视频道及卫视频道...

    Android TV直播电视节目 更多技术博客 项目 欢迎关注公众号 Android TV开发交流群 135622564 传统电视直播节目 在Android TV上起着越来越重要的作用 央视 各地卫视 满足观众日益增长的多元化需求 看下效果
  • IV转换电路原理图

    毫安级IV转换电路如下 如果要uA级转换 可把运放改为 D795 SG 8210 L C6482等输入偏置电流在pA级的运
  • tomcat配置443端口

  • true_type与false_type

    std true type和std false type 实际上是类型别名 是两个类型 类模板 注意区分true type与false type与true和false区别 true type false type代表类型 true fals
  • 【项目实战】Python基于局部离群因子LOF算法(LocalOutlierFactor)实现信用卡数据异常值检测项目实战

    说明 这是一个机器学习实战项目 附带数据 代码 文档 代码讲解 如需数据 代码 文档 代码讲解可以直接到文章最后获取 1 项目背景 异常检测是数据挖掘领域研究的基本问题之一 已被广泛应用于网络入侵检测 信用卡欺诈侦查等领域 局部离群因子 简
  • 以太坊客户端Geth命令用法-参数详解

    Geth是在以太坊智能合约开发中最常用的工具 必备开发工具 一个多用途的命令行工具 熟悉Geth可以让我们有更好的效率 大家可收藏起来作为Geth命令用法手册 本文主要是对geth help的翻译 基于最新的geth 1 7 3 stabl
  • sqlite3查看数据库中有哪些表(代码)

    说实话 用代码实现sqlite3查看数据库中有哪些表我还真的没找到现成资源 网上提供的语句还真用不了 而且大多都是命令行语句 由于的做的MFC项目要用到这个功能 特意学习了下 下面分享我的成果 希望可以帮到你 环境 VS2005 inclu
  • FPGA中值滤波实现并Modelsim仿真,与MATLAB中值滤波进行对比

    文章目录 一 中值滤波算法 二 FPGA实现中值滤波 2 1 3 3窗口的生成 2 2 排序模块 2 3中值滤波模块 2 4 整体RTL图 三 modeslim仿真 四 matlab中值滤波 五 效果对比 一 中值滤波算法 1 中值滤波算法
  • 解决wangEditor表格边框显示不出来、没有的问题

    仔细阅读文档 不过我一直找表格或者边框搜索 发现没有特定的栏目 所以忽略了 当然也有我不够仔细的原因 有点着急莽荒了 链接 在官网的这个页面 从官网贴下来的 把这段复制到想要的地方就可以了
  • 自定义表单控件 [(ngModel)]

    ngModel 拆分 ngModel 将 输入 输出组合起来 进行双向数据绑定 拆分开来 输入属性 ngModel ngModelChange 输出监听元素值的变化 并同步view value与model value
  • 头都给我找烂

    mysql5 6下载 https dev mysql com downloads file id 487425 win10添加本地用户和组 https blog csdn net qq 40151857 article details 89
  • 【Linux命令集】top命令的用法详解

    在使用linux系统中 我们最长用的查看系统性能的方式就是使用命令top 通知我们只关心总体的cpu和内存的使用情况 对其他的参数基本无视 也看不懂 下面来介绍一下top的详细参数的意义 top视图 进入top的基本视图 我们来结合这个视图
  • 电容选型及计算

    一 电容容值计算 1 电容整流波形分析 经过整流后三相电压整流为六脉波电压 电压波形如下图 上图中各时期充电过程如下 在t0 t3阶段是一个T 6周期 一个完整波头阶段 在t0 t1阶段 电容放电 给负载提供能量 此时输入电压小于电容电压
  • openEuler实验之A-Tune智能调优

    1 A Tune介绍 A Tune是一款基于AI开发的系统性能优化引擎 它利用人工智能技术 对业务场景建立精准的系统画像 感知并推理出业务特征 进而做出智能决策 匹配并推荐最佳的系统参数配置组合 使业务处于最佳运行状态 1 1 A Tune
  • redis docker安装、进入命令行后启动服务

    下载redis镜像 首先查看一下redis是否正确 docker search redis 显示 NAME DESCRIPTION STARS OFFICIAL AUTOMATED redis Redis is an open source
  • 一文读懂工厂MES系统的详细功能介绍

    一 工单管理 MES通过工单来管理生产执行 工单状态有 创建 下达 执行 完成 取消 计划员创建工单 审核通过后释放到设备或产线 仓库可收到工单下达的通知 及时备料 产线只能看到已下达的工单 执行工单 完成后登记报工 将相关信息反馈回ERP
  • 网络编程——实现HTTP服务器端

    参考 TCP IP网络编程 尹圣雨 Web服务器端 概述 Web服务器端是 基于HTTP Hypertext Transfer Protocol 协议 将网页对应文件传输给客户端的服务器端 Hypertext 超文本 是可以根据客户端请求而