使用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;
}

 

posted @ 2024-09-23 16:57  白伟碧一些小心得  阅读(24)  评论(0编辑  收藏  举报