首頁 > 軟體

基於C++編寫一個簡單的伺服器

2023-03-15 06:02:09

本文使用上一期寫的反射類,另外我發現<WinSock2.h>這個標頭檔案裡有RegisterClass 這個結構,還有typedef RegisterClass RegisterClassW這句話。。。這都能重複,汗。

先寫個簡易的controller基礎類別繼承反射基礎類別,之後動態呼叫的時候直接使用父類別指標,這樣就能根據對映表來動態使用對應的成員方法。

#pragma once
#include "Reflex.h"
using namespace myUtil;
class CController :public RObject{
};

先寫個index控制器,這裡我是將宣告和實現分為兩個檔案寫的,不知道為啥分開寫就報錯LNK2005 和 LNK1169,好在找到了解決辦法,在 屬性->設定屬性->連結器->命令列中新增 /FORCE:MULTIPLE 即可

這裡我給控制器傳入的引數是兩個字串,這是簡易版本,完全可以照著請求報文和響應報文實現兩個類來完成這部分,之後更新吧
接著說,我直接在響應報文中加入了寫的對應的兩個html頁面,之後用Postman來測試

#pragma once
#include "CController.h"
using namespace std;
class indexController : public CController
{
public:
    void show();
    void fun();
    void add(int& a, int& b);
    void index(const string& req, string& resp);
    void title(const string& req, string& resp);
    int m_age;
    indexController():m_age(10) {}
};
#include "indexController.h"
#include <iostream>
#include <fstream>
using namespace std;

void indexController::show() {
    cout << "hello world show" << endl;
}
void indexController::fun() {
    cout << "hello world fun" << endl;
}
void indexController::add(int& a, int& b) {
    cout << "hello world add" << endl;
}
void indexController::index(const string& req, string& resp) {
    resp = "";
    resp.append("HTTP/1.1 200 OKrn");
    resp.append("content-language:zh-CN");
    resp.append("content-type:text/html;charset=utf-8rnrn");
    string text = "";
    fstream file;
    file.open("index.html", ios::in);
    if (file.fail()) return;
    while (!file.eof()) {
        char ch;
        file.get(ch);
        text += ch;
    }
    resp.append(text);
}
void indexController::title(const string& req, string& resp) {
    resp = "";
    resp.append("HTTP/1.1 200 OKrn");
    resp.append("Content-Type:text/htmlrnrn");
    resp.append("{"name":"title"}");
}

這是一個專門用來註冊反射的標頭檔案,在main中直接呼叫宏即可

#pragma once
#include "Reflex.h"
#include "indexController.h"
#define REFLEX_DECLARE            
REGISTER_REFLEX(indexController)
REGISTER_REFLEX_FIELD(indexController, int, m_age)
REGISTER_REFLEX_METHOD(indexController, show)
REGISTER_REFLEX_METHOD(indexController, fun)
REGISTER_REFLEX_METHOD_ARGS(indexController, add, void, int&, int&)
REGISTER_REFLEX_METHOD_ARGS(indexController, index, void, string&, string&)
REGISTER_REFLEX_METHOD_ARGS(indexController, title, void, string&, string&)

這裡將對映表設定為全域性變數,可以將服務作為一個類,在這個類中維護一個登入檔,再新增一個方法增加對映,就像springboot中的註釋一樣,下面有反射的測試,可以用函數名來測試

#include <iostream>
#include <string>
#include <thread>
#include <map>
#include <WinSock2.h>
#include "Util.h"
#include "Singleton.h"
#include "macro.h"
#include "indexController.h"
#pragma comment(lib,"ws2_32.lib")
using namespace std;
using namespace myUtil;

REFLEX_DECLARE
//對映表
map<string, string> mapTable = {
    {"/","index"},
    {"/title","title"}
};
//用來獲取url
vector<string> getStringVectorByChar(const string& source, const char& ch) {
    vector<string> res;
    string temp = "";
    for (char item : source) {
        if (item == ch) {
            res.push_back(temp);
            temp = "";
        }
        else {
            temp += item;
        }
    }
    if (temp != "") res.push_back(temp);
    return res;
}

void threadFunc(SOCKET ServerSocket) {
    char ReceiveBuff[BUFSIZ];
    char SendBuff[BUFSIZ];
    while (true)
    {
        SOCKET ClientSocket;
        SOCKADDR_IN ClientAddr;
        int ClientAddrLen = sizeof(ClientAddr);
        ClientSocket = ::accept(ServerSocket, (SOCKADDR*)&ClientAddr, &ClientAddrLen);
        ZeroMemory(ReceiveBuff, BUFSIZ);
        recv(ClientSocket, ReceiveBuff, BUFSIZ, 0);
        cout << "接收自使用者端資料:n" << ReceiveBuff << endl;
        string source(ReceiveBuff);
        string url = getStringVectorByChar(source, ' ')[1];
        //反射使用的地方
        Reflex* factory = Singleton<Reflex>::Instance();
        CController* a = (CController*)factory->createClass("indexController");
        string info = "";
        string req = "";
        string funName = mapTable[url];
        a->Call<void, string&, string&>(funName, req, info);
        //反射使用結束
        ::send(ClientSocket, info.c_str(), info.size(), 0);
        closesocket(ClientSocket);
    }
}

int main() {
	//測試反射
    //Reflex* factory = Singleton<Reflex>::Instance();
    //CController* a = (CController*)factory->createClass("indexController");
    //while (1) {
    //    string funName = "";
    //    cin >> funName;
    //    a->Call<void,int,int>(funName,1,1);
    //}

    WORD SocketVersion = MAKEWORD(2, 2);
    WSADATA wsd;
    if (WSAStartup(SocketVersion, &wsd) != 0)
    {
        cout << "繫結Socket庫失敗" << endl;
    }
    SOCKET ServerSocket;
    ServerSocket = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if (ServerSocket == INVALID_SOCKET)
    {
        cout << "建立伺服器通訊端失敗" << endl;
        WSACleanup();
        return -1;
    }
    SOCKADDR_IN ServerAddr;
    ServerAddr.sin_family = AF_INET;
    ServerAddr.sin_port = htons(9090);
    ServerAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
    int RetVal = ::bind(ServerSocket, (SOCKADDR*)&ServerAddr, sizeof(SOCKADDR_IN));
    if (RetVal == SOCKET_ERROR)
    {
        cout << "通訊端繫結失敗" << endl;
        closesocket(ServerSocket);
        WSACleanup();
        return -1;
    }
    RetVal = ::listen(ServerSocket, 2);
    if (RetVal == SOCKET_ERROR)
    {
        cout << "通訊端監聽失敗" << endl;
        closesocket(ServerSocket);
        WSACleanup();
        return -1;
    }
    thread th(threadFunc, ServerSocket);
    th.join();
    return 0;
}

測試結果

index

title

以上就是基於C++編寫一個簡單的伺服器的詳細內容,更多關於C++伺服器的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com