响应:
第一部分测试代码,读取请求
Makefile
bin=httpserver #生成的可执行程序
cc=g++ #编译器名称
LD_FLAGS=-std=c++11 -lpthread #-DDEBUG=1 #链接选项
src=main.cc
$(bin):$(src)
$(cc) -o $@ $^ $(LD_FLAGS)
.PHONY:clean
clean:
rm -f $(bin)
1111111
main.cc
#include <iostream>
#include <string>
#include <memory>
#include "HttpServer.hpp"
static void Usage(std::string proc)
{
std::cout << "Usage:\n\t" << proc << " port" << std::endl;;
}
int main(int argc, char *argv[])
{
if( argc != 2 )
{
Usage(argv[0]);
exit(4);
}
int port = atoi(argv[1]);
std::shared_ptr<HttpServer> http_server(new HttpServer(port));
http_server->InitServer();
http_server->Loop();
return 0;
}
11111
Log.hpp
#pragma once
#include <iostream>
#include <string>
#include <ctime>
#define INFO 1
#define WARNING 2
#define ERROR 3
#define FATAL 4
#define LOG(level, message) Log(#level, message, __FILE__, __LINE__)
void Log(std::string level, std::string message, std::string file_name, int line)
{ // [日志级别][时间戳][日志信息][错误文件名称][行数]
std::cout << "[" << level << "]" << "[" << time(nullptr) << "]"
<< "[" << message << "]" << "[" << file_name << "]" << "[" << line << "]" << std::endl;
}
111
TcpServer.hpp
#pragma once
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include "Log.hpp"
#define BACKLOG 7
class TcpServer
{
private:
int _port;
int _listen_sock;
static TcpServer* _svr; // 指向单例对象的static指针,懒汉模式
private: // 单例模式
TcpServer(int port)
: _port(port)
, _listen_sock(-1)
{}
TcpServer()
{}
TcpServer(const TcpServer& s) = delete;
TcpServer* operator=(const TcpServer& s) = delete;
public:
static TcpServer *GetInstance(int port)
{
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
if(nullptr == _svr)
{
pthread_mutex_lock(&lock);
if(nullptr == _svr)
{
_svr = new TcpServer(port);
_svr->InitServer();
}
pthread_mutex_unlock(&lock);
}
return _svr;
}
void InitServer()
{
Socket();
Bind();
Listen();
LOG(INFO, "tcp_server init ... success");
}
void Socket()
{
_listen_sock = socket(AF_INET, SOCK_STREAM, 0);
if(_listen_sock < 0)
{
LOG(FATAL, "socket error");
exit(1);
}
int opt = 1; // 端口复用:宕机了也能立马绑定端口号
setsockopt(_listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
LOG(INFO, "create socket ... success");
}
void Bind()
{
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(_port);
local.sin_addr.s_addr = INADDR_ANY; //云服务器不能直接绑定公网IP
if(bind(_listen_sock, (struct sockaddr*)&local, sizeof(local)) < 0)
{
LOG(FATAL, "bind error");
exit(2);
}
LOG(INFO, "bind socket ... success");
}
void Listen()
{
if(listen(_listen_sock, BACKLOG) < 0)
{
LOG(FATAL, "listen socket error");
exit(3);
}
LOG(INFO, "listen socket ... success");
}
int Sock()
{
return _listen_sock;
}
~TcpServer()
{
if(_listen_sock >= 0)
{
close(_listen_sock);
}
}
};
TcpServer* TcpServer::_svr = nullptr;
11111111
HttpServer.hpp
#pragma once
#include <iostream>
#include <pthread.h>
#include <signal.h>
#include "Log.hpp"
#include "TcpServer.hpp"
#include "Protocol.hpp"
#define PORT 7777
class HttpServer
{
private:
int _port;
bool _stop;
public:
HttpServer(int port = PORT)
: _port(port)
, _stop(false)
{}
void InitServer()
{
// 信号SIGPIPE进行忽略,否则在写入时候,可能直接崩溃server
signal(SIGPIPE, SIG_IGN);
}
void Loop()
{
TcpServer *tsvr = TcpServer::GetInstance(_port);
LOG(INFO, "Loop begin");
while(!_stop)
{
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int sock = accept(tsvr->Sock(), (struct sockaddr*)&peer, &len);
if(sock < 0) // 获取套接字失败
{
continue;
}
LOG(INFO, "Get a new link");
int *_sock = new int(sock); // 暂时方案
pthread_t pid;
pthread_create(&pid, nullptr, Entrance::HandlerRequest, _sock);
pthread_detach(pid);
}
}
~HttpServer()
{}
};
1111
Protocol.hpp
#pragma once
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "Log.hpp"
#include "Util.hpp"
#define DEBUG
class Entrance // 线程入口
{
public:
Entrance()
{}
~Entrance()
{}
static void* HandlerRequest(void * _sock)
{
int sock = *(int*)_sock; // 临时测试方案
LOG(INFO, "Hander Request Begin");
#ifdef DEBUG
//For Test
char buffer[4096];
recv(sock, buffer, sizeof(buffer), 0);
std::cout << "-------------begin----------------" << std::endl;
std::cout << buffer << std::endl;
std::cout << "-------------end----------------" << std::endl;
#else
EndPoint* ep = new EndPoint(sock);
ep->RecvHttpRequest();
ep->BuildHttpResponse();
ep->SendHttpResponse();
delete ep;
#endif
LOG(INFO, "Hander Request End");
}
};
测试结果
11111
第二部分测试代码,解析请求,返回静态网页
过程截图:
读取请求行和报头
解析uri:
返回了静态网页:
telnet测试:
加上了响应类型和长度:
第二份代码链接:
Linux_Code: 存放linux学习代码 - Gitee.com