草庐IT

C++ 手撸简易服务器(完善版本)

咩~~ 2023-04-15 原文

本文没有带反射部分内容,可以看我之前发的

Server.h

#pragma once

#include <string>
#include <iostream>
#include <thread>
#include <unordered_map>
using namespace std;
#ifndef _SERVER_
#define _SERVER_

#include <winsock.h>
#include "Net.h"
#include "Util.h"
#pragma comment(lib,"ws2_32.lib")

NAME_SPACE_START(myUtil)

#define SERVER_ADDR "127.0.0.1"
#define SERVER_PORT 8080

class Server {
public:
    Server();
    Server(const std::string& addr = SERVER_ADDR, const int& port = SERVER_PORT);
    ~Server() {}
public:
    bool listen(const int& maxConnect = 1);
    void setRoute(const string& url, const string& className, const string& classFunName);
    void runRoute(const Request& req, Response* resp);
    void setInterceptor(const string& url, const string& InterceptorName);
    void close();
protected:
    bool Init();
    void threadFunc(SOCKET m_server);
    int sendTelegram(const SOCKET& accept, const string& info, int flags);
private:
    SOCKET m_server;
    SOCKADDR_IN m_add_in;
    //thread listenThread;
    int connectCount{ 0 };
    unordered_map<string, pair<string, string>> routeMap;
    unordered_map<string, string> interceptorMap;
    IniHelper iniHelper;
};

NAME_SPACE_END()
#endif //!_SERVER_

Server.cpp

#include "Server.h"
#include <minwindef.h>
#include <string>
#include <winsock.h>
#include <iostream>
#include <thread>
#include <fstream>
#include "Net.h"
#include "Util.h"
#include "Reflex.h"
#include "CController.h"
#include "Interceptor.h"
using namespace std;

NAME_SPACE_START(myUtil)

Server::Server()
{
    m_add_in.sin_family = AF_INET;
    m_add_in.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
    m_add_in.sin_port = htons(SERVER_PORT);
}

Server::Server(const std::string& addr, const int& port)
{
    m_add_in.sin_family = AF_INET;
    m_add_in.sin_addr.S_un.S_addr = inet_addr(addr.c_str());
    m_add_in.sin_port = htons(port);
}

bool Server::listen(const int& maxConnect)
{
    if (!Init()) {
        return false;
    }
    m_server = socket(AF_INET, SOCK_STREAM, 0);
    if (::bind(m_server, (sockaddr*)&m_add_in, sizeof(SOCKADDR)) == SOCKET_ERROR) {
        WSACleanup();
        return false;
    }
    if (::listen(m_server, maxConnect) < 0) {
        WSACleanup();
        return false;
    }
    thread listenThread(&Server::threadFunc, this, m_server);
    listenThread.join();
    return true;
}

void Server::setRoute(const string& url, const string& className, const string& classFunName)
{
    routeMap.insert(pair<string, pair<string, string>>(url, pair<string, string>(className, classFunName)));
}

void Server::runRoute(const Request& req, Response* resp)
{
    string url = req.getRequestStatus().Url;
    Reflex* factory = myUtil::Singleton<Reflex>::Instance();
    string interceptorName = "";
    string res = "";
    string content = "";
    //拦截器
    //先去拦截器映射表中寻找类名,没有的话默认使用基类
    auto interceptorIt = interceptorMap.find(url);
    if (interceptorIt != interceptorMap.end()) interceptorName = interceptorIt->second;
    Interceptor* inter = (Interceptor*)factory->createClass(interceptorName);
    if (inter == nullptr) inter = new Interceptor();
    if (inter->preHandle(req, *resp)) {
        //反射
        auto it = routeMap.find(url);
        if (it != routeMap.end()) {
            CController* cont = (CController*)factory->createClass(it->second.first);
            res = cont->Call<string, Request, Response*>(it->second.second, req, resp);
        }
        //反射结束
    }
    else {
        resp->setResponseStatus("HTTP", 1, 1, 404, "Forbidden");
    }
    if (url.find("favicon.ico") != string::npos) {
        content = getFile(iniHelper.getIniConfig("staticResource", "favicon_path", "./favicon.ico"));
        resp->setResponseHead("content-type", "image/x-icon");
    }
    else if(res != "") {
        try {
            content = getFile(res);
        }
        catch(exception ex){
            content = ex.what();
        }
    }
    resp->setResponseContent(content);
    auto list = resp->getCookie();
    for (auto item : list) {
        resp->setResponseHead("Set-Cookie", item.toString());
    }
    resp->setResponseHead("content-length", to_string(content.size()));
    resp->setResponseHead("Server", "C++MVC");
    inter->postHandle(req, *resp);
}

void Server::setInterceptor(const string& url, const string& InterceptorName)
{
    interceptorMap.insert(pair<string, string>(url, InterceptorName));
}

void Server::close()
{
    closesocket(m_server);
    WSACleanup();
}

bool Server::Init()
{
    WORD ver = MAKEWORD(2, 2);
    WSADATA wsadata;
    int errFlag = -1;
    errFlag = WSAStartup(ver, &wsadata);
    if (errFlag != 0) return false;
    //检测版本号
    if (LOBYTE(wsadata.wVersion) != 2 || HIBYTE(wsadata.wHighVersion) != 2) {
        WSACleanup();
        return false;
    }
    return true;

}
void Server::threadFunc(SOCKET m_server)
{
    while (1) {
        SOCKADDR_IN m_acc_in;
        int len = sizeof(SOCKADDR);
        SOCKET m_accept = accept(m_server, (sockaddr*)&m_acc_in, &len);
        if (m_accept == SOCKET_ERROR) {
            continue;
        }
        int recv_len = 0;
        char recv_buf[10000];
        recv_len = recv(m_accept, recv_buf, 10000, 0);
        
        //char 转 wchar
        int  unicodeLen = ::MultiByteToWideChar(CP_UTF8, 0, recv_buf, -1, NULL, 0);
        wchar_t* pUnicode = new  wchar_t[unicodeLen];
        memset(pUnicode, 0, unicodeLen * sizeof(wchar_t));
        ::MultiByteToWideChar(CP_UTF8, 0, recv_buf, -1, (LPWSTR)pUnicode, unicodeLen);
        wstring  rt = pUnicode;
        //重设大小
        char* pAscii = new char[recv_len];
        memset(pAscii, 0, sizeof(char) * recv_len);
        strncpy(pAscii, recv_buf, recv_len);
        string lrt(pAscii);
        //解析请求
        Request req(lrt);
        Response resp;
        runRoute(req, &resp);
        cout << "请求地址:" << req.getRequestStatus().Url << endl;
        sendTelegram(m_accept, resp.toString(), 0);
        closesocket(m_accept);
    }
}

int Server::sendTelegram(const SOCKET& accept, const string& info, int flags)
{
    int res = send(accept, info.c_str(), info.size(), flags);
    return res;
}

NAME_SPACE_END()

Interceptor.h

#pragma once
#include "Net.h"
#include "Reflex.h"
using namespace myUtil;
#ifndef _INTERCEPTOR_
#define _INTERCEPTOR_

class Interceptor : public RObject {
public:
    virtual bool preHandle(const Request& request, const Response& response) { return true; }

    virtual void postHandle(const Request& request, const Response& response) {}

    virtual void afterCompletion(const Request& request, const Response& response) {}
};

#endif //!_INTERCEPTOR_

indexInterceptor.h

#pragma once
#include "Interceptor.h"
class IndexInterceptor : public Interceptor {
public:
    bool preHandle(const Request& request, const Response& response) override {
        return false;
    }

    void postHandle(const Request& request, const Response& response) override {}

    void afterCompletion(const Request& request, const Response& response) override {}
};

InterceptorMacro.h

#pragma once
#include "Reflex.h"
#include "indexInterceptor.h"
#define REFLEX_INPERCEPTOR_DECLARE \
REGISTER_REFLEX(IndexInterceptor)

Cookie.h

#pragma once
#ifndef _COOKIE_
#define _COOKIE_
#include <string>
using namespace std;
class Cookie
{
public:
	Cookie() {}
	Cookie(const string& name, const string& value) :
		_name(name), _value(value) {
		_comment = "";
		_path = "";
		_domain = "";
		_version = "";
		_maxAge = 0;
	}
	string getNameValue() const;
	void setNameValue(const string& name, const string& value);
	void setComment(const string& comment);
	void setPath(const string& path);
	void setDomain(const string& domain);
	void setVersion(const string& version);
	void setMaxAge(const int& maxAge);
	string getComment() const;
	string getPath() const;
	string getDomain() const;
	string getVersion() const;
	int getMaxAge() const;
	string toString() const;
private:
	string _name;
	string _value;
	//注释
	string _comment;
	//路径,若要访问的url startwith(path)此cookie携带
	string _path;
	//网站域名
	string _domain;
	string _version;
	//生存时间
	int _maxAge{ 0 };
};

#endif //!_COOKIE_

Cookie.cpp

#include "Cookie.h"

string Cookie::getNameValue() const
{
	return _name + "=" + _value;
}

void Cookie::setNameValue(const string& name, const string& value)
{
	_name = name;
	_value = value;
}

void Cookie::setComment(const string& comment)
{
	_comment = comment;
}

void Cookie::setPath(const string& path)
{
	_path = path;
}

void Cookie::setDomain(const string& domain)
{
	_domain = domain;
}

void Cookie::setVersion(const string& version)
{
	_version = version;
}

void Cookie::setMaxAge(const int& maxAge)
{
	_maxAge = maxAge;
}

string Cookie::getComment() const
{
	return _comment;
}

string Cookie::getPath() const
{
	return _path;
}

string Cookie::getDomain() const
{
	return _domain;
}

string Cookie::getVersion() const
{
	return _version;
}

int Cookie::getMaxAge() const
{
	return _maxAge;
}

string Cookie::toString() const
{
	string res = getNameValue();
	if (_comment != "") res += ";comment=" + _comment;
	if (_path != "") res += ";Path=" + _path;
	if (_domain != "") res += ";Domain=" + _domain;
	if (_version != "") res += ";Version=" + _version;
	res += ";Max-Age=" + _maxAge;
	return res;
}

CController.h

#pragma once
#ifndef _CCONTROLLER_
#define _CCONTROLLER_
#include "Reflex.h"
using namespace myUtil;
class CController : public RObject
{
};
#endif //!_CCONTROLLER_

indexController.h

#pragma once
#include "CController.h"
#include "Net.h"
#include "Reflex.h"
using namespace myUtil;
class indexController : public CController
{
public:
	indexController() {}
	~indexController() {}
	string index(const Request& req, Response* resp);
	string test(const Request& req, Response* resp);
};

indexController.cpp

#include "indexController.h"
#include <fstream>
using namespace std;

string indexController::index(const Request& req, Response* resp)
{
    resp->setResponseStatus("HTTP", 1, 1, 200, "OK");
    resp->setResponseHead("Content-Type", "text/html,charset=UTF-8");
    return "index.html";
}

string indexController::test(const Request& req, Response* resp)
{
    resp->setResponseStatus("HTTP", 1, 1, 200, "OK");
    resp->setResponseHead("Content-Type", "text/html,charset=UTF-8");
    Cookie cookie("test", "test");
    cookie.setDomain("localhost");
    cookie.setMaxAge(10);
    cookie.setPath("/test");
    resp->setCookie(cookie);
    return "test.html";
}

ControllerMacro.h

#pragma once
#include "indexController.h"
#define REFLEX_DECLARE \
REGISTER_REFLEX(indexController)\
REGISTER_REFLEX_METHOD_ARGS(indexController, index, string, Request&, Response*)\
REGISTER_REFLEX_METHOD_ARGS(indexController, test, string, Request&, Response*)

Net.h

#pragma once
#ifndef _NET_
#define _NET_

#include <string>
#include <wtypes.h>
#include <unordered_map>
#include "Util.h"
#include "Cookie.h"
using namespace std;

NAME_SPACE_START(myUtil)

#define BLACK "\r\n"
#define SPACE " "

class Net {
public:
	virtual string toString() = 0;
	virtual vector<Cookie> getCookie() const = 0;
	virtual void setCookie(const Cookie& cookie) = 0;
	Net() {}
protected:
	vector<Cookie> _cookie;
};

struct RequestStatus
{
	string RMethod;
	string Url;
	string ProName;
	short verHigh;
	short verLow;
};

struct ResponseStatus
{
	string ProName;
	short verHigh;
	short verLow;
	short status;
	string statusWord;
};

//请求
class Request : public Net {
public:
	Request();
	Request(const string& sourceStr);
	void setRequestStatus(const string& method = "GET", const string& url = "/", const string& _proName = "HTTP", const short& _verHigh = 1, const short& _verLow = 1);
	void setRequestHead(const string& headKey, const string& headValue);
	void setRequestContent(const string& content);

	RequestStatus getRequestStatus() const;
	string getRequestContent(const string& headKey) const;

	vector<Cookie> getCookie() const override;
	void setCookie(const Cookie& cookie) override;

	string toString() override;
	~Request() {}
private:
	RequestStatus _status;
	unordered_map<string, string> _RequestHead;
	string _RequestContent{ "" };
};

//响应
//结构 状态行, 响应头部, 空行, 响应正文
class Response : public Net {
public:
	Response();
	void setResponseStatus(const string& _proName = "HTTP", const short& _verHigh = 1, const short& _verLow = 1, const short& status = 200, const string& word = "");
	void setResponseHead(const string& headKey, const string& headValue);
	void setResponseContent(const string& content);

	ResponseStatus getResponseStatus() const;
	string getResponseHeadByKey(const string& headKey) const;

	vector<Cookie> getCookie() const override;
	void setCookie(const Cookie& cookie) override;

	string toString() override;
	~Response();
private:
	ResponseStatus _status;
	unordered_map<string, string> _ResponseHead;
	string _ResponseContent{ "" };
};

class Analyse {
public:
	static vector<string> getVectorBySplit(const string& source, const char& ch);
	static unordered_map<string, string> getMapBySplit(const string& source, const char& ch1,const char& ch2);
	static string makeVectorByChar(const vector<string>& v, const char& ch);
	static string makeMapByChars(const unordered_map<string, string>& m, const char& ch1, const char& ch2);
};


NAME_SPACE_END()
#endif //!_NET_

Net.cpp

#include "Net.h"
NAME_SPACE_START(myUtil)

Response::Response()
{
	_status.ProName = "HTTP";
	_status.verHigh = 1;
	_status.verLow = 1;
	_status.status = 200;
	_status.statusWord = "OK";
}

void Response::setResponseStatus(const string& _proName, const short& _verHigh, const short& _verLow, const short& status, const string& word)
{
	_status.ProName = _proName;
	_status.verHigh = _verHigh;
	_status.verLow = _verLow;
	_status.status = status;
	_status.statusWord = word;
}

void Response::setResponseHead(const string& headKey, const string& headValue)
{
	_ResponseHead.insert(pair<string, string>(headKey, headValue));
}

void Response::setResponseContent(const string& content)
{
	_ResponseContent = content;
}

ResponseStatus Response::getResponseStatus() const
{
	return _status;
}

string Response::getResponseHeadByKey(const string& headKey) const
{
	auto it = _ResponseHead.find(headKey);
	if (it == _ResponseHead.end()) return "";
	return (*it).second;
}

vector<Cookie> Response::getCookie() const
{
	return _cookie;
}

void Response::setCookie(const Cookie& cookie)
{
	_cookie.push_back(cookie);
}

string Response::toString()
{
	string res = "";
	res += _status.ProName + "/" + to_string(_status.verHigh) + "." + to_string(_status.verLow)
		+ SPACE + to_string(_status.status) + SPACE + _status.statusWord + BLACK;
	for (auto it = _ResponseHead.begin(); it != _ResponseHead.end(); it++) {
		res += (*it).first + ":" + SPACE + (*it).second + BLACK;
	}
	res += BLACK;
	res += _ResponseContent;
	return res;
}

Response::~Response()
{
}

Request::Request(const string& sourceStr)
{
	int i = 0;
	vector<string> reqGroup = Analyse::getVectorBySplit(sourceStr, '\n');
	//解析状态行
	vector<string> statuses = Analyse::getVectorBySplit(reqGroup[0], ' ');
	_status.RMethod = statuses.at(0);
	_status.Url = statuses.at(1);
	statuses.at(2).pop_back();
	vector<string> verInfo = Analyse::getVectorBySplit(statuses.at(2), '/');
	_status.ProName = verInfo.at(0);
	_status.verHigh = Analyse::getVectorBySplit(verInfo.at(1), '.').at(0).at(0) - '0';
	_status.verLow = Analyse::getVectorBySplit(verInfo.at(1), '.').at(1).at(0) - '0';
	//解析请求头
	for (i = 1; i < reqGroup.size(); i++) {
		if (reqGroup[i] == "\r")break;
		reqGroup[i].pop_back();
		vector<string> temp = Analyse::getVectorBySplit(reqGroup[i], ':');
		_RequestHead.insert(pair<string, string>(temp.at(0), temp.at(1)));
	}
	i++;
	for (i; i < reqGroup.size(); i++) {
		_RequestContent += reqGroup.at(i) + "\n";
	}
}

void Request::setRequestStatus(const string& method, const string& url, const string& _proName, const short& _verHigh, const short& _verLow)
{
	_status.RMethod = method;
	_status.Url = url;
	_status.ProName = _proName;
	_status.verHigh = _verHigh;
	_status.verLow = _verLow;
}

void Request::setRequestHead(const string& headKey, const string& headValue)
{
	_RequestHead.insert(pair<string, string>(headKey, headValue));
}

void Request::setRequestContent(const string& content)
{
	_RequestContent = content;
}

RequestStatus Request::getRequestStatus() const
{
	return _status;
}

string Request::getRequestContent(const string& headKey) const
{
	return _RequestContent;
}

string Request::toString()
{
	string res = "";
	res += _status.RMethod + SPACE + _status.Url + SPACE + _status.ProName + "/"
		+ to_string(_status.verHigh) + "." + to_string(_status.verLow) + BLACK;
	for (auto it = _RequestHead.begin(); it != _RequestHead.end(); it++) {
		res += (*it).first + ":" + SPACE + (*it).second + BLACK;
	}
	res += BLACK;
	res += _RequestContent;
	return res;
}

vector<Cookie> Request::getCookie() const
{
	return _cookie;
}

void Request::setCookie(const Cookie& cookie)
{
	_cookie.push_back(cookie);
}

vector<string> Analyse::getVectorBySplit(const string& source, const char& ch)
{
	vector<string> res;
	string temp = "";
	for (int i = 0; i < source.size(); i++) {
		if (source[i] == ch) {
			res.push_back(temp);
			temp = "";
		}
		else {
			char ch = source[i];
			temp.push_back(ch);
		}
	}
	if (temp != "") res.push_back(temp);
	return res;
}

unordered_map<string, string> Analyse::getMapBySplit(const string& source, const char& ch1, const char& ch2)
{
	unordered_map<string, string> res;
	vector<string> temp = getVectorBySplit(source, ch1);
	for (string str : temp) {
		vector<string> t = getVectorBySplit(str, ch2);
		if (t.size() != 2) continue;
		res.insert(pair<string, string>(t.at(0), t.at(1)));
	}
	return res;
}

string Analyse::makeVectorByChar(const vector<string>& v, const char& ch)
{
	string res = "";
	for (auto str : v) {
		res += str + ch;
	}
	res.pop_back();
	return res;
}

string Analyse::makeMapByChars(const unordered_map<string, string>& m, const char& ch1, const char& ch2)
{
	string res = "";
	for (auto it = m.begin(); it != m.end(); it++) {
		res += it->first + ch2 + it->second + ch1;
	}
	res.pop_back();
	return res;
}
NAME_SPACE_END()

config.ini

[staticResource]
favicon_path=./ico/favicon.ico

使用方式如下

通过setRoute设置路由规则,类似于springboot中的注释部分
通过setInterceptor设置拦截器规则,如果没有设置的话,会默认找基类
以上两个设置之后还要再两个macro文件中设置宏展开,否则反射找不到对应的类,关于反射如何使用请看这个
https://blog.csdn.net/weixin_43891802/article/details/129411364

#include <iostream>
#include <string>
#include "ControllerMacro.h"
#include "InterceptorMacro.h"
#include "Server.h"
using namespace std;
using namespace myUtil;

REFLEX_DECLARE
REFLEX_INPERCEPTOR_DECLARE

int main() {
    Server server("127.0.0.1", 8080);
    server.setRoute("/", "indexController", "index");
    server.setRoute("/test", "indexController", "test");
    //server.setInterceptor("/test", "IndexInterceptor");
    server.listen(2);
    return 0;
}

有关C++ 手撸简易服务器(完善版本)的更多相关文章

  1. ruby - 使用 ruby​​ 和 savon 的 SOAP 服务 - 2

    我正在尝试使用ruby​​和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我

  2. ruby - 具有身份验证的私有(private) Ruby Gem 服务器 - 2

    我想安装一个带有一些身份验证的私有(private)Rubygem服务器。我希望能够使用公共(public)Ubuntu服务器托管内部gem。我读到了http://docs.rubygems.org/read/chapter/18.但是那个没有身份验证-如我所见。然后我读到了https://github.com/cwninja/geminabox.但是当我使用基本身份验证(他们在他们的Wiki中有)时,它会提示从我的服务器获取源。所以。如何制作带有身份验证的私有(private)Rubygem服务器?这是不可能的吗?谢谢。编辑:Geminabox问题。我尝试“捆绑”以安装新的gem..

  3. ruby-on-rails - 项目升级后 Pow 不会更改 ruby​​ 版本 - 2

    我在我的Rails项目中使用Pow和powifygem。现在我尝试升级我的ruby​​版本(从1.9.3到2.0.0,我使用RVM)当我切换ruby​​版本、安装所有gem依赖项时,我通过运行railss并访问localhost:3000确保该应用程序正常运行以前,我通过使用pow访问http://my_app.dev来浏览我的应用程序。升级后,由于错误Bundler::RubyVersionMismatch:YourRubyversionis1.9.3,butyourGemfilespecified2.0.0,此url不起作用我尝试过的:重新创建pow应用程序重启pow服务器更新战俘

  4. ruby-on-rails - 启动 Rails 服务器时 ImageMagick 的警告 - 2

    最近,当我启动我的Rails服务器时,我收到了一长串警告。虽然它不影响我的应用程序,但我想知道如何解决这些警告。我的估计是imagemagick以某种方式被调用了两次?当我在警告前后检查我的git日志时。我想知道如何解决这个问题。-bcrypt-ruby(3.1.2)-better_errors(1.0.1)+bcrypt(3.1.7)+bcrypt-ruby(3.1.5)-bcrypt(>=3.1.3)+better_errors(1.1.0)bcrypt和imagemagick有关系吗?/Users/rbchris/.rbenv/versions/2.0.0-p247/lib/ru

  5. ruby-on-rails - 在 ruby​​ .gemspec 文件中,如何指定依赖项的多个版本? - 2

    我正在尝试修改当前依赖于定义为activeresource的gem:s.add_dependency"activeresource","~>3.0"为了让gem与Rails4一起工作,我需要扩展依赖关系以与activeresource的版本3或4一起工作。我不想简单地添加以下内容,因为它可能会在以后引起问题:s.add_dependency"activeresource",">=3.0"有没有办法指定可接受版本的列表?~>3.0还是~>4.0? 最佳答案 根据thedocumentation,如果你想要3到4之间的所有版本,你可以这

  6. ruby-on-rails - s3_direct_upload 在生产服务器中不工作 - 2

    在Rails4.0.2中,我使用s3_direct_upload和aws-sdkgems直接为s3存储桶上传文件。在开发环境中它工作正常,但在生产环境中它会抛出如下错误,ActionView::Template::Error(noimplicitconversionofnilintoString)在View中,create_cv_url,:id=>"s3_uploader",:key=>"cv_uploads/{unique_id}/${filename}",:key_starts_with=>"cv_uploads/",:callback_param=>"cv[direct_uplo

  7. ruby-on-rails - 如果我将 ruby​​ 版本 2.5.1 与 rails 版本 2.3.18 一起使用会怎样? - 2

    如果我使用ruby​​版本2.5.1和Rails版本2.3.18会怎样?我有基于rails2.3.18和ruby​​1.9.2p320构建的rails应用程序,我只想升级ruby的版本,而不是rails,这可能吗?我必须面对哪些挑战? 最佳答案 GitHub维护apublicfork它有针对旧Rails版本的分支,有各种变化,它们一直在运行。有一段时间,他们在较新的Ruby版本上运行较旧的Rails版本,而不是最初支持的版本,因此您可能会发现一些关于需要向后移植的有用提示。不过,他们现在已经有几年没有使用2.3了,所以充其量只能让更

  8. ruby-on-rails - 获取 inf-ruby 以使用 ruby​​ 版本管理器 (rvm) - 2

    我安装了ruby​​版本管理器,并将RVM安装的ruby​​实现设置为默认值,这样'哪个ruby'显示'~/.rvm/ruby-1.8.6-p383/bin/ruby'但是当我在emacs中打开inf-ruby缓冲区时,它使用安装在/usr/bin中的ruby​​。有没有办法让emacs像shell一样尊重ruby​​的路径?谢谢! 最佳答案 我创建了一个emacs扩展来将rvm集成到emacs中。如果您有兴趣,可以在这里获取:http://github.com/senny/rvm.el

  9. ruby - 用 Ruby 编写一个简单的网络服务器 - 2

    我想在Ruby中创建一个用于开发目的的极其简单的Web服务器(不,不想使用现成的解决方案)。代码如下:#!/usr/bin/rubyrequire'socket'server=TCPServer.new('127.0.0.1',8080)whileconnection=server.acceptheaders=[]length=0whileline=connection.getsheaders想法是从命令行运行这个脚本,提供另一个脚本,它将在其标准输入上获取请求,并在其标准输出上返回完整的响应。到目前为止一切顺利,但事实证明这真的很脆弱,因为它在第二个请求上中断并出现错误:/usr/b

  10. ruby-on-rails - 在 Rails 中调试生产服务器 - 2

    您如何在Rails中的实时服务器上进行有效调试,无论是在测试版/生产服务器上?我试过直接在服务器上修改文件,然后重启应用,但是修改好像没有生效,或者需要很长时间(缓存?)我也试过在本地做“脚本/服务器生产”,但是那很慢另一种选择是编码和部署,但效率很低。有人对他们如何有效地做到这一点有任何见解吗? 最佳答案 我会回答你的问题,即使我不同意这种热修补服务器代码的方式:)首先,你真的确定你已经重启了服务器吗?您可以通过跟踪日志文件来检查它。您更改的代码显示的View可能会被缓存。缓存页面位于tmp/cache文件夹下。您可以尝试手动删除

随机推荐