Demo:HTTP

阅读: 评论:0

Demo:HTTP

Demo:HTTP

HTTP协议

HTTP(HyperText Transfer Protocol)超文本传输协议,用于解决万维网(www)Server/Client间统一通信的问题,面向事务(transaction-oriented,一系列信息的完整传输交换)应用层协议

URL与URI

URI(Uniform Resource Identifier), 区别不同网络事物的抽象概念,如sina代表新浪网,qq号代表一个账户
URL(Uniform Resource Locator),统一资源定位符 = “协议://IP地址:端口/路径和文件名”,访问机制+网络位置,(大小写不敏感)
URN(Uniform Resource Name),来唯一标识一个实体,特定命名空间+名字,不需要知道地址或访问机制

网页访问

网页访问过程

鼠标点击某链接 地址栏输入域名并回车 浏览器分析URL 浏览器向DNS请求解析域名的IP地址 DNS返回IP地址 浏览器与IP地址的服务器建立TCP连接port=80 浏览器向服务器请求home目录下的mypage.html文件/home/mypage.html 服务器响应返回mypage.html文件 S/C端释放TCP连接 浏览器解析mypage.html并显示给用户

PS:为加快响应用户的速度,音视频是点击页面上的播放按钮后再次建立连接并传输
从Client请求到得到文档计时:RTT*2+文档传输时间

Client Server 第一次TCP请求 第一次TCP应答 Round-Trip Time 第三次TCP应答+HTTP请求 HTTP响应+文档的传输 Round-Trip Time 传输结束 Client Server

HTTP特性

  • 面向文本(text-oriented)
  • 无连接
    HTTP本身不建立或维持连接
  • 无状态(stateless)
    Server不记录Client的历史,每次HTTP请求都是新的,如此支持HTTP服务器应用的高并发
  • 持续链接(persistent connection,HTTP/1.1后)
    解决2*RTT时间+变量空间的浪费,Server响应后一段时间内保持TCP连接继续HTTP请求响应
    非流水线式(without pipelining):Client等到Server的响应才请求,和HTTP1.0差别不大
    流水线式(with pipelining):Clinet流水线请求+Server流水线响应,"真"提速

代理服务器

Proxy Server,又被称为“万维网高速缓存(Web cache)”,将请求和响应暂存于本地磁盘中,新请求与暂存请求相同时直接返回暂存响应,举例

PC1 HTTP Proxy Server PC2 Router1 Router2 源点服务器Origin Server

Client优先访问Proxy Server,Proxy Server未缓存响应时,由Proxy Server 向 Origin Server 发起TCP链接及HTTP请求,收到请求对象后,先复制缓存在本地存储器中再通过请求时建立的TCP连接返回对象

HTTP报文

分为请求报文和响应报文,ASCII编码的字段组成

  • 开始行
    CR(Carriage Return)回车 ’ r ’ 与 LF(Line Feed)换行 ‘n’ 结尾。 换行与回车的历史起源
    请求报文中: 请求行 (Request-Line)
    GET IP:8080/home/mypage.html HTTP/1.1
    方法(method)有:GET,PUT,OPTION,DELETE,TRACE,CONNECT,POST
    响应报文中: 状态行 (Status-Line)
    HTTP/1.1 202 Accepted
    HTTP/1.1 404 Not Found
    状态码(Status-Code)有:
    1xx:通知信息
    2xx:成功
    3xx:重定向
    4xx:请求错误(不能完成)
    5xx:服务器差错无法完成请求

  • 首部行

  • 实体主体(entity body)
    举例:
    GET /home/mypage.html HTTP/1.1rn {URL使用相对路径}
    Host: 域名rn {首部给出域名}
    Connection: Closern {传输结束请求文档即可释放连接}
    Accept-Language: cnrn

HTTP/1.1 301 Moved Permanently {永久转移}
Location: IP:port/location/mypage.html {新的URL}

Cookie

Server与Client间传递的状态信息

张三 哔哩哔哩 用户名与密码 为张三生成一个唯一识别码123456作为后端索引 响应报文的首部行Set-cookie:123456 浏览器在Cookie文件中管理主机名和识别码 张三再次请求时的首部行Cookie:123456 记录用户操作(购物结算等) 张三 哔哩哔哩

HTML

HyperText Markup Language 超文本标记语言

<html><head><title>好诗</title></head><body> <H1>一级标题</H1><H2>春江花月夜</H2><p>春江潮水连海平,海上明月共潮生。<br>滟滟随波千万里,何处春江无月明! <br>江流宛转绕芳甸,月照花林皆似霰; <br>空里流霜不觉飞,汀上白沙看不见。 <br>江天一色无纤尘,皎皎空中孤月轮。 <br>江畔何人初见月?江月何年初照人? <br>人生代代无穷已,江月年年望相似。</P><p>My UNIX do not have Chiness charactor set</P></body>
</html>

使用ASCII编码,浏览器直接打开查看效果

HTML允许在网页中插入图像(内含图像inline image),其中gif格式压缩后体积最小

远程连接:终点在其他网站
本地连接:终点在本计算机

静态文档(static document)
工人写完后不再改变,简单但不灵活

动态文档(dynamic document)
浏览器访问Server时程序动态创建

通用网关接口(CGI,Common Gateway Interface
是一种定义动态文档的创建,数据输入(给Server应用程序) 和 输出的标准
CGI程序 是 脚本程序(script language)

http_request http_response Client Server CGI Database dynamic_document

活动文档(active document)

  • 服务器推送(server push):服务器运行程序更新文档,服务器 及 网络 开销随用户数量增大
  • 活动文档(active document):服务器返回活动程序副本,在浏览器运行,开销小

TCP/IP

了解Socket套接字编程,主要
变量有:
sockaddr
socket(_fd)
函数有:
Client端的socket()、send()、recv()
Server端Listen()、accept()

#include <iostream>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <unistd.h>
//-------------------
//V1.0 接收请求,显示报文
//-------------------
using namespace std;
int main(void)
{int listener=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);if(listener==(int)(~0)){	cout<<"Listenner Initiate Failure!"<<endl;return 0;}struct sockaddr_in ServerAddr;ServerAddr.sin_family=AF_INET;ServerAddr.sin_port=htons(8080); //http服务端口ServerAddr.sin_addr.s_addr=INADDR_ANY;if(bind(listener,(struct sockaddr*)&ServerAddr,sizeof(ServerAddr))== -1){cout<<"Binding Failure!"<<endl;return 0;}if(listen(listener,5)==-1){cout<<"Listening Start Failure!"<<endl;return 0;}int Client_id;struct sockaddr_in ClientAddr;socklen_t ClientAddrLen = sizeof(ClientAddr);while(1){cout<<&#"<<endl;Client_id=accept(listener,(struct sockaddr*)&ClientAddr,&ClientAddrLen);if(Client_id==(int)(~0)){cout<<"Accept Error!"<<endl;continue;}cout<<"Accept message from Client :"<<endl;char recvData[512]={};recv(Client_id,recvData,512,0);cout<<recvData<<endl;const char* cmd=recvData;close(Client_id);if(!strcmp(cmd,"CloseServer"))break;}close(listener);return 0;
}

在Unix系统下运行以上源文件编写的HTTPServer,再使用其他主机上的浏览器通过URL访问任意Server上的资源,HTTPServer都会打印HTTP请求的内容

可以手写响应报文:

char sendData[512]={0};
strcpy(sendData,"HTTP/1.1 202 OK!rncontent-type: text/html; charset=UTF-8rnrn<html><head><title>Try</title></head><body><H1>Title-C1</H1></body></html>");
send(Client_id,&sendData,512,0);

再使用其他主机上的浏览器通过URL访问任意Server上的资源,HTTPServer将返回响应报文
在浏览器中可以看到响应报文实体(html文档)在浏览器中显示的效果

按下F12使用控制台,刷新后重新访问URL,可查看响应报文的标头

文件存取

首先提取请求中的文件名,然后使用中的ifstream类open(请求的file),open()函数搜索可执行文件所在的目录下是否存在目标文件,成功打开后读取内容,并与HTTP的开始行、首部行,一并返回

//-----------------------------------
//V2.0 接受请求,分析URL,响应传输html文档
//-----------------------------------
#include <iostream>
#include <string.h>
#include <string>
#include <fstream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <unistd.h>using namespace std;
int main(void)
{int listener=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);if(listener==(int)(~0)){	cout<<"Listenner Initiate Failure!"<<endl;return 0;}struct sockaddr_in ServerAddr;ServerAddr.sin_family=AF_INET;ServerAddr.sin_port=htons(8080);ServerAddr.sin_addr.s_addr=INADDR_ANY;setsockopt(listener, SOL_SOCKET, SO_REUSEPORT, (char*)&ServerAddr, sizeof(ServerAddr));if(bind(listener,(struct sockaddr*)&ServerAddr,sizeof(ServerAddr))== -1){cout<<"Binding Failure!"<<endl;return 0;}if(listen(listener,5)==-1){cout<<"Listening Start!"<<endl;return 0;}int Client_id;struct sockaddr_in ClientAddr;socklen_t ClientAddrLen = sizeof(ClientAddr);while(1){cout<<&#"<<endl;//--接收请求--Client_id=accept(listener,(struct sockaddr*)&ClientAddr,&ClientAddrLen);if(Client_id==(int)(~0)){cout<<"Accept Error!"<<endl;continue;}cout<<"Accept message from Client :"<<endl;char recvData[512]={0};recv(Client_id,recvData,512,0);recvData[511]='';cout<<recvData<<endl;//--处理请求--string fpath(recvData);string::size_type start=fpath.find('/');string::size_type end=fpath.find(' ',start);string fname = fpath.substr(start+1,end-start-1); //文件名提却ifstream inFile;string content;inFile.open(fname.c_str(), ios::in);if (inFile)  //条件成立,则说明文件打开成功{cout << "File Open Succuss!" << endl;string tmp;while(inFile>>tmp)content=content+tmp;cout<<content<<endl;inFile.close();}elsecout << "File doesn't exist" << endl;//--发送响应--char sendData[1024]={0};char httpHeader[255]={0};pty()) //404 not found httpheaderstrcpy(httpHeader,"HTTP/1.1 404 file not found!rnrn");else				//202 OK httpheaderstrcpy(httpHeader,"HTTP/1.1 202 OK!rncontent-type: text/html; charset=UTF-8rnrn");cout<<httpHeader<<endl;strcpy(sendData,httpHeader);strcat(sendData,content.c_str());send(Client_id,&sendData,1024,0);//--关闭check--const char* cmd=recvData;close(Client_id);if(!strcmp(cmd,"CloseServer"))break;}close(listener);return 0;
}

进程与线程

线程间通信

accept(),recv()会阻塞,需要特别小心
如多线程项目中,收到退出信号后,阻塞态的主线程在收到(不可能到来的)下一个信号前,无法响应退出命令
可以将他们设置为非阻塞

并发回显测试

/* -----------------------------------------------------------------
Server.V3.0.CentOS 多线程接受,回显,主线程main中accept()会阻塞,CloseServer需发送两次以上
-------------------------------------------------------------------*/
#include <iostream>
#include <string.h>
#include <string>
#include <fstream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/errno.h>
#include <pthread.h>
#include <deque>
#include <vector>
using namespace std;#define ERROR ((int)(~0))
#define MAX_THREAD_NUM 4
#define MAX_REQ_NUM 512     bool exit_flag=false;
int thread_no[MAX_THREAD_NUM];
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
vector<deque<int> > Client_id(MAX_THREAD_NUM);  //线程请求队列void *pthread_start_fcn(void *);                //线程启动函数
int find_shortest_deque(vector<deque<int> >&); //最短队列查找
bool empty_check(vector<deque<int> >&);        //空队列检测
bool full_check(vector<deque<int> >&);         //满队列检测int main(void)
{//监听套接字int listener=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);if(listener==ERROR){	cout<<"Listenner Initiate Failure!"<<endl;return 0;}struct sockaddr_in ServerAddr;ServerAddr.sin_family=AF_INET;ServerAddr.sin_port=htons(8080);ServerAddr.sin_addr.s_addr=INADDR_ANY;setsockopt(listener, SOL_SOCKET, SO_REUSEPORT, (char*)&ServerAddr, sizeof(ServerAddr));if(bind(listener,(struct sockaddr*)&ServerAddr,sizeof(ServerAddr))== -1){cout<<"Binding Failure!"<<endl;return 0;}if(listen(listener,5)==-1){cout<<"Listening Initiation Failure!"<<endl;return 0;}cout<<&#"<<endl;//初始化处理线程pthread_mutex_lock(&lock);for (int i=0;i<MAX_THREAD_NUM;i++){thread_no[i]=i;pthread_t tmp;pthread_create(&tmp,NULL,pthread_start_fcn,&thread_no[i]);pthread_detach(tmp);}pthread_mutex_unlock(&lock);//开始接收套接字struct sockaddr_in ClientAddr;socklen_t ClientAddrLen = sizeof(ClientAddr);while(true){if(exit_flag==false){//--接收请求--//cout<<"Before Accept!"<<endl;  //阻塞bug定位Aint id = accept(listener,(struct sockaddr*)&ClientAddr,&ClientAddrLen);//cout<<"After Accept!"<<endl;   //阻塞bug定位Bif( id == ERROR ) {cout<<"Accept Error!"<<endl;continue;}else if (!full_check(Client_id)) //压入 请求队列最短的线程 处理{pthread_mutex_lock(&lock);Client_id[find_shortest_deque(Client_id)].push_back(id);pthread_mutex_unlock(&lock);}else  //队列满,放弃处理 关闭套接字close(id);}if(exit_flag==true)  //队列不空不能关闭break; //关闭服务器}close(listener);return 0;
}void *pthread_start_fcn(void *arg)
{int thread_no=*(int*)arg;while(1){if(!Client_id[thread_no].empty()) //队列非空{//上锁pthread_mutex_lock(&lock);int id = Client_id[thread_no][0];Client_id[thread_no].pop_front();pthread_mutex_unlock(&lock);//解锁--处理请求--char recv_data[512]={0};recv(id,recv_data,512,0);recv_data[511]='';cout<<recv_data<<endl;if(!strcmp(recv_data,"CloseServer")){exit_flag=true;cout<<"Kill:"<<endl;}elsesend(id,&recv_data,512,0);close(id);}else if(exit_flag == true){cout<<"T"<<thread_no<<endl;break;}}return NULL;
}int find_shortest_deque(vector<deque<int> > &Client_id)
{int shortest = 0;int min_req = MAX_REQ_NUM;for(int i=0;i<MAX_THREAD_NUM;i++){if(Client_id[i].size()<min_req){min_req = Client_id[i].size();shortest = i;}}return shortest;
}bool empty_check(vector<deque<int> > &Client_id)
{bool empty = true;for(int i=0;i<MAX_THREAD_NUM;i++)if(Client_id[i].size()>0){empty = false;break;}return empty;
}bool full_check(vector<deque<int> > &Client_id)
{bool full = true;for(int i=0;i<MAX_THREAD_NUM;i++)if(Client_id[i].size()<MAX_REQ_NUM){full = false;break;}return full;
}
/*
Client.3.0.windows. 并发回显测试Client端
须在Visual Studio的项目属性-->链接器-->命令行中输入"ws2_32.lib" 
*/
#include<winsock2.h>
#include<iostream>
#include<string.h>#pragma warning(disable : 4996)
#define REQ_NUM 50    //并发请求数量
char ip_addr[] = { "192.168.31.246" };
using namespace std;
int main(void)
{WORD socket_ver = MAKEWORD(2, 2);WSADATA data;if (WSAStartup(socket_ver, &data) != 0){cout << "WSAStartup failed!" << endl;return 0;}while (true){string input;cin >> input;if (!strcmp(input.c_str(), "CloseClient"))break;for (int i = 0; i < REQ_NUM; i++){SOCKET client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (client == INVALID_SOCKET){cout << "Socket Init Failure!" << endl;continue;}sockaddr_in server;server.sin_family = AF_INET;server.sin_port = htons(8080);server.sin_addr.S_un.S_addr = inet_addr(ip_addr);if (connect(client, (const sockaddr*)&server, sizeof(server)) == SOCKET_ERROR){cout << "Connect Failure!" << endl;break;}char sendmsg[255] = { '' };char no[10] = { '' };sprintf(no, "%d", i);strcat(sendmsg, "REQ_No.");strcat(sendmsg, no);strcat(sendmsg, " : ");strcat(sendmsg, input.c_str());send(client, sendmsg, 255, 0);char recvmsg[255] = {};recv(client, recvmsg, 255, 0);cout << "Server:" << endl << recvmsg << endl;closesocket(client);}}WSACleanup();return 0;
}

并发HTTP请求测试

/* --------------------------------------------------------
Server.V3.1.CentOS 多线程接受HTTP请求,分析URL,传输html文档
----------------------------------------------------------*/
#include <iostream>
#include <string.h>
#include <string>
#include <fstream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/errno.h>
#include <pthread.h>
#include <deque>
#include <vector>
using namespace std;#define ERROR ((int)(~0))
#define MAX_THREAD_NUM 4
#define MAX_REQ_NUM 512bool exit_flag = false;int thread_no[MAX_THREAD_NUM];
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
vector<deque<int>> Client_id(MAX_THREAD_NUM); //线程请求队列void *pthread_start_fcn(void *);               //线程启动函数
int find_shortest_deque(vector<deque<int>> &); //最短队列查找
bool empty_check(vector<deque<int>> &);        //空队列检测
bool full_check(vector<deque<int>> &);         //满队列检测
void send_failure(int, int);                   //向套接字发送 失败状态码
void cycle_deque(void);                        //队列轮转int main(void)
{//监听套接字int listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (listener == ERROR){cout << "Listenner Initiate Failure!" << endl;return 0;}struct sockaddr_in ServerAddr;ServerAddr.sin_family = AF_INET;ServerAddr.sin_port = htons(8080);ServerAddr.sin_addr.s_addr = INADDR_ANY;setsockopt(listener, SOL_SOCKET, SO_REUSEPORT, (char *)&ServerAddr, sizeof(ServerAddr));if (bind(listener, (struct sockaddr *)&ServerAddr, sizeof(ServerAddr)) == -1){cout << "Binding Failure!" << endl;return 0;}if (listen(listener, 5) == -1){cout << "Listening Initiation Failure!" << endl;return 0;}//初始化处理线程pthread_mutex_lock(&lock);for (int i = 0; i < MAX_THREAD_NUM; i++){thread_no[i] = i;pthread_t tmp;pthread_create(&tmp, NULL, pthread_start_fcn, &thread_no[i]);pthread_detach(tmp);}pthread_mutex_unlock(&lock);//开始接收套接字struct sockaddr_in ClientAddr;socklen_t ClientAddrLen = sizeof(ClientAddr);while (true){if (exit_flag == false){cout << &#" << endl;//--接收请求--//cout<<"Before Accept!"<<endl;int id = accept(listener, (struct sockaddr *)&ClientAddr, &ClientAddrLen);//cout<<"After Accept!"<<endl;if (id == ERROR){cout << "Accept Error!" << endl;continue;}else if (!full_check(Client_id)) //压入 请求队列最短的线程 处理{cycle_deque();pthread_mutex_lock(&lock);Client_id[thread_no[0]].push_back(id);   //循环轮转//Client_id[find_shortest_deque(Client_id)].push_back(id); //最短队列pthread_mutex_unlock(&lock);}else //队列满,放弃处理 关闭套接字{send_failure(id, 501);close(id);}}if (exit_flag == true) //队列不空不能关闭break;             //关闭服务器}close(listener);return 0;
}void *pthread_start_fcn(void *arg)
{int thread_no = *(int *)arg;while (1){if (!Client_id[thread_no].empty()) //队列非空{//上锁pthread_mutex_lock(&lock);int id = Client_id[thread_no][0];Client_id[thread_no].pop_front();pthread_mutex_unlock(&lock);//解锁--处理请求--char recv_data[512] = {0};recv(id, recv_data, 512, 0);recv_data[511] = '';cout << "Client:" << endl << recv_data << endl;//--处理请求--string fpath(recv_data);string::size_type start = fpath.find('/');string::size_type end = fpath.find(' ', start);string fname = fpath.substr(start + 1, end - start - 1); //文件名提却ifstream inFile;string content;inFile.open(fname.c_str(), ios::in);if (inFile) //条件成立,则说明文件打开成功{cout << "Succuss!" << endl;string tmp;while (inFile >> tmp)content = content + tmp;inFile.close();char sendData[1024] = {0};strcpy(sendData, "HTTP/1.1 202 OK!rncontent-type: text/html; charset=UTF-8rn");string label("REQ_No.");int pos = fpath.find(label);if(pos!=string::npos)  //使用字符串查找拍除浏览器访问的情况{string req_no = fpath.substr(pos,fpath.find(string("rn"),pos)-pos);strcat(sendData, req_no.c_str());}char no[10] = { '' };sprintf(no, "%d", thread_no);strcat(sendData, "rnThread_No. : ");  //客户端处理线程识别标识,勿改strcat(sendData, no);strcat(sendData, "rnrn");strcat(sendData, content.c_str());strcat(sendData, "nn");send(id, &sendData, 1024, 0);}else{if (!strcmp(fname.c_str(), "CloseServer")){exit_flag = true;cout << &#" << endl;char sendData[255]={"Input any thing to close Server:"};send(id, &sendData, 255, 0);}else{cout << "Failed" << endl;send_failure(id, 404);}}close(id);}else if (exit_flag == true)break;}return NULL;
}int find_shortest_deque(vector<deque<int>> &Client_id)
{int shortest = 0;int min_req = MAX_REQ_NUM;for (int i = 0; i < MAX_THREAD_NUM; i++){if (Client_id[i].size() < min_req){min_req = Client_id[i].size();shortest = i;}}return shortest;
}bool empty_check(vector<deque<int>> &Client_id)
{bool empty = true;for (int i = 0; i < MAX_THREAD_NUM; i++)if (Client_id[i].size() > 0){empty = false;break;}return empty;
}bool full_check(vector<deque<int>> &Client_id)
{bool full = true;for (int i = 0; i < MAX_THREAD_NUM; i++)if (Client_id[i].size() < MAX_REQ_NUM){full = false;break;}return full;
}void send_failure(int id, int state)
{char httpHeader[255] = {0};strcpy(httpHeader, "HTTP/1.1 ");char state_char[10] = {0};sprintf(state_char, "%d", state);strcat(httpHeader, state_char);strcat(httpHeader, " Failed!rnrn");send(id, &httpHeader, 255, 0);return;
}void cycle_deque(void)
{int tmp=thread_no[0];for (int i=0;i<MAX_THREAD_NUM-1;i++)thread_no[i]=thread_no[i+1];thread_no[MAX_THREAD_NUM-1]=tmp;return;
}
/*
Client.3.1.windows. 并发HTTP请求测试Client端
须在Visual Studio的项目属性-->链接器-->命令行中输入"ws2_32.lib" 
*/
#include<winsock2.h>
#include<iostream>
#include<string.h>
#include<string>
#include<thread>
#include<deque>#pragma warning(disable : 4996)
#define REQ_NUM 50char ip_addr[] = { "192.168.31.246" };
using namespace std;
deque<SOCKET> Client_id;
void recv_thread(); //接收线程函数
bool exit_flag = false;
bool send_flag = false;
string recv_content;int main(void)
{WORD socket_ver = MAKEWORD(2, 2);WSADATA data;if (WSAStartup(socket_ver, &data) != 0){cout << "WSAStartup failed!" << endl;return 0;}thread recv_thread(recv_thread);recv_thread.detach();while (true){string input;cin >> input;if (!strcmp(input.c_str(), "CloseClient")){exit_flag = true;break;}// http 头部char httpheader[255] = { 0 };strcat(httpheader, "GET /");strcat(httpheader, input.c_str());strcat(httpheader, " HTTP/1.1rn");strcat(httpheader, "Host: ");strcat(httpheader, ip_addr);strcat(httpheader, "rnConnection: keep-alivern");for (int i = 0; i < REQ_NUM; i++){SOCKET client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (client == INVALID_SOCKET){cout << "Socket Init Failure!" << endl;continue;}sockaddr_in server;server.sin_family = AF_INET;server.sin_port = htons(8080);server.sin_addr.S_un.S_addr = inet_addr(ip_addr);if (connect(client, (const sockaddr*)&server, sizeof(server)) == SOCKET_ERROR){cout << "Connect Failure!" << endl;break;}char sendmsg[1024] = { '' };strcat(sendmsg, httpheader);char no[10] = { '' };sprintf(no, "%d", i);strcat(sendmsg, "REQ_No."); //用于Server中识别请求计数labelstrcat(sendmsg, no);strcat(sendmsg, "rnrn");send(client, sendmsg, 1024, 0);Client_id.push_back(client);if (!strcmp(input.c_str(), "CloseServer"))break;}send_flag = true;//发送完毕}WSACleanup();return 0;
}void recv_thread(void)
{while (exit_flag == false){if (Client_id.size()>0){SOCKET client = Client_id.front();Client_id.pop_front();char recvmsg[1024] = { '' }; recv(client, recvmsg, 1024, 0);  //recv()阻塞string tmp = string(recvmsg);int pos = tmp.find("Thread_No");if (pos != string::npos)recv_content = recv_content + tmp.substr(0, tmp.find("rn", pos))+"nnn";elserecv_content = recv_content + tmp;closesocket(client);}else if(send_flag) //接受完Client_id中请求的响应后{send_flag = false;cout << "Server:" << endl << recv_content << endl;}}return;
}

进程间通信

IPC(interprocess communication),操作系统上不同进程间的消息传递(message passing),同步(synchronization)

  • 消息传递发展历程:
    管道(pipe)
    System V message queue
    Posix 消息队列
    远程过程调用(Remote Procedure Call,RPC)

  • 同步方式发展历程:
    文件
    记录上锁(record locking)
    System V semaphore / System V shared memory
    Posix semaphore / Posix shared memory
    mutex / condition variable
    read-write lock

数据库

使用HTTP协议操作sqlite3数据库
类unix系统内置sqlite3,使用>>man sqlite3等相关命令确认
本文暂不涉及高并发读写,在了解原子性的实现后再做补充
Server端需要下载sqlite3数据库的源码,即sqlite3.c和sqlite3.h两个文件(官方)

/* --------------------------------------------------------
Server.V4.0.CentOS 多线程接受HTTP请求,分析URL,传输html文档,接收SQL数据库操作请求
----------------------------------------------------------*/
#include <iostream>
#include <string.h>
#include <string>
#include <fstream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/errno.h>
#include <pthread.h>
#include <deque>
#include <vector>
#include <stdlib.h>
#include"sqltest/sqlite3.h"using namespace std;#define ERROR ((int)(~0))
#define MAX_THREAD_NUM 2
#define MAX_REQ_NUM 512bool exit_flag = false;int thread_no[MAX_THREAD_NUM];
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
vector<deque<int> > Client_id(MAX_THREAD_NUM); //线程请求队列
sqlite3 *db;                                   //打开的数据库void *pthread_start_fcn(void *);               //线程启动函数
int find_shortest_deque(vector<deque<int> > &); //最短队列查找
bool empty_check(vector<deque<int> > &);        //空队列检测
bool full_check(vector<deque<int> > &);         //满队列检测
void send_failure(int, int);                   //向套接字发送 失败状态码
void cycle_deque(void);                        //队列轮转
int sql_callback(void* a,int b,char**d,char**e); //sql回调函数
void URIdecode(string &sqlcmd);                    //URI解码int main(void)
{int rc;//建立打开数据库文件rc = sqlite3_open("sqltest.db", &db);if( rc ){fprintf(stderr, "Can't open database: %sn", sqlite3_errmsg(db));exit(0);}else{fprintf(stderr, "Opened database successfullyn");}//监听套接字int listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (listener == ERROR){cout << "Listenner Initiate Failure!" << endl;return 0;}struct sockaddr_in ServerAddr;ServerAddr.sin_family = AF_INET;ServerAddr.sin_port = htons(8080);ServerAddr.sin_addr.s_addr = INADDR_ANY;setsockopt(listener, SOL_SOCKET, SO_REUSEPORT, (char *)&ServerAddr, sizeof(ServerAddr));if (bind(listener, (struct sockaddr *)&ServerAddr, sizeof(ServerAddr)) == -1){cout << "Binding Failure!" << endl;return 0;}if (listen(listener, 5) == -1){cout << "Listening Initiation Failure!" << endl;return 0;}//初始化处理线程pthread_mutex_lock(&lock);for (int i = 0; i < MAX_THREAD_NUM; i++){thread_no[i] = i;pthread_t tmp;pthread_create(&tmp, NULL, pthread_start_fcn, &thread_no[i]);pthread_detach(tmp);}pthread_mutex_unlock(&lock);//开始接收套接字struct sockaddr_in ClientAddr;socklen_t ClientAddrLen = sizeof(ClientAddr);while (true){if (exit_flag == false){cout << &#" << endl;//--接收请求--//cout<<"Before Accept!"<<endl;int id = accept(listener, (struct sockaddr *)&ClientAddr, &ClientAddrLen);//cout<<"After Accept!"<<endl;if (id == ERROR){cout << "Accept Error!" << endl;continue;}else if (!full_check(Client_id)) //压入 请求队列最短的线程 处理{pthread_mutex_lock(&lock);Client_id[thread_no[0]].push_back(id);   //循环轮转//Client_id[find_shortest_deque(Client_id)].push_back(id); //最短队列pthread_mutex_unlock(&lock);cycle_deque();}else //队列满,放弃处理 关闭套接字{send_failure(id, 501);close(id);}}if (exit_flag == true) //队列不空不能关闭break;             //关闭服务器}sqlite3_close(db);  //关闭数据库文件close(listener);return 0;
}void *pthread_start_fcn(void *arg)
{int thread_no = *(int *)arg;while (true){if (!Client_id[thread_no].empty()) //队列非空{//上锁pthread_mutex_lock(&lock);int id = Client_id[thread_no][0];Client_id[thread_no].pop_front();pthread_mutex_unlock(&lock);//解锁--处理请求--char recv_data[512] = {0};recv(id, recv_data, 512, 0);recv_data[511] = '';cout << "Client:" << endl << recv_data << endl;//--处理请求--string fpath(recv_data);string::size_type start = fpath.find('/');string::size_type end = fpath.find(' ', start);string fname = fpath.substr(start + 1, end - start - 1); //文件名提却if(!strcmp(fname.substr(0,3).c_str(),"sql")) //sql命令{char sendData[1024] = {0};strcpy(sendData, "HTTP/1.1 202 OK!rncontent-type: text/html; charset=UTF-8rnrn");char no[10] = { '' };sprintf(no, "%d", thread_no);strcat(sendData, "Thread_No. : "); strcat(sendData, no);strcat(sendData, "<br>Operation:");//sql命令提取string sqlcmd=fname.substr(4,fname.find('/',4));URIdecode(sqlcmd);strcat(sendData, sqlcmd.c_str());char *zErrMsg = 0;int rc = sqlite3_exec(db, sqlcmd.c_str(), sql_callback, NULL, &zErrMsg);if( rc != SQLITE_OK ){strcat(sendData, "<br>failed !");strcat(sendData, "<br>SQL error : <br>");strcat(sendData, zErrMsg);cout<<"failed !"<<endl<<zErrMsg<<endl;sqlite3_free(zErrMsg);}else{strcat(sendData, "<br>Done!");cout<<"sql success!"<<endl;}send(id, &sendData, 1024, 0);}else if(!strcmp(fname.c_str(), "CloseServer")){exit_flag = true;cout << &#" << endl;char sendData[255]={"Input any thing to close Server:"};send(id, &sendData, 255, 0);}else{ifstream inFile;string content;inFile.open(fname.c_str(), ios::in);if (inFile) //条件成立,则说明文件打开成功{cout << "Succuss!" << endl;string tmp;while (inFile >> tmp)content = content + tmp;inFile.close();char sendData[1024] = {0};strcpy(sendData, "HTTP/1.1 202 OK!rncontent-type: text/html; charset=UTF-8rn");string label("REQ_No.");int pos = fpath.find(label);if(pos!=string::npos)  //使用字符串查找拍除浏览器访问的情况{string req_no = fpath.substr(pos,fpath.find(string("rn"),pos)-pos);strcat(sendData, req_no.c_str());}char no[10] = { '' };sprintf(no, "%d", thread_no);strcat(sendData, "rnThread_No. : ");  //客户端处理线程识别标识,勿改strcat(sendData, no);strcat(sendData, "rnrn");strcat(sendData, content.c_str());strcat(sendData, "nn");send(id, &sendData, 1024, 0);}else{cout << "Failed" << endl;send_failure(id, 404);}}close(id);}else if (exit_flag == true)break;}return NULL;
}int find_shortest_deque(vector<deque<int> > &Client_id)
{int shortest = 0;int min_req = MAX_REQ_NUM;for (int i = 0; i < MAX_THREAD_NUM; i++){if (Client_id[i].size() < min_req){min_req = Client_id[i].size();shortest = i;}}return shortest;
}bool empty_check(vector<deque<int> > &Client_id)
{bool empty = true;for (int i = 0; i < MAX_THREAD_NUM; i++)if (Client_id[i].size() > 0){empty = false;break;}return empty;
}bool full_check(vector<deque<int> > &Client_id)
{bool full = true;for (int i = 0; i < MAX_THREAD_NUM; i++)if (Client_id[i].size() < MAX_REQ_NUM){full = false;break;}return full;
}void send_failure(int id, int state)
{char httpHeader[255] = {0};strcpy(httpHeader, "HTTP/1.1 ");char state_char[10] = {0};sprintf(state_char, "%d", state);strcat(httpHeader, state_char);strcat(httpHeader, " Failed!rnrn");send(id, &httpHeader, 255, 0);return;
}void cycle_deque(void)
{int tmp=thread_no[0];for (int i=0;i<MAX_THREAD_NUM-1;i++)thread_no[i]=thread_no[i+1];thread_no[MAX_THREAD_NUM-1]=tmp;return;
}int sql_callback(void *data, int argc, char **argv, char **azColName)
{int i;fprintf(stderr, "%s: ", (const char*)data);for(i=0; i<argc; i++){printf("%s = %sn", azColName[i], argv[i] ? argv[i] : "NULL");}printf("n");return 0;
}void URIdecode(string &sqlcmd)
{for(int pos=sqlcmd.find("%20",4);pos!=string::npos;pos=sqlcmd.find("%20",pos)){place(pos,3," ");pos++;}for(int pos=sqlcmd.find("%3E",4);pos!=string::npos;pos=sqlcmd.find("%3E",pos)){place(pos,3,">");pos++;}for(int pos=sqlcmd.find("%3C",4);pos!=string::npos;pos=sqlcmd.find("%3C",pos)){place(pos,3,"<");pos++;}for(int pos=sqlcmd.find("%22",4);pos!=string::npos;pos=sqlcmd.find("%22",pos)){place(pos,3,""");pos++;}d()-1);return;
}

使用浏览器输入URL即可交由Server解析sql命令,从Client端操作Server端数据库
IP:d/
举例
192.168.31.201:ate table A (name char,age int);/
192.168.31.201:8080/sql.select * from A where age>25;/

本文发布于:2024-02-03 05:36:47,感谢您对本站的认可!

本文链接:https://www.4u4v.net/it/170690980548994.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:Demo   HTTP
留言与评论(共有 0 条评论)
   
验证码:

Copyright ©2019-2022 Comsenz Inc.Powered by ©

网站地图1 网站地图2 网站地图3 网站地图4 网站地图5 网站地图6 网站地图7 网站地图8 网站地图9 网站地图10 网站地图11 网站地图12 网站地图13 网站地图14 网站地图15 网站地图16 网站地图17 网站地图18 网站地图19 网站地图20 网站地图21 网站地图22/a> 网站地图23