当前位置: 首页 > wzjs >正文

深圳正规做网站的公司网站建设公司哪个好呀

深圳正规做网站的公司,网站建设公司哪个好呀,网站建设 常见问题,开发安卓软件需要学什么1.HTTP协议 虽然我们说,应用层协议是我们程序猿自己定的.但实际上,已经有大佬们定义了一些现成的,又非常好用的应用层协议,供我们直接参考使用.HTTP(超文本传输协议)就是其中之一。 在互联网世界中,HTTP(HyperTextTransfer Protocol,超文本…

1.HTTP协议

虽然我们说,应用层协议是我们程序猿自己定的.但实际上,已经有大佬们定义了一些现成的,又非常好用的应用层协议,供我们直接参考使用.HTTP(超文本传输协议)就是其中之一。

在互联网世界中,HTTP(HyperTextTransfer Protocol,超文本传输协议)是一个至关重要的协议。它定义了客户端(如浏览器)与服务器之间如何通信,以交换或传输超文本(如HTML文档)。

HTTP协议是客户端与服务器之间通信的基础。客户端通过HTTP协议向服务器发送请求,服务器收到请求后处理并返回响应。HTTP协议是一个无连接、无状态的协议,即每次请求都需要建立新的连接,且服务器不会保存客户端的状态信息。

2. HTTP 协议的工作过程

当我们在浏览器输入一个网址,此时浏览器就会给对应的服务器发送一个 HTTP 请求,对应的服务器收到这个请求之后,经过计算处理,就会返回一个 HTTP 响应。并且当我们访问一个网站时,可能涉及不止一次的 HTTP 请求和响应的交互过程。

基础术语:

  1. 客户端: 主动发起网络请求的一端
  2. 服务器: 被动接收网络请求的一端
  3. 请求: 客户端给服务器发送的数据
  4. 响应: 服务器给客户端返回的数据

HTTP 协议的重要特点: 一发一收,一问一答
在这里插入图片描述
注意: 网络编程中,除了一发一收之外,还有其它的模式

多发一收:例如上传大文件
一发多收:例如看直播时,搜索一个词条可以得到多个视频源
多发多收:例如串流(steam link、moonlight 等等)

3. Fiddler 抓包工具介绍

3.1 抓包工具的使用
当我们访问一个网站时,可能涉及不止一次的 HTTP 请求和响应的交互,为此为了更加清楚的了解我们访问一个网站时 HTTP 请求/协议是怎么交互的,由于 HTTP 是一个文本格式的协议,就可以通过以下两种方式:

方式一: 通过 F12 打开浏览器的开发者工具,点击 Network 标签页,然后刷新页面就行。显示的每一条记录都是一次 HTTP 请求/响应
方式二(推荐): 抓包工具,这里以 Fiddler 为例,它能够直接读取你电脑上网卡的信息,网卡上有什么数据流动,它都能感知到并且显示出来
Fiddler 使用方式:

打开 Fiddler,然后打开你要访问的网站,访问该网站的 HTTP 请求和响应就会显示在 Fiddler 上

urlencode 和urldecode
像/?:等这样的字符,已经被url当做特殊意义理解了.因此这些字符不能随意出现.
比如,某个参数中需要带有这些特殊字符,就必须先对特殊字符进行转义.
转义的规则如下:
将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位
做一位,前面加上%,编码成%XY格式

在这里插入图片描述
当我们选择好我们具体要查看的 HTTP/HTTPS 请求和响应时,右上栏就会显示具体的请求报文内容,右下角就会显示具体的响应报文内容(需要点击 Raw 标签来查看详细的数据格式)

请求和响应的详细数据,可以通过右下角的 view in Notepad 键通过记事本打开

为了方便我们抓取我们自己想查看的抓包结果,可以通过 ctrl + a 全选左侧的抓包结果,ctrl + x 删除选中的所有抓包结果,再进行网页的刷新就行

响应的正文往往是被显示在浏览器上,最常见的响应格式就是 html,很多网站返回的 html 是压缩过的(因为压缩之后,网络传输的数据量就变少了,即节省了网络带宽),所以需要进行解压缩 decode
在这里插入图片描述
在这里插入图片描述

4.HTTP协议请求与响应格式

HTTP请求
首行:[方法]+[url]+[版本]
• Header:请求的属性,冒号分割的键值对;每组属性之间使用\r\n分隔;遇到空行
表示Header部分结束
• Body:空行后面的内容都是Body.Body允许为空字符串.如果Body存在,则在Header 中会有一个Content-Length属性来标识Body的长度;
在这里插入图片描述

在这里插入图片描述

5.认识“方法”(method)

HTTP 中的方法,就是 HTTP 请求报文中的首行的第一个部分。

原本 HTTP 的设计者,是希望通过不同的方法,来表达不同的语义。但是至今,其实也没有被实现,以下具体的方法具体起到了什么作用,完全看程序员在代码中是如何实现的。

虽然 HTTP 中的方法很多,但是最常用的就两个 GET 和 POST。以下主要介绍这两个方法
在这里插入图片描述
GET 方法
基本介绍:

GET 是最常用的 HTTP 方法,常用于获取服务器上的某个资源。以下几种方式都会触发 GET 方法的请求

在浏览器中直接输入 URL 回车或点击浏览器收藏夹中的链接,此时浏览器就会发送出一个 GET 请求。
HTML 中的 link、img、script 等标签的属性中放的 URL,浏览器也会构造出 HTTP GET 请求
使用 Javascript 重点 ajax,也能构造出 HTTP GET 请求
各种编程语言(只要能够访问网络),就都能够构造出 HTTP GER 请求
GET 请求的特点:

首行里面的第一个部分就是 GET
URL 里面的 query string 可以为空,也可以不为空
GET 请求的 header 有若干个键值对结构
GET 请求的 body 一般是空的
POST 方法
基本介绍:

POST 方法也是一种常见的方法,多用于提交用户输入的数据给服务器(如登录页面)。以下几种方法都会触发 POST 方法的请求

通过 HTML 中的 form 标签可以构造 POST 请求
使用 JavaScript 的 ajax 可以构造 POST 请求
POST 请求的特点:

首行第一个部分就是 POST
URL 里面的 query string 一般是空的
POST 请求的 header 里面有若干个键值对
POST 请求的 body 一般不为空(body 的具体数据格式,由 header 中的 Content-Type 来描述;body 的具体数据长度,由 header 中的 Content-Length 来描述

GET 和 POST 的区别
其实 GET 和 POST 的区别是一个经典的面试题,以下为大家介绍如何在面试中回答上述问题

答题模板:

GET 和 POST 其实没有本质区别,使用 GET 的场景完全可以使用 POST 代替,使用 POST 的场景一样可以使用 GET 代替。但是在具体的使用上,还是存在一些细节的区别

GET 习惯上会把客户端的数据通过 query string 来传输(body 部分是空的);POST 习惯上会把客户端的数据通过 body 来传输(query string 部分是空的)
GET 习惯上用于从服务器获取数据;POST 习惯上是客户端给服务器提交数据
一般情况,程序员会把 GET 请求的处理,实现成“幂等”的;对于 POST 请求的处理,不要求实现成“幂等”
GET 请求可以被缓存,可以被浏览器保存到收藏夹中;POST 请求不能被缓存

#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <unordered_map>
#include <fstream>
#include <memory>
#include "Log.hpp"
const std::string Separator = "\r\n";                                                      // 常量定义,表示回车换行符
static const std::string sep = "\r\n";                                                     // 静态常量定义,表示回车换行符
static const std::string header_sep = ": ";                                                // 静态常量定义,表示冒号和空格
static const std::string wwwroot = "wwwroot";                                              // 静态常量定义,表示wwwroot目录
static const std::string homepage = "index.html";                                          // 静态常量定义,表示首页文件名
static const std::string httpversion = "HTTP/1.0";                                         // 静态常量定义,表示HTTP版本
static const std::string httpversion = "HTTP/1.0";                                         // 静态常量定义,表示HTTP版本
static const std::string space = " ";                                                      // 静态常量定义,表示空格
static const std::string filesuffixsep = ".";                                              // 静态常量定义,表示文件后缀分隔符
using func_t = std::function<std::shared_ptr<HttpResponse>(std::shared_ptr<HttpRequest>)>; // 定义回调函数类型
using func_t = std::function<std::shared_ptr<HttpResponse>(std::shared_ptr<HttpRequest>)>;
using func_t = std::function<std::shared_ptr<HttpResponse>(std::shared_ptr<HttpRequest>)>; // 定义回调函数类型
class HttpRequest                                                                          // 定义一个HTTP请求类
{
private:std::string GetOneLine(std::string &str) // 读取一行数据{if (str.empty()){return std::string(); // 如果字符串为空,则返回空字符串}auto pos = str.find(Separator); // 查找回车换行符if (pos == std::string::npos)   // 如果没有找到回车换行符,则返回空字符串{return std::string();}std::string line = str.substr(0, pos);  // 截取一行数据str.erase(0, pos + Separator.length()); // 删除已读取的字符串return line.empty() ? Separator : line; // 如果一行数据为空,则返回回车换行符,否则返回一行数据}bool PraseHeaderHelper(const std::string &line, std::string *key, std::string *value) // 解析请求头{auto pos = line.find(':'); // 查找冒号if (pos == std::string::npos){return false; // 如果没有找到冒号,则返回false}*key = line.substr(0, pos);    // 截取键*value = line.substr(pos + 1); // 截取值}bool PraseRequestLine(){if (_req_line.empty()){return false; // 如果请求行为空,则返回false}std::stringstream ss(_req_line);      // 字符串流ss >> _method >> _url >> _version;    // 解析请求行_path += _url;                        // 路径if (_path[_path.length() - 1] == '/') // 如果URL以/结尾,则添加index.html{// TODO: 这里需要添加index.html的处理逻辑_path += homepage; // 这里需要添加index.html的处理逻辑}auto pos = _path.rfind(filesuffixsep); // rfind表示从右边开始查找,找到最后一个.的位置if (pos == std::string::npos){_suffix = ".html"; // 如果没有后缀名,则默认为.html}else{_suffix = _path.substr(pos); // 截取后缀名}LOG(INFO, "client wang get %s\n", _path.c_str()); // 打印日志return true;                                      // 解析成功}bool PraseHeader() // 解析请求头{for (const auto &line : _headers) // 遍历请求头{std::string key, value; // 定义键值对if (PraseHeaderHelper(line, &key, &value)){_header.insert(std::make_pair(key, value)); // 插入键值对}}}void Print() // 打印HTTP请求{std::cout << "===" << _req_line << "===" << std::endl; // 打印请求行for (const auto &header : _headers){std::cout << "***" << header << "***" << std::endl; // 打印请求头}std::cout << _Null_String << std::endl;                     // 打印请求正文;std::cout << "===" << _resq_text << "===" << std::endl;     // 打印响应std::cout << "method ###" << _method << "###" << std::endl; // 打印请求方法std::cout << "url ###" << _url << "###" << std::endl;       // 打印URLstd::cout << "path ###" << _path << "###" << std::endl;     // 打印路径std::cout << "args ###" << _args << "###" << std::endl;     // 打印参数}public:HttpRequest(){}void Serialization() // 序列化HTTP请求{}void DeSerialization(std::string &request) // 反序列化HTTP请求{_req_line = GetOneLine(request); // 读取请求行while (true){std::string line = GetOneLine(request); // 读取请求头if (line.empty()){break; // 读取到空行,则表示请求头结束}else if (line == Separator){_resq_text = request; // 读取请求正文;break;                // 读取到空行,则表示请求头结束}else{_headers.push_back(line); // 读取请求头}}}std::string Path() // 获取文件路径{return _path; // 为什么要获取文件路径?->为了找到文件并返回给客户端}std::string Suffix() // 获取文件后缀名{return _suffix;}std::string Method() // 获取请求方法{return _method;}std::string Args() // 获取参数{return _args;}std::string Text(){return _resq_text; // 读取请求正文;}~HttpRequest(){}private:std::string _req_line;                                // 请求行std::vector<std::string> _headers;                    // 请求头std::string _Null_String;                             // 空行std::string _resq_text;                               // 请求正文;std::unordered_map<std::string, std::string> _header; // 请求头std::string _method;  // 请求方法std::string _url;     // URLstd::string _args;    // 参数std::string _path;    // 路径std::string _suffix;  // 后缀名std::string _version; // HTTP版本
};class HttpResponse // 定义一个HTTP响应类
{
public:HttpResponse(): _version(httpversion),_code(200),_Null_String(Separator){}void AddStatusLine(int code, std::string message) // 添加状态行{_code = code;_message = message; // TODO: 这里需要添加响应信息_desc = "OK";       // TODO: 这里需要添加描述信息}void AddHeader(const std::string &key, const std::string &value) // 添加响应头{_header.insert(std::make_pair(key, value)); // 插入响应头_header[key] = value;                       // 插入响应头}void AddText(const std::string &text) // 添加响应内容{_resq_text = text; // 响应内容}std::string Serialize() // 序列化HTTP响应{std::string _status_line = _version + space + std::to_string(_code) + space + _desc + Separator; // 状态行for (const auto &header : _header){_headers.push_back(header.first + header_sep + header.second + Separator); // 响应头}// 序列化;std::string respstr = _status_line; // 状态行for (const auto &header : _headers){respstr += header; // 响应头}respstr += _Null_String; // 空行respstr += _resq_text;   // 响应内容return respstr;          // 序列化;}~HttpResponse(){}private:int _code;                                            // 响应代码std::string _desc;                                    // 响应描述std::string _message;                                 // 响应信息std::string _content;                                 // 响应内容std::string _version;                                 // HTTP版本std::unordered_map<std::string, std::string> _header; // 响应头std::string _req_line;                                // 请求行std::vector<std::string> _headers;                    // 请求头std::string _Null_String;                             // 空行std::string _resq_text;                               // 请求正文;
};class Factory // 定义一个工厂类
{
public:// 静态成员函数// 1.所有对象共享同一个函数// 2.静态成员函数只能访问静态成员变量// 3.静态成员函数也有访问权限 写在private里,类外是访问不到的static std::shared_ptr<HttpRequest> BuildHttpRequest() // 静态工厂函数,用于创建HTTP请求对象{return std::make_shared<HttpRequest>(); // 实例化一个HTTP请求对象并返回}static std::shared_ptr<HttpResponse> BuildHttpReponse() // 静态工厂函数,用于创建HTTP响应对象{return std::make_shared<HttpResponse>(); // 实例化一个HTTP响应对象并返回}
};class HttpServer // 定义一个HTTP服务器类
{
public:HttpServer(){_mime_type.insert(std::make_pair(".html", "text/html"));              // 定义文件类型_mime_type.insert(std::make_pair(".css", "text/css"));                // 定义文件类型_mime_type.insert(std::make_pair(".js", "application/x-javascript")); // 定义文件类型_mime_type.insert(std::make_pair(".png", "image/png"));               // 定义文件类型_mime_type.insert(std::make_pair(".jpg", "image/jpeg"));              // 定义文件类型_mime_type.insert(std::make_pair(".unknown", "text/html"));           // 定义文件类型_code_to_desc.insert(std::make_pair(100, "Continue"));              // 定义响应描述_code_to_desc.insert(std::make_pair(200, "OK"));                    // 定义响应描述_code_to_desc.insert(std::make_pair(301, "Moved Permanently"));     // 定义响应描述_code_to_desc.insert(std::make_pair(302, "Found"));                 // 定义响应描述_code_to_desc.insert(std::make_pair(404, "Not Found"));             // 定义响应描述_code_to_desc.insert(std::make_pair(500, "Internal Server Error")); // 定义响应描述}std::string ReadFileContent(const std::string path, int *Size) // 读取文件内容{std::ifstream ifs(path, std::ios::binary); // 打开文件if (!ifs.is_open()){return std::string(); // 打开失败,返回空字符串}ifs.seekg(0, ifs.end);      // 移动文件指针到文件末尾int filesize = ifs.tellg(); // 获取文件大小ifs.seekg(0, ifs.beg);      // 移动文件指针到文件开头std::string content;content.resize(filesize); // 预分配内存ifs.read((char *)content.c_str(), filesize);*Size = filesize; // 保存文件大小ifs.close();      // 关闭文件}std::string HandlerHttpReuqest(std::string request) // 处理HTTP请求的函数{
#ifdef TESTstd::cout << "HTTP Request: " << request << std::endl;       // 打印HTTP请求std::string response = "HTTP/1.0 200 OK\r\n";                // 200 OKresponse += "\r\n";                                          // 空行response += "<html><boby><h1>Hello C++!</h1></body></html>"; // 响应内容return response;
#else// return "HTTP/1.0 404 Not Found\r\n\r\n<html><body><h1>404 Not Found</h1></body></html>";std::shared_ptr<HttpRequest> req = Factory::BuildHttpRequest(); // 创建HTTP请求对象req->DeSerialization(request);                                  // 反序列化HTTP请求int content_size = 0;std::string content = ReadFileContent(req->Path(), &content_size); // 读取文件内容std::string suffix = req->Suffix();                                // 获取文件后缀名std::shared_ptr<HttpResponse> res = Factory::BuildHttpReponse();   // 创建HTTP响应对象res->AddStatusLine(200, "OK");                                     // 添加状态行res->AddHeader("Content-Type", _mime_type[suffix]);                // 添加响应头res->AddHeader("Content-Length", std::to_string(content_size));    // 添加响应头res->AddHeader("Location", "https://www.baidu.com");               // 添加响应头,重定向res->AddText(content);                                             // 添加响应内容return res->Serialize();                                           // 序列化HTTP响应int code = 0;                                                      // 响应代码if (req->Path() == "wwwroot/index.html"){code = 301;                                          // 重定向res->AddHeader("Location", "https://www.baidu.com"); // 添加响应头,重定向res->AddHeader("Content-Type", "text/html");         // 添加响应头}else{code = 200;                                                     // 200 OKint content_size = 0;                                           // 响应内容大小std::string text = ReadFileContent(req->Path(), &content_size); // 读取文件内容res->AddHeader("Content_length", std::to_string(content_size)); // 添加响应头res->AddText(text);if (text.empty()) // 如果文件内容为空,则返回404//文件内容为空表示文件不存在{code = 404;res->AddStatusLine(code, _code_to_desc[code]);                            //     添加状态行std::string text404 = ReadFileContent("wwwroot/404.html", &content_size); // 读取404页面内容res->AddHeader("Content-Length", std::to_string(content_size));           // 添加响应头res->AddHeader("Content-Type", _mime_type[".html"]);                      // 添加响应头res->AddText(text404);                                                    // 添加响应内容}else{std::string suffix = req->Suffix();                             // 获取文件后缀名res->AddStatusLine(code, _code_to_desc[code]);                  // 添加状态行res->AddHeader("Content-Length", std::to_string(content_size)); // 添加响应头res->AddText(text);                                             // 添加响应内容res->AddHeader("Content-Type", _mime_type[suffix]);             // 添加响应头}return res->Serialize(); // 序列化HTTP响应}
#endif}~HttpServer(){}private:int _code;                                               // 响应代码std::string _message;                                    // 响应信息std::string _content;                                    // 响应内容std::string _version;                                    // HTTP版本std::unordered_map<std::string, std::string> _header;    // 响应头std::string _req_line;                                   // 请求行std::vector<std::string> _headers;                       // 请求头std::string _Null_String;                                // 空行std::string _resq_text;                                  // 请求正文;std::unordered_map<std::string, std::string> _mime_type; // _mime_type表示文件类型,为什么要有文件类型->因为HTTP协议是基于文本的协议,所以需要知道文件类型才能知道如何解析std::unordered_map<int, std::string> _code_to_desc;      // _code_to_desc表示响应描述
};
#pragma once#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>class InetAddr
{
private:void GetAddress(std::string *ip, uint16_t *port){*port = ntohs(_addr.sin_port);*ip = inet_ntoa(_addr.sin_addr);}public:InetAddr(const struct sockaddr_in &addr) : _addr(addr){GetAddress(&_ip, &_port);}InetAddr(const std::string &ip, uint16_t port) : _ip(ip), _port(port){_addr.sin_family = AF_INET;_addr.sin_port = htons(_port);_addr.sin_addr.s_addr = inet_addr(_ip.c_str());}InetAddr(){}std::string Ip(){return _ip;}bool operator==(const InetAddr &addr){// if(_ip == addr._ip)if (_ip == addr._ip && _port == addr._port) // 方便测试{return true;}return false;}// bool operator = (const struct sockaddr_in &addr)// {//     _addr = addr;// }struct sockaddr_in Addr(){return _addr;}uint16_t Port(){return _port;}~InetAddr(){}private:struct sockaddr_in _addr;std::string _ip;uint16_t _port;
};
#ifndef __LOCK_GUARD_HPP__
#define __LOCK_GUARD_HPP__#include <iostream>
#include <pthread.h>class LockGuard
{
public:LockGuard(pthread_mutex_t *mutex) : _mutex(mutex){pthread_mutex_lock(_mutex); // 构造加锁}~LockGuard(){pthread_mutex_unlock(_mutex);}private:pthread_mutex_t *_mutex;
};#endif
#pragma once#include <iostream>
#include <fstream>
#include <cstdio>
#include <string>
#include <ctime>
#include <cstdarg>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include "LockGuard.hpp"bool gIsSave = false;
const std::string logname = "log.txt";// 1. 日志是由等级的
enum Level
{DEBUG = 0,INFO,WARNING,ERROR,FATAL
};void SaveFile(const std::string &filename, const std::string &message)
{std::ofstream out(filename, std::ios::app);if (!out.is_open()){return;}out << message;out.close();
}std::string LevelToString(int level)
{switch (level){case DEBUG:return "Debug";case INFO:return "Info";case WARNING:return "Warning";case ERROR:return "Error";case FATAL:return "Fatal";default:return "Unknown";}
}std::string GetTimeString()
{time_t curr_time = time(nullptr);struct tm *format_time = localtime(&curr_time);if (format_time == nullptr)return "None";char time_buffer[1024];snprintf(time_buffer, sizeof(time_buffer), "%d-%d-%d %d:%d:%d",format_time->tm_year + 1900,format_time->tm_mon + 1,format_time->tm_mday,format_time->tm_hour,format_time->tm_min,format_time->tm_sec);return time_buffer;
}pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
// 2. 日志是由格式的
// 日志等级 时间 代码所在的文件名/行数 日志的内容
void LogMessage(std::string filename, int line, bool issave, int level, const char *format, ...)
{std::string levelstr = LevelToString(level);std::string timestr = GetTimeString();pid_t selfid = getpid();char buffer[1024];va_list arg;va_start(arg, format);vsnprintf(buffer, sizeof(buffer), format, arg);va_end(arg);std::string message = "[" + timestr + "]" + "[" + levelstr + "]" +"[" + std::to_string(selfid) + "]" +"[" + filename + "]" + "[" + std::to_string(line) + "] " + buffer;LockGuard lockguard(&lock);// pthread_mutex_lock(&lock);if (!issave){std::cout << message;}else{SaveFile(logname, message);}// pthread_mutex_lock(&lock); // bug??// std::cout << levelstr << " : " << timestr << " : " << filename << " : " << line << ":" << buffer << std::endl;
}// C99新特性__VA_ARGS__
#define LOG(level, format, ...)                                                \do                                                                         \{                                                                          \LogMessage(__FILE__, __LINE__, gIsSave, level, format, ##__VA_ARGS__); \} while (0)#define EnableFile()    \do                  \{                   \gIsSave = true; \} while (0)
#define EnableScreen()   \do                   \{                    \gIsSave = false; \} while (0)// 默认传递进来的参数都是整数
// void Test(int num, ...)
// {
//     va_list arg;
//     va_start(arg, num);//     while(num)
//     {
//         int data = va_arg(arg, int);
//         std::cout << "data: " << data << std::endl;
//         num--;
//     }//     va_end(arg); // arg = NULL
// }
#include "TcpServer.hpp"
#include "Http.hpp"void Usage(std::string proc)
{std::cout << "Usage:\n\t" << proc << " local_port\n"<< std::endl;
}
std::shared_ptr<HttpResponse> Login(std::shared_ptr<HttpRequest> req)
{LOG(DEBUG, "=========================\n");std::string userdata;if (req->Method() == "GET"){userdata = req->Args();}else if (req->Method() == "POST"){userdata = req->Text(); // 获取请求体}else{}// 1. 进程间通信, 比如 pipe! 还有环境变量!// 2. fork();// 3. exec(); python / php / java / C++// 处理数据了LOG(DEBUG, "enter data handler, data is : %s\n", userdata.c_str());auto response = Factory::BuildHttpReponse();response->AddStatusLine(200, "OK");response->AddHeader("Content-Type", "text/html");response->AddText("<html><h1>handler data done</h1></html>");LOG(DEBUG, "=========================\n");return response;
}
int main(int argc, char *argv[])
{if (argc != 2){Usage(argv[0]);return 1;}uint16_t port = atoi(argv[1]);                                                                                // 获取端口号HttpServer http_service;                                                                                      // 实例化HTTP服务器类TcpServer tcp_server(port, std::bind(&HttpServer::HandlerHttpReuqest, &http_service, std::placeholders::_1)); // 实例化TCP服务器类,绑定HTTP请求处理函数return 0;
}
#pragma once#include <iostream>
#include <string>
#include <functional>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#include <pthread.h>
#include <sys/types.h>
#include <memory>
#include "InetAddr.hpp"
#include "Log.hpp"// 模版方法模式
namespace socket_ns
{class Socket;const static int gbacklog = 8;using socket_sptr = std::shared_ptr<Socket>;enum{SOCKET_ERROR = 1,BIND_ERROR,LISTEN_ERROR,USAGE_ERROR};// std::unique_ptr<Socket> listensock = std::make_unique<TcpSocket>();// listensock->BuildListenSocket();// std::unique_ptr<Socket> clientsock = std::make_unique<TcpSocket>();// clientsock->BuildClientSocket();// clientsock->send();// clientsock->Recv();class Socket{public:virtual void CreateSocketOrDie() = 0;             // 创建套接字virtual void BindSocketOrDie(InetAddr &addr) = 0; // 绑定本地地址virtual void ListenSocketOrDie() = 0;             // 监听套接字virtual socket_sptr Accepter(InetAddr *addr) = 0; // 接收客户端连接virtual bool Connetcor(InetAddr &addr) = 0;       // 连接服务器virtual void SetSocketAddrReuse() = 0;            // 设置套接字地址复用virtual int SockFd() = 0;                         // 获取套接字描述符virtual void SetSocketAddrReuse() = 0;            // 设置套接字地址复用->为什么要设置套接字地址复用?因为服务器程序在运行时,可能会多次启动,而端口号是临时端口,所以需要设置套接字地址复用,使得端口号可以被重复使用。virtual int Recv(std::string *out) = 0;           // 接收数据virtual int Send(const std::string &in) = 0;      // 发送数据virtual void Close() = 0;// virtual void Recv() = 0;// virtual void Send() = 0;// virtual void other() = 0;public:void BuildListenSocket(InetAddr &addr){CreateSocketOrDie();SetSocketAddrReuse();BindSocketOrDie(addr);ListenSocketOrDie();}bool BuildClientSocket(InetAddr &addr){CreateSocketOrDie();return Connetcor(addr);}// void BuildUdpSocket()// {//     CreateSocketOrDie();//     BindSocketOrDie();// }};class TcpSocket : public Socket{public:TcpSocket(int fd = -1) : _sockfd(fd){}void CreateSocketOrDie() override{// 1. 创建流式套接字_sockfd = ::socket(AF_INET, SOCK_STREAM, 0);if (_sockfd < 0){LOG(FATAL, "socket error");exit(SOCKET_ERROR);}LOG(DEBUG, "socket create success, sockfd is : %d\n", _sockfd);}void BindSocketOrDie(InetAddr &addr) override // 绑定本地地址{// 2. bindstruct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(addr.Port());local.sin_addr.s_addr = inet_addr(addr.Ip().c_str());int n = ::bind(_sockfd, (struct sockaddr *)&local, sizeof(local)); //_sockfd表示套接字描述符,(struct sockaddr *)&local表示要绑定的地址,sizeof(local)表示地址长度if (n < 0){LOG(FATAL, "bind error\n");exit(BIND_ERROR);}LOG(DEBUG, "bind success, sockfd is : %d\n", _sockfd);}void ListenSocketOrDie() override{int n = ::listen(_sockfd, gbacklog);if (n < 0){LOG(FATAL, "listen error\n");exit(LISTEN_ERROR);}LOG(DEBUG, "listen success, sockfd is : %d\n", _sockfd);}socket_sptr Accepter(InetAddr *addr) override{struct sockaddr_in peer;socklen_t len = sizeof(peer);int sockfd = ::accept(_sockfd, (struct sockaddr *)&peer, &len);if (sockfd < 0){LOG(WARNING, "accept error\n");return nullptr;}*addr = peer;socket_sptr sock = std::make_shared<TcpSocket>(sockfd);return sock;}bool Connetcor(InetAddr &addr) override{// tcp client 要bind,不要显示的bind.struct sockaddr_in server;// 构建目标主机的socket信息memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(addr.Port());server.sin_addr.s_addr = inet_addr(addr.Ip().c_str());int n = connect(_sockfd, (struct sockaddr *)&server, sizeof(server));if (n < 0){std::cerr << "connect error" << std::endl;return false;}return true;}void SetSocketAddrReuse() override{int opt = 1;::setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));}int Recv(std::string *out) override{char inbuffer[4096];ssize_t n = ::recv(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0);if (n > 0){inbuffer[n] = 0;*out = inbuffer; // ??? +=}return n;}void SetSocketAddrReuse() override // 设置套接字地址复用->为什么要设置套接字地址复用?因为服务器程序在运行时,可能会多次启动,而端口号是临时端口,所以需要设置套接字地址复用,使得端口号可以被重复使用。{int opt = 1;                                                                       // 1表示开启地址复用::setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt)); // 设置套接字地址复用}int Send(const std::string &in) override{int n = ::send(_sockfd, in.c_str(), in.size(), 0);return n;}int SockFd() override{return _sockfd;}void Close() override{if (_sockfd > -1)::close(_sockfd);}private:int _sockfd;};// class SocketFactor// {// public://     void Build// }
} // namespace socket_ns
#include <iostream>
#include <string>
#include <functional>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#include <pthread.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <memory>#include "InetAddr.hpp"
#include "Log.hpp"
#include "Socket.hpp"using namespace socket_ns;class TcpServer;using http_t = std::function<std::string(std::string reqeust)>;class ThreadData
{
public:ThreadData(socket_sptr fd, InetAddr addr, TcpServer *s) : sockfd(fd), clientaddr(addr), self(s){}public:socket_sptr sockfd;InetAddr clientaddr;TcpServer *self;
};
class TcpServer
{
public:TcpServer(int port, http_t service): _localaddr("0", port), // 本地地址为什么要设置为0?因为0.0.0.0可以表示任何地址,而0.0.0.0:8080表示本机的8080端口_listensock(std::make_unique<TcpSocket>()),_http_service(service),_isrunning(false){_listensock->BuildListenSocket(_localaddr);}static void *HandlerSock(void *args){pthread_detach(pthread_self());ThreadData *td = static_cast<ThreadData *>(args);std::string request, response;// 有较大的概率,读到的就是一个完整的http requestssize_t n = td->sockfd->Recv(&request); // 读取请求if (n > 0){response = td->self->_http_service(request); // self表示TcpServer对象td->sockfd->Send(response);                  // 发送应答}td->sockfd->Close();delete td;return nullptr;}void Loop(){_isrunning = true;while (_isrunning){InetAddr peeraddr;                                         // 客户端地址socket_sptr normalsock = _listensock->Accepter(&peeraddr); // 接受连接if (normalsock == nullptr){continue;}pthread_t t;                                                 // 线程ThreadData *td = new ThreadData(normalsock, peeraddr, this); // 创建线程数据}}void Loop() // 启动服务{_isrunning = true;// 4. 不能直接接受数据,先获取连接while (_isrunning){InetAddr peeraddr;socket_sptr normalsock = _listensock->Accepter(&peeraddr);if (normalsock == nullptr)continue;pthread_t t;ThreadData *td = new ThreadData(normalsock, peeraddr, this);pthread_create(&t, nullptr, HandlerSock, td); // 将线程分离}_isrunning = false;}~TcpServer(){}private:InetAddr _localaddr;std::unique_ptr<Socket> _listensock;bool _isrunning;http_t _http_service;
};
http://www.dtcms.com/wzjs/304509.html

相关文章:

  • 网站打不开原因检测营销方式都有哪些
  • 网站建设与管理书西安疫情最新消息
  • 成都网站建设前50强2023推广平台
  • 微信上打开连接的网站怎么做高端快速建站
  • 点样用外网访问自己做的网站山东服务好的seo公司
  • 哈市那里网站做的好网络广告网站
  • 848给我做一下88网站杭州seo软件
  • 服装行业网站建设教育培训机构十大排名
  • 网站怎么做 吸引人武汉刚刚突然宣布
  • 动态网站开发实训百度一下全知道
  • 网站服务器要求专注于seo顾问
  • 珠海手机网站建设推广公司想要网站导航推广
  • 电话销售怎么做 网站网站收录教程
  • 佛山市网站建设哪家好seo优化工具哪个好
  • 新建的网站多长时间在百度搜到搜索引擎营销推广方案
  • 免费b2b信息平台重庆seo优化
  • 新邱建设网站电话营销系统
  • 网站建设吕凡科技网站关键词怎样优化
  • 大港建站公司凤凰网台湾资讯
  • 建筑工人招聘网站怎么做在线搭建网站
  • 网站建设在会计里算什么资产百度竞价推广是什么
  • 集团门户潍坊seo推广
  • 虚拟主机网站建设百度搜索次数统计
  • 点击app图标进入网站怎么做网站推广具体内容
  • 网站优化培训学校天津关键词优化平台
  • 网站空间公司成都网站seo公司
  • 百度四川营销中心seo官网
  • 2021建站公司免费网站模板网
  • 简述建站流程百度网站排名优化价格
  • 财经直播网站建设网站优化公司哪家好