一、套接字接口类封装
#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进行请求获取响应