LinuxでC++サーバを作成(HTTPサーバ)

95599 ワード

今日はMakefileを使ってHTTPのサーバーを書きますが、これまでMakefileに触れたことがないので、探ってみる必要があります.
VS 2015でMakefileをテスト
  • HTTPTestという名前のMakefile Project(Linux)を新規作成します.Linux下编写C++服务器(HTTP服务器)_第1张图片
  • 新規項目の追加、C++ファイルの追加(.cpp)、main.cppという名前、Linux下编写C++服务器(HTTP服务器)_第2张图片内容は以下の通りです:
  • #pragma once
    #pragma execution_character_set("utf-8")
    
    #include 
    
    int main()
    {
    	printf("HttpServer  
    "
    ); int count = 0; while (count<3) { count++; printf("hello %d
    "
    ,count); } printf("HttpServer
    "
    ); return 0; }
  • main.cppの同級ディレクトリの下にMakefileの空のファイルを作成し、ファイル名の大文字と小文字に注意し、HTTPTestプロジェクトに追加します.Linux下编写C++服务器(HTTP服务器)_第3张图片は次のように書き込まれます.
  • build:
    	gcc -gdwarf-2 -o HTTPTest main.cpp
    
    clean:
    	rm -rf HTTPTest
    
  • 右クリック項目、属性を選択し、GeneralのRemote Build Root DirectoryにLinux上の項目ディレクトリを書き込み、Linux下编写C++服务器(HTTP服务器)_第4张图片Remote Buildの4項目にそれぞれ
  • を書き込む
    cd $(RemoteRootDir)/$(ProjectName); make build
    cd $(RemoteRootDir)/$(ProjectName); make clean build
    cd $(RemoteRootDir)/$(ProjectName); make clean
    $(RemoteRootDir)/$(ProjectName)/HTTPTest
    

    図:Linux下编写C++服务器(HTTP服务器)_第5张图片
  • main.cppの1行にブレークポイントを打ち、F 5を押してデバッグし、テストに成功しました.Linux下编写C++服务器(HTTP服务器)_第6张图片

  • libeventのインストール
    HTTPサーバlibeventライブラリ実装を選択しました.
  • libeventをLinux仮想マシンにダウンロードし、ここで2.1を選択する.10バージョン、ダウンロード完了後に圧縮パッケージを解凍するLinux下编写C++服务器(HTTP服务器)_第7张图片
  • tar xvf libevent-2.1.10-stable.tar.gz
    

    Linux下编写C++服务器(HTTP服务器)_第8张图片
  • 解凍されたフォルダに入りmakefileを生成するコマンドを入力します.-prefixはlibeventのインストールディレクトリを指定します.
  • cd libevent-2.1.10-stable/
    ./configure --prefix=/usr/libevent
    

    Linux下编写C++服务器(HTTP服务器)_第9张图片
  • 入力makeコンパイル;在这里插入图片描述
  • make installを入力してインストールを開始し、インストールに成功すると、/usr/libeventはbin,include,libフォルダを生成します.在这里插入图片描述 在这里插入图片描述

  • 環境の設定
  • 上記で生成した/usr/libevent/includeをwindowsにコピーする必要があります.ここでは共有フォルダを使用します.
  • mkdir /mnt/hgfs/LinuxShare/libevent
    cp -r /usr/libevent/include /mnt/hgfs/LinuxShare/libevent
    

    WindowsにE:LinuxSharelibeventincludeディレクトリが表示されました.必要なヘッダファイルです.
  • 右クリック項目->プロパティ->C+->Include Search Path、E:LinuxShareinclude;E:\LinuxShare\include\c++\4.8.2;E:\LinuxShare\libevent\include;,前の2つは前のLinuxヘッダファイルで、最後の1つはlibeventヘッダファイルです.Linux下编写C++服务器(HTTP服务器)_第10张图片

  • Makefileファイルのbuildは修正が必要です
    gcc -gdwarf-2 -o HTTPTest main.cpp  -I /usr/libevent/include/ -L /usr/libevent/lib/ -levent
    

    Linux下编写C++服务器(HTTP服务器)_第11张图片F 5を押すとコンパイルは通過するが、実行できず、エラーは以下の通りである:error while loading shared libraries:libevent-2.1.so.6:cannot open shared object file:No such file or directory、このライブラリが見つからないことをヒントに、linux端末で入力する方法を解決します
    ln  -s /usr/libevent/lib/libevent-2.1.so.6 /usr/lib64/libevent-2.1.so.6
    

    これにより、実行時にライブラリをリンクできます.
    パッケージHTTP
    myhttpserverを作成します.hとmyhttpserver.cppは,libeventを参照してhttp serverを実現し,多くのバグを経験した後,getとpost法を実現した.
    myhttpserver.h
    #pragma once
    
    #include 
    #include 
    #include 
    
    static bool closesign = false;
    //  Content-Type
    const char *get_file_type(char *name);
    //    html
    int send_dir(struct evbuffer* bev, const char *dirname);
    //http 
    int send_header(struct evhttp_request* request, const char* filename, long filelen, const char* connest);
    //    
    int send_file_to_http(const char *filename, struct evbuffer* bev);
    //    
    void HttpGenericCallback(struct evhttp_request* request, void* arg);
    
    class Myhttpserver
    {
    public:
    	Myhttpserver();
    	~Myhttpserver();
    
    private:
    	struct event_base* base;
    	struct evhttp* http;
    	char errmsg[512];
    public:
    	//   
    	bool inithttp();
    	//     
    	bool start(unsigned int port);
    	//    
    	void set_gencb(void(*cb)(struct evhttp_request *, void *));
    	//    
    	void dispatch();
    	static void stop(struct event_base* base);
    	void free();
    	char* geterrmsg();
    };
    

    myhttpserver.cpp
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include "myhttpserver.h"
    
    /*
    charset=iso-8859-1	     ,            ;
    charset=gb2312		              ;
    charset=utf-8			           ;
    						      、  、             
    charset=euc-kr		            ;
    charset=big5			              ;
    
                 ,             
              http          
    */
    const char *get_file_type(const char *name) {
    	char* dot;
    	char tmpname[128] = {0};
    	strncpy(tmpname, name, strlen(name));
    	dot = strrchr(tmpname, '.');	//      ‘.’  ;      NULL
    
    	if (dot == (char*)0)
    		return "text/plain; charset=utf-8";
    	if (strcmp(dot, ".html") == 0 || strcmp(dot, ".htm") == 0)
    		return "text/html; charset=utf-8";
    	if (strcmp(dot, ".jpg") == 0 || strcmp(dot, ".jpeg") == 0)
    		return "image/jpeg";
    	if (strcmp(dot, ".gif") == 0)
    		return "image/gif";
    	if (strcmp(dot, ".png") == 0)
    		return "image/png";
    	if (strcmp(dot, ".css") == 0)
    		return "text/css";
    	if (strcmp(dot, ".au") == 0)
    		return "audio/basic";
    	if (strcmp(dot, ".wav") == 0)
    		return "audio/wav";
    	if (strcmp(dot, ".avi") == 0)
    		return "video/x-msvideo";
    	if (strcmp(dot, ".mov") == 0 || strcmp(dot, ".qt") == 0)
    		return "video/quicktime";
    	if (strcmp(dot, ".mpeg") == 0 || strcmp(dot, ".mpe") == 0)
    		return "video/mpeg";
    	if (strcmp(dot, ".vrml") == 0 || strcmp(dot, ".wrl") == 0)
    		return "model/vrml";
    	if (strcmp(dot, ".midi") == 0 || strcmp(dot, ".mid") == 0)
    		return "audio/midi";
    	if (strcmp(dot, ".mp3") == 0)
    		return "audio/mpeg";
    	if (strcmp(dot, ".ogg") == 0)
    		return "application/ogg";
    	if (strcmp(dot, ".pac") == 0)
    		return "application/x-ns-proxy-autoconfig";
    
    	return "text/plain; charset=utf-8";
    }
    
    
    int send_header(struct evhttp_request* request, const char* filename, long filelen, const char* connest) {
    
    	//     
    	evhttp_add_header(request->output_headers, "Content-Type", get_file_type(filename));
    	//     
    	if (filelen > 0)
    	{
    		char charsize[50] = { 0 };
    		sprintf(charsize, "%ld", filelen);
    		evhttp_add_header(request->output_headers, "Content-Length", charsize);
    	}
    	// Connection: close/keep-alive
    	evhttp_add_header(request->output_headers, "Connection", connest);
    
    	return 0;
    }
    
    int send_dir(struct evbuffer* bev, const char *dirname) {
    	char encoded_name[1024];
    	char path[1024];
    	char timestr[64];
    	struct stat sb;
    	struct dirent **dirinfo;
    
    	char buf[4096] = { 0 };
    	sprintf(buf, "%s", dirname);
    	sprintf(buf + strlen(buf), "

    :%s

    ", dirname);// int num =scandir(dirname,&dirinfo,NULL, alphasort);for(int i =0; i<num;++i){// strcpy(encoded_name, dirinfo[i]->d_name);sprintf(path,"%s%s", dirname, dirinfo[i]->d_name);printf("############# path = %s
    "
    , path);if(lstat(path,&sb)<0){sprintf(buf +strlen(buf),"
    ", encoded_name, dirinfo[i]->d_name);}else{strftime(timestr,sizeof(timestr)," %d %b %Y %H:%M",localtime(&sb.st_mtime));if(S_ISDIR(sb.st_mode)){sprintf(buf +strlen(buf),"
    ", encoded_name, dirinfo[i]->d_name, timestr, sb.st_size);}else{sprintf(buf +strlen(buf),"
    ", encoded_name, dirinfo[i]->d_name, timestr, sb.st_size);}}//bufferevent_write(bev, buf, strlen(buf));evbuffer_add_printf(bev, buf);printf(buf);memset(buf,0,sizeof(buf));}sprintf(buf +strlen(buf),"
    %s
    %s/%s%ld
    %s%s%ld
    "
    ); //bufferevent_write(bev, buf, strlen(buf)); evbuffer_add_printf(bev, buf); printf(buf); printf("################# Dir Read OK !!!!!!!!!!!!!!
    "
    ); return 0; } int send_file_to_http(const char *filename, struct evbuffer* bev) { int fd = open(filename, O_RDONLY); int ret = 0; char buf[4096] = { 0 }; // 4096 //evbuffer_read(bev, fd, 30556); while ((ret = read(fd, buf, sizeof(buf)))) { if (ret < 0) break; // \0 //evbuffer_add_printf(bev, buf); // \0 evbuffer_add(bev, buf, ret); memset(buf, 0, 4096); } close(fd); return 0; } // void HttpGenericCallback(struct evhttp_request* request, void* arg) { const struct evhttp_uri* evhttp_uri = evhttp_request_get_evhttp_uri(request); char url[8192]; memset(url, 0x00, sizeof(url)); evhttp_uri_join(const_cast<struct evhttp_uri*>(evhttp_uri), url, 8192); printf("accept request url:%s
    "
    , url); if (request->kind == EVHTTP_REQUEST) { //get if (request->type == EVHTTP_REQ_GET) { /* http://foo.com/?q=test&s=some+thing evhttp_request_uri: HTTP ur, /?q=test&s=some+thing evhttp_parse_query: , evkeyvalq , key/value . */ char* decode_uri = strdup((char*)evhttp_request_uri(request)); struct evkeyvalq http_query; evhttp_parse_query(decode_uri, &http_query); free(decode_uri); //const char *request_value = evhttp_find_header(&http_query, "data"); // //strcpy(url, evhttp_decode_uri(url)); char *pf = &url[1]; if (strcmp(url, "/") == 0 || strcmp(url, "/.") == 0) { pf = "./"; } printf("***** http Request Resource Path = %s, pf = %s
    "
    , url, pf); // if (strcmp(url, "/bye") == 0) { closesign = true; struct event_base* base = (struct event_base*)arg; Myhttpserver::stop(base); return; } struct stat sb; struct evbuffer* evbuf = evbuffer_new(); if (!evbuf) { printf("create evbuffer failed!
    "
    ); return; } char dir[256] = { 0 }, filename[256] = { 0 }; snprintf(dir, sizeof(dir), "/home/aubin/projects/HTTPTest/%s", pf); // if (stat(dir, &sb) == 0 && (sb.st_mode & S_IFREG)) { send_header(request, pf, sb.st_size, "close"); send_file_to_http(dir, evbuf); evhttp_send_reply(request, HTTP_OK, "OK", evbuf); evbuffer_free(evbuf); return; } // else if (stat(dir, &sb) == 0 && (sb.st_mode & __S_IFDIR)) { send_header(request, ".html", 0, "close"); send_dir(evbuf, dir); evhttp_send_reply(request, HTTP_OK, "OK", evbuf); evhttp_clear_headers(&http_query); evbuffer_free(evbuf); return; } // else { send_header(request, ".html", 0, "close"); send_dir(evbuf, "/home/aubin/projects/HTTPTest"); evhttp_send_reply(request, HTTP_OK, "OK", evbuf); evhttp_clear_headers(&http_query); evbuffer_free(evbuf); return; } } //post else if (request->type == EVHTTP_REQ_POST) { char charadd1[10], charadd2[10]; struct evkeyvalq http_query_post; char decode_post_uri[1024] = { 0 }; int buffer_data_len = EVBUFFER_LENGTH(request->input_buffer); char *post_data = (char *)malloc(buffer_data_len + 1); memset(post_data, 0, buffer_data_len + 1); memcpy(post_data, EVBUFFER_DATA(request->input_buffer), buffer_data_len); sprintf(decode_post_uri, "/?%s", post_data); // evhttp_parse_query(decode_post_uri, &http_query_post); strcpy(charadd1, evhttp_find_header(&http_query_post, "add1")); strcpy(charadd2, evhttp_find_header(&http_query_post, "add2")); int sum = atoi(charadd1) + atoi(charadd2); struct evbuffer* evbuf = evbuffer_new(); if (!evbuf) { printf("create evbuffer failed!
    "
    ); return; } evbuffer_add_printf(evbuf, "\ \ \ \ (runoob.com)\ \ \

    %d

    \ \ "
    , sum); evhttp_send_reply(request, HTTP_OK, "OK", evbuf); evbuffer_free(evbuf); } } } Myhttpserver::Myhttpserver() { } Myhttpserver::~Myhttpserver() { } bool Myhttpserver::inithttp() { event_init(); base = event_base_new(); if (!base) { sprintf(errmsg, "create event_base failed,error: %s(errno: %d)
    "
    , strerror(errno), errno); return false; } http = evhttp_new(base); if (!http) { sprintf(errmsg, "create evhttp failed,error: %s(errno: %d)
    "
    , strerror(errno), errno); return false; } return true; } bool Myhttpserver::start(unsigned int port) { if (evhttp_bind_socket(http, "0.0.0.0", port) != 0) { sprintf(errmsg, "start evhttp failed,error: %s(errno: %d)
    "
    , strerror(errno), errno); return false; } evhttp_set_timeout(http, 120); return true; } void Myhttpserver::set_gencb(void(*cb)(struct evhttp_request *, void *)) { evhttp_set_gencb(http, cb, base); } void Myhttpserver::dispatch() { event_base_dispatch(base); } void Myhttpserver::stop(struct event_base* base) { event_base_loopbreak(base); event_base_loopexit(base, 0); return; } void Myhttpserver::free() { evhttp_free(http); event_base_free(base); } char * Myhttpserver::geterrmsg() { return errmsg; }

    main.cpp
    #pragma once
    #pragma execution_character_set("utf-8")
    
    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    #include "myhttpserver.h"
    
    
    int main(int argc, char **argv) {
    	Myhttpserver httpserver;
    	if (!httpserver.inithttp()) {
    		printf(httpserver.geterrmsg());
    		return 1;
    	}
    		
    	if (!httpserver.start(5000))
    	{
    		printf(httpserver.geterrmsg());
    		return 1;
    	}
    	httpserver.set_gencb(HttpGenericCallback);
    	httpserver.dispatch();
    	httpserver.free();
    
    	return 0;
    }
    
    

    Makefileのgccはg++に変更する必要があります.そうしないと、includeとlibがコンパイルして実行できるという問題が発生します.Linux下编写C++服务器(HTTP服务器)_第12张图片 Makefile
    INCLUDEPATH=/usr/libevent/include
    LIBPATH=/usr/libevent/lib
    CC1=g++
    
    build:main.o myhttpserver.o
    	$(CC1) -gdwarf-2 -o HTTPTest main.o myhttpserver.o -I $(INCLUDEPATH) -L $(LIBPATH) -levent
    
    main.o:main.cpp myhttpserver.h
    	$(CC1) -gdwarf-2 -c main.cpp -I $(INCLUDEPATH) -L $(LIBPATH) -levent
    
    myhttpserver.o:myhttpserver.cpp myhttpserver.h
    	$(CC1) -gdwarf-2 -c myhttpserver.cpp -I $(INCLUDEPATH) -L $(LIBPATH) -levent
    
    clean:
    	rm *.o HTTPTest
    

    ディスプレイの実行
    サービス側のHTTPTestにはこれらのファイルがあります.Linux下编写C++服务器(HTTP服务器)_第13张图片 index.htmlの内容は以下の通りです.
    
    <html>
    <head> 
    <meta charset="utf-8"> 
    <title>    (runoob.com)title> 
    head>
    <body>
    
    <form name="input" action="/add" method="post">
    add1: <input type="text" name="add1">
    add2: <input type="text" name="add2">
    <input type="submit" value="Submit">
    form>
    
    <p><b>b>          。                20   。p>
    
    body>
    html>
    

    トップページLinux下编写C++服务器(HTTP服务器)_第14张图片一般ファイルは、ビデオを取得するにはhtmlに接続を書き込む必要があることを示しており、画像、gifのように直接開くことはできません.そうしないと、ダウンロードインタフェースから飛び出します.Linux下编写C++服务器(HTTP服务器)_第15张图片indexページLinux下编写C++服务器(HTTP服务器)_第16张图片送信ボタンをクリックLinux下编写C++服务器(HTTP服务器)_第17张图片byeサーバを閉じるLinux下编写C++服务器(HTTP服务器)_第18张图片