目录
一、套接字接口类封装
#include<iostream>
#include<string>
#include<vector>
#include<unistd.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<sys/socket.h>
//封装TCPsocket类
#define MAX_LISTEN 5
#define CHECK_RETURN(X) if((X) == false) {return -1;}
class TCPsocket {
private:
int _sockfd;
public:
TCPsocket () : _sockfd(-1) {}
//1.创建套接字
bool Socket() {
_sockfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (_sockfd < 0) {
perror("create socket error!");
return false;
}
return true;
}
//2.为套接字绑定地址信息
bool Bind(const std::string &ip, uint16_t port) {
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip.c_str());
socklen_t len = sizeof(struct sockaddr_in);
int ret = bind(_sockfd, (struct sockaddr*)&addr, len);
if (ret < 0) {
perror("bind error!");
return false;
}
return true;
}
//客户端:3.向服务器发起连接请求
bool Connect(const std::string &ip, uint16_t port) {
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = inet_addr(ip.c_str());
socklen_t len = sizeof(struct sockaddr_in);
int ret = connect(_sockfd, (struct sockaddr*)&addr, len);
if (ret < 0) {
perror("connect error!");
return false;
}
return true;
}
//服务端:3.开始监听
bool Listen(int backlog = MAX_LISTEN) {
int ret = listen(_sockfd, backlog);
if (ret < 0) {
perror("connect error!");
return false;
}
return true;
}
//服务端:4. 获取新建连接
bool Accept(TCPsocket *sock, std::string *ip = NULL, uint16_t *port = NULL) {
struct sockaddr_in addr;
socklen_t len = sizeof(struct sockaddr_in);
int newfd = accept(_sockfd, (struct sockaddr*)&addr, &len);
if (newfd < 0) {
perror("accept error!");
return false;
}
sock -> _sockfd = newfd;
if (ip != NULL) *ip = inet_ntoa(addr.sin_addr);
if (port != NULL) *port = ntohs(addr.sin_port);
return true;
}
//4. 接收数据
bool Recve(std::string *body) {
char temp[8193] = {0};
int ret = recv(_sockfd, temp, 8192, 0);
if (ret < 0) {
perror("recve error!");
return false;
}
else if (ret == 0) {
std::cout<<"peer shutdown!"<< std::endl;
return false;
}
body -> assign(temp, ret);
return true;
}
//5.发送数据
bool Send(const std::string &body) {
int ret = send(_sockfd, body.c_str(), body.size(), 0);
if (ret < 0) {
perror("send error!");
return false;
}
return true;
}
//6.关闭套接字
bool Close() {
if (_sockfd != -1) close(_sockfd);
return true;
}
};
二、服务器实现
#include "socket_tcp.hpp"
#include <sstream>
int main(int argc, char *argv[]) {
if (argc != 2) {
std::cout<<"./http_server port"<< std::endl;
return -1;
}
int srv_port = std::stoi(argv[1]);
std::string srv_ip = "0.0.0.0";
TCPsocket sock;
//1.创建套接字
CHECK_RETURN(sock.Socket());
//2.绑定地址信息
CHECK_RETURN(sock.Bind(srv_ip, srv_port));
//3.开始监听
CHECK_RETURN(sock.Listen());
while(1) {
TCPsocket new_sock;
bool ret = sock.Accept(&new_sock);
//4.获取新建连接
if (ret == false) continue;
//5.收发数据
std::string rec;
new_sock.Recve(&rec);
size_t pos = rec.find("\r\n\r\n");//查找头部结尾标志
if (pos == std::string::npos) {
//没有找到,则认为头部过大
//将响应状态码设置为414
//这里简单实现,就直接错误返回了
return -1;
}
std::string header = rec.substr(0,pos);//截取头部
std::string body = rec.substr(pos + 4);//截取正文
//正常的截图正文,应该将头部按照\r\n进行分割,逐个取出头部;
//然后根据头部中的Content-Length确定正文长度,正文长度= rec.size() - header.size() - 4;
//然后在截取正文
std::cout<<"header:[" << body << "]" << std::endl;
std::cout<< "body:[" << body << "]" << std::endl;
//响应
std::string rep_body = "<html><body><h1>Hello world!</h1></body></html>";
std::stringstream ss_reply;
ss_reply << "HTTP/1.1 200 OK\r\n";
ss_reply << "Connection: close\r\n";
ss_reply << "Content-Type: text/html\r\n";
ss_reply << "Content-Length: " << rep_body.size() << "\r\n";
ss_reply << "\r\n";
ss_reply << rep_body;
std::cout << "response:[" << ss_reply.str() << "]" << std::endl;
new_sock.Send(ss_reply.str());
//短连接,完成响应,关闭连接
new_sock.Close();
}
//6.关闭套接字
sock.Close();
return 0;
}
三、实现效果
1.运行服务端
2.在浏览器输入URL进行请求获取响应