使用socket实现简单的http服务器
socket具体使用在这里不再描述,这里主要描述一下具体的使用范例
Xtcp.h
#ifndef XTCP_H #define XTCP_H #ifdef WIN32 #ifdef XSOCKET_EXPORTS #define XSOCKET_API __declspec(dllexport) #else #define XSOCKET_API __declspec(dllimport) #endif #else #define XSOCKET_API #endif #include <string> XSOCKET_API std::string GetIpByHost(std::string host); class XSOCKET_API XTcp { public: int CreateSocket(); bool Bind(unsigned short port); XTcp Accept(); void Close(); int Recv(char *buf,int bufsize); int Send(const char *buf,int sendsize); bool Connect(const char *ip,unsigned short port,int timeoutms=1000); bool SetBlock(bool isblock); XTcp(); virtual ~XTcp(); int sock = 0; unsigned short port = 0; char ip[16]; }; #endif
XTcp.cpp
#include "XTcp.h" #include <string.h> #include <stdio.h> #include <stdlib.h> #ifdef WIN32 #include <windows.h> #define socklen_t int #else #include <sys/types.h> #include <sys/socket.h> #include <unistd.h> #include <arpa/inet.h> #include <fcntl.h> #define closesocket close #endif #include <thread> using namespace std; XSOCKET_API std::string GetIpByHost(std::string host) { hostent *hptr = gethostbyname(host.c_str()); if (!hptr)return ""; in_addr addr; addr.s_addr = *(unsigned long *)hptr->h_addr; return inet_ntoa(addr); } XTcp::XTcp() { #ifdef WIN32 static bool first = true; if (first) { first = false; WSADATA ws; WSAStartup(MAKEWORD(2, 2), &ws); } #endif } int XTcp::CreateSocket() { sock = socket(AF_INET, SOCK_STREAM, 0); if (sock == -1) { printf("create socket failed!\n"); } return sock; } bool XTcp::Bind(unsigned short port) { if (sock <= 0) CreateSocket(); sockaddr_in saddr; saddr.sin_family = AF_INET; saddr.sin_port = htons(port); saddr.sin_addr.s_addr = htonl(0); if (::bind(sock, (sockaddr*)&saddr, sizeof(saddr)) != 0) { printf("bind port %d failed!\n", port); return false; } printf("bind port %d success!\n", port); listen(sock, 1000); return true; } int XTcp::Recv(char *buf, int bufsize) { return recv(sock, buf, bufsize, 0); } int XTcp::Send(const char *buf, int size) { int s = 0; while (s != size) { int len = send(sock, buf + s, size - s, 0); if (len <= 0)break; s += len; } return s; } void XTcp::Close() { if (sock <= 0) return; closesocket(sock); sock = 0; } XTcp XTcp::Accept() { XTcp tcp; sockaddr_in caddr; socklen_t len = sizeof(caddr); int client = accept(sock, (sockaddr*)&caddr, &len); if (client <= 0) return tcp; //printf("accept client %d\n", client); printf("."); char *ip = inet_ntoa(caddr.sin_addr); strcpy(tcp.ip, ip); tcp.port = ntohs(caddr.sin_port); tcp.sock = client; //printf("client ip is %s,port is %d\n", tcp.ip, tcp.port); return tcp; } bool XTcp::Connect(const char *ip,unsigned short port,int timeoutms) { if(sock <=0 ) CreateSocket(); sockaddr_in saddr; saddr.sin_family = AF_INET; saddr.sin_port = htons(port); saddr.sin_addr.s_addr = inet_addr(ip); SetBlock(false); fd_set set; if(connect(sock,(sockaddr*)&saddr,sizeof(saddr))!=0) { FD_ZERO(&set); FD_SET(sock,&set); timeval tm; tm.tv_sec = 0; tm.tv_usec = timeoutms*1000; if(select(sock+1,0,&set,0,&tm) <= 0) { printf("connect timeout or error!\n"); printf("connect %s:%d failed!:%s\n",ip,port,strerror(errno)); return false; } } SetBlock(true); printf("connect %s:%d success!\n",ip,port); return true; } XTcp::~XTcp() { } bool XTcp::SetBlock(bool isblock) { if(sock<=0) return false; #ifdef WIN32 unsigned long ul = 0; if(!isblock) ul = 1; ioctlsocket(sock,FIONBIO,&ul); #else int flags = fcntl(sock,F_GETFL,0); if(flags<0) return false; if(isblock) { flags = flags&~O_NONBLOCK; } else { flags = flags|O_NONBLOCK; } if(fcntl(sock,F_SETFL,flags)!=0) return false; #endif return true; }
以上是基于linux和windows的简单封装,简单使用一下:
server.cpp
#include <stdlib.h> #include "XTcp.h" #include <thread> #include <string.h> class TcpThread { public: void Main() { char buf[1024] = { 0 }; for (;;) { int recvlen = client.Recv(buf, sizeof(buf)-1); if (recvlen <= 0) break; buf[recvlen] = '\0'; if (strstr(buf, "quit") != NULL) { char re[] = "quit success!\n"; client.Send(re, strlen(re) + 1); break; } int sendlen = client.Send("ok\n", 4); printf("recv %s\n", buf); } client.Close(); delete this; } XTcp client; }; int main(int argc, char *argv[]) { unsigned short port = 8080; if (argc > 1) { port = atoi(argv[1]); } XTcp server; server.CreateSocket(); server.Bind(port); for (;;) { XTcp client = server.Accept(); TcpThread *th = new TcpThread(); th->client = client; std::thread sth(&TcpThread::Main, th); sth.detach(); } server.Close(); getchar(); return 0; }
tcpclient.cpp
#include "XTcp.h" int main() { for (;;) { XTcp client; client.CreateSocket(); client.SetBlock(false); client.Connect("192.168.3.69",8080,3000); //client.Send("client", 6); char* msg = "GET / HTTP/1.1\r\n\r\n"; client.Send(msg, strlen(msg)); char buf[1024] = { 0 }; client.Recv(buf, sizeof(buf)); printf("%s\n", buf); } getchar(); return 0; }
//实现一个支持http 1.0/1.1 /phh的简单网页服务器
XHttpServer.h
#pragma once #include "XTcp.h" class XHttpServer { public: bool Start(unsigned short port); XHttpServer(); ~XHttpServer(); XTcp server; void Main(); void Stop(); bool isexit = false; };
XHttpServer.cpp
#include "XHttpServer.h" #include "XHttpClient.h" #include <thread> using namespace std; void XHttpServer::Stop() { isexit = true; } void XHttpServer::Main() { while (!isexit) { XTcp client = server.Accept(); if (client.sock <= 0)continue; XHttpClient *th = new XHttpClient(); th->Start(client); } } bool XHttpServer::Start(unsigned short port) { isexit = false; server.CreateSocket(); if (!server.Bind(port)) return false; thread sth(&XHttpServer::Main, this); sth.detach(); return true; } XHttpServer::XHttpServer() { } XHttpServer::~XHttpServer() { }
XHttpClient.h
#pragma once #include "XTcp.h" #include "XHttpResponse.h" class XHttpClient { public: XHttpClient(); ~XHttpClient(); bool Start(XTcp client); void Main(); XTcp client; XHttpResponse res; };
XHttpClient.cpp
#include "XHttpClient.h" #include <thread> using namespace std; bool XHttpClient::Start(XTcp client) { this->client = client; thread sth(&XHttpClient::Main, this); sth.detach(); return true; } void XHttpClient::Main() { char buf[10240] = { 0 }; for (;;) { int len = client.Recv(buf, sizeof(buf)-1); if (len <= 0) { break; } buf[len] = '\0'; if (!res.SetRequest(buf)) { break; } string head = res.GetHead(); if (client.Send(head.c_str(), head.size())<=0) { break; } int size = sizeof(buf); bool error = false; for (;;) { int len = res.Read(buf, size); if (len < 0) { error = true; break; } if (len == 0) break; if (client.Send(buf, len) <= 0) { error = true; break; } } } client.Close(); delete this; } XHttpClient::XHttpClient() { } XHttpClient::~XHttpClient() { }
XHttpResponse.h
#pragma once #include <string> class XHttpResponse { public: bool SetRequest(std::string request); std::string GetHead(); int Read(char *buf, int bufsize); XHttpResponse(); ~XHttpResponse(); private: int filesize = 0; FILE *fp = NULL; };
XHttpResponse.cpp
#include "XHttpResponse.h" #include <string> #include <regex> using namespace std; bool XHttpResponse::SetRequest(std::string request) { string src = request; // / /index.html /ff string pattern = "^([A-Z]+) /([a-zA-Z0-9]*([.][a-zA-Z]*)?)[?]?(.*) HTTP/1"; regex r(pattern); smatch mas; regex_search(src,mas,r); if(mas.size() == 0) { printf("%s failed!\n",pattern.c_str()); return false ; } string type = mas[1]; string path = "/"; path += mas[2]; string filetype = mas[3]; string query = mas[4]; if(type != "GET") { printf("Not GET!!!\n"); return false; } string filename = path; if(path == "/") { filename = "/index.html"; } string filepath = "www"; filepath += filename; //php-cgi www/index.php id=1 name=xcj > www/index.php.html if(filetype == "php") { string cmd = "php-cgi "; cmd += filepath; cmd += " "; //query id=1&name=xcj // id=1 name=xcj for(int i =0; i< query.size(); i++) { if(query[i] == '&') query[i] = ' '; } cmd += query; cmd += " > "; filepath += ".html"; cmd += filepath; //printf("%s\n",cmd.c_str()); system(cmd.c_str()); } fp = fopen(filepath.c_str(),"rb"); if(fp == NULL) { printf("open file %s failed!\n",filepath.c_str()); return false; } //获取文件大小 fseek(fp,0,SEEK_END); filesize = ftell(fp); fseek(fp,0,0); //printf("file size is %d\n",filesize); if(filetype == "php") { char c = 0; //\r\n\r\n int headsize = 0; while(fread(&c,1,1,fp) > 0) { headsize++; if(c=='\r') { fseek(fp,3,SEEK_CUR); headsize += 3; break; } } filesize = filesize - headsize; } return true; } std::string XHttpResponse::GetHead() { string rmsg = ""; rmsg = "HTTP/1.1 200 OK\r\n"; rmsg += "Server: XHttp\r\n"; rmsg += "Content-Type: text/html\r\n"; rmsg += "Content-Length: "; char bsize[128] = { 0 }; sprintf(bsize, "%d", filesize); rmsg += bsize; //rmsg += //rmsg += "10\r\n"; rmsg += "\r\n\r\n"; return rmsg; } int XHttpResponse::Read(char *buf, int bufsize) { return fread(buf, 1, bufsize, fp); } XHttpResponse::XHttpResponse() { } XHttpResponse::~XHttpResponse() { }
server.cpp
#include <stdlib.h> #include "XTcp.h" #include "XHttpServer.h" using namespace std; #include <signal.h> int main(int argc, char *argv[]) { signal(SIGPIPE,SIG_IGN); unsigned short port = 8080; if (argc > 1) { port = atoi(argv[1]); } XHttpServer server; server.Start(port); getchar(); return 0; }