你的代碼有很多問題。
你的HTTP響應誤格式化:
您Content-Type
頭的格式不正確。 (或6
)而不是5
。
您在Content-Length: ...\r\n
之前和Ahoj
之前缺少額外的所需\r\n
。 HTTP頭由\r\n\r\n
終止。
的\r\n
後Ahoj
需要被刪除,除非您增加Content-Length
至6
保持HTTP連接進行談判,所以不要盲目地在回信一個Connection: keep-alive
頭。您必須檢查客戶端是否發送帶有Connection: keep-alive
標頭的HTTP 1.0請求,或者是否不帶Connection: close
標頭的HTTP 1.1請求,然後相應地執行操作。除非客戶要求您這樣做,否則不要保持連接活動,並且您同意這樣做。請確保將Connection
響應標頭相應地設置爲您實際使用套接字執行的操作。
您沒有正確處理客戶端請求。
首先,recv()
不會返回以空字符結尾的數據,但您的cout
語句假定它有。您需要將輸出限制爲由recv()
報告的實際字節數。使用cout.write()
代替cout << ...
。
但是,更重要的是,您根本沒有實際解析客戶端的HTTP請求。當客戶端發送任何任意數據(不是有效的HTTP請求),你要發送的HTTP響應,但你的send()
的len
參數設置爲從客戶端讀取的字節數,而不是數量你的迴應的字節。
您必須正確解析客戶端的請求!特別是如果你尊重保活。首先閱讀最初的請求行,告訴你所請求的資源以及請求的HTTP版本。然後,逐行讀取請求標題,直到遇到結束標頭的\r\n\r\n
序列。然後,您必須解析標題以確定IF是否存在請求主體,如何讀取它以及何時停止讀取它。有關這方面的具體細節,請參閱RFC 2616 Section 4.4 Message Length。
完整地讀完整個ENTIRE請求並進行驗證後,請發送您的響應(確保相應設置Connection
響應標頭),然後關閉套接字(如果未請求保持活動狀態),否則返回並開始等待下一個請求到達。
send()
可以發送比請求更少的字節,因此如果還有更多數據要發送,則需要檢查返回值並再次調用send()
。您需要循環呼叫send()
,直到完整發送完整的響應。
「將緩衝區回送給發件人」對於HTTP服務器來說不是正確的做法。感覺就像你拿了一個ECHO服務器的例子,並且正在修改它的HTTP。 HTTP比ECHO實現起來要複雜得多。
隨着中說,嘗試更多的東西像這樣(未經測試,但應該給你什麼是參與一個想法):
有很多問題,你的代碼。
你的HTTP響應誤格式化:
您Content-Type
頭的格式不正確。 (或6
)而不是5
。
您在Content-Length: ...\r\n
之前和Ahoj
之前缺少額外的所需\r\n
。 HTTP頭由\r\n\r\n
終止。
的\r\n
後Ahoj
需要被刪除,除非您增加Content-Length
至6
保持HTTP連接進行談判,所以不要盲目地在回信一個Connection: keep-alive
頭。您必須檢查客戶端是否發送帶有Connection: keep-alive
標頭的HTTP 1.0請求,或者是否不帶Connection: close
標頭的HTTP 1.1請求,然後相應地執行操作。除非客戶要求您這樣做,否則不要保持連接活動,並且您同意這樣做。請確保將Connection
響應標頭相應地設置爲您實際使用套接字執行的操作。
您沒有正確處理客戶端請求。
首先,recv()
不會返回以空字符結尾的數據,但您的cout
語句假定它有。您需要將輸出限制爲由recv()
報告的實際字節數。使用cout.write()
代替cout << ...
。
但是,更重要的是,您根本沒有實際解析客戶端的HTTP請求。當客戶端發送任何任意數據(不是有效的HTTP請求),你要發送的HTTP響應,但你的send()
的len
參數設置爲從客戶端讀取的字節數,而不是數量你的迴應的字節。
您必須正確解析客戶端的請求!特別是如果你尊重保活。首先閱讀最初的請求行,告訴你所請求的資源以及請求的HTTP版本。然後,逐行讀取請求標題,直到遇到結束標頭的\r\n\r\n
序列。然後,您必須解析標題以確定IF是否存在請求主體,如何讀取它以及何時停止讀取它。有關這方面的具體細節,請參閱RFC 2616 Section 4.4 Message Length。
完整地讀完整個ENTIRE請求並進行驗證後,請發送您的響應(確保相應設置Connection
響應標頭),然後關閉套接字(如果未請求保持活動狀態),否則返回並開始等待下一個請求到達。
send()
可以發送比請求更少的字節,因此如果還有更多數據要發送,則需要檢查返回值並再次調用send()
。您需要循環呼叫send()
,直到完整發送完整的響應。
「將緩衝區回送給發件人」對於HTTP服務器來說不是正確的做法。感覺就像你拿了一個ECHO服務器的例子,並且正在修改它的HTTP。 HTTP比ECHO實現起來要複雜得多。
隨着中說,嘗試更多的東西像這樣(未經測試,但應該給你什麼是參與一個想法):
#include <iostream>
#include <string>
#include <sstream>
#include <vector>
#include <map>
#include <stdexcept>
#include <algorithm>
// Need to link with Ws2_32.lib
#pragma comment (lib, "Ws2_32.lib")
// #pragma comment (lib, "Mswsock.lib")
#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "8080"
class WinsockError : public std::runtime_error
{
public:
int ErrCode;
std::string FuncName;
WinsockError(const char *func)
: std::runtime_error("Winsock function failed"), ErrCode(WSAGetLastError()), FuncName(func)
{
}
WinsockError(int err, const char *func)
: std::runtime_error("Winsock function failed"), ErrCode(err), FuncName(func)
{
}
};
class WinsockDisconnected : public std::runtime_error
{
public:
bool WasGraceful;
WinsockDisconnected(bool graceful)
: std::runtime_error("socket disconnected"), WasGraceful(graceful)
{
}
};
class JustStopNow : public std::runtime_error
{
public:
JustStopNow()
: std::runtime_error("")
{
}
};
class WSAInit
{
private:
WSAInit(const WSAInit &) {}
WSAInit& operator=(const WSAInit &) { return *this; }
public:
WSAInit() {
WSADATA wsaData;
int iResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iResult != 0) {
throw WinsockError(iResult, "WSAStartup");
}
}
~WSAInit() {
WSACleanup();
}
};
class Socket
{
private:
SOCKET m_sock;
Socket(const Socket &) {}
Socket& operator=(const Socket &) { return *this; }
public:
Socket(SOCKET s = INVALID_SOCKET) : m_sock(s) {}
~Socket() {
Close();
}
void Close() {
if (m_sock != INVALID_SOCKET) {
shutdown(m_sock, SD_BOTH);
closesocket(m_sock);
m_sock = INVALID_SOCKET;
}
}
operator SOCKET() {
return m_sock;
}
bool operator !() {
return (m_sock == INVALID_SOCKET);
}
Socket& operator =(SOCKET s) {
if (m_sock != s) {
close();
m_sock = s;
}
return *this;
}
int ReadSome(void *buffer, int buflen)
{
int iResult = recv(m_sock, buffer, buflen, 0);
if (iResult == SOCKET_ERROR) {
iResult = WSAGetLastError();
switch (iResult) {
case WSAECONNABORTED:
case WSAECONNRESET:
case WSAENETRESET:
throw WinsockDisconnected(false);
}
throw WinsockError(iResult, "recv");
}
if (iResult == 0) {
throw WinsockDisconnected(true);
}
std::cout << "Bytes received: " << iResult << std:::endl;
std::cout.write((char*)recvbuf, iResult);
std::cout << std::endl;
return iResult;
}
void ReadAll(void *buffer, int buflen)
{
unsigned char *pbuffer = (unsigned char *) buffer;
int iResult;
while (buflen > 0) {
iResult = ReadSome(pbuffer, buflen);
pbuffer += iResult;
buflen -= iResult;
}
}
void Send(const std:string &s)
{
Send(s.c_str(), s.size());
}
void Send(const void *buffer, int buflen)
{
const unsigned char *pbuffer = (const unsigned char *) buffer;
int iResult;
while (buflen > 0) {
iResult = send(m_sock, pbuffer, buflen, 0);
if (iResult == SOCKET_ERROR) {
iResult = WSAGetLastError();
switch (iResult) {
case WSAECONNABORTED:
case WSAECONNRESET:
case WSAENETRESET:
throw WinsockDisconnected(false);
}
throw WinsockError(iResult, "send");
}
std::cout << "Bytes send: " << iResult << std:::endl;
std::cout.write((char*)pbuffer, iResult);
std::cout << std::endl;
pbuffer += iResult;
buflen -= iResult;
}
}
};
class AddrInfoPtr
{
private:
struct addrinfo *m_info;
AddrInfoPtr(const AddrInfoPtr &) {}
AddrInfoPtr& operator=(const AddrInfoPtr &) { return *this; }
public:
AddrInfoPtr(struct addrinfo *info = NULL) : m_info(info) {}
~AddrInfoPtr() {
if (m_info) {
freeaddrinfo(m_info);
}
}
operator struct addrinfo*() {
return m_info;
}
struct addrinfo* operator ->() {
return m_info;
}
bool operator !() {
return (m_info == NULL);
}
struct addrinfo** operator & {
return &m_info;
}
};
char UpperCaseChar(char ch)
{
return std::toupper(ch);
}
void UpperCaseStr(std::string &s)
{
std::transform(s.begin(), s.end(), s.begin(), UpperCaseChar);
}
void LTrimLWS(std::string &s)
{
s.erase(0, s.find_first_not_of(" \t"));
}
void RTrimLWS(std::string &s)
{
s.erase(s.find_last_not_of(" \t")+1);
}
void TrimLWS(std::string &s)
{
LTrimLWS(s);
RTrimLWS(s);
}
typedef std::map<std::string, std::string> NameValueMap;
typedef std::vector<unsigned char> ByteVec;
class InputBuffer
{
private:
Socket &m_sock;
ByteVec m_buf;
static void ParseHeader(const std::string &header, NameValueMap &headers)
{
if (header.empty()) {
return;
}
std::string name, value;
std::istringstream iss(header);
std::getline(iss, name, ':');
TrimLWS(name);
std::getline(iss, value);
TrimLWS(value);
UpperCaseStr(name);
NameValueMap::iterator iter = headers.find(name);
if (iter != headers.end())
iter->second += ("," + value);
else
headers.insert(std::make_pair(name, value));
}
public:
InputBuffer(Socket &s) : m_sock(s) {}
bool HasPendingData()
{
return !m_buf.empty();
}
bool FillFromSocket(int Timeout = -1)
{
if (Timeout >= 0) {
fd_set rfd;
FD_ZERO(&rfd);
FD_SET(m_sock, &rfd);
timeval t;
t.tv_sec = Timeout/1000;
t.tv_usec = (Timeout % 1000) * 1000;
switch (select(0, &rfd, NULL, NULL, &t)) {
case SOCKET_ERROR:
throw WinsockError("select");
case 0:
return false;
}
}
unsigned char buf[DEFAULT_BUFLEN];
int iResult = m_sock.ReadSome(buf, sizeof buf);
m_buf.insert(m_buf.end(), &buf[0], &buf[iResult]);
return true;
}
std::string ReadLine()
{
ByteVec::iterator iter = std::find(m_buf.begin(), m_buf.end(), '\n');
while (iter == m_buf.end()) {
ByteVec::size_type offset = m_buf.size();
FillFromSocket();
iter = std::find(m_buf.begin()+offset, m_buf.end(), '\n');
}
std::string::size_type len = std::distance(m_buf.begin(), iter);
if ((len > 0) && (*(iter-1) == '\r')) {
--len;
}
std::string out(m_buf.begin(), m_buf.begin()+len);
m_buf.erase(m_buf.begin(), iter+1);
return out;
}
void ReadHeaders(NameValueMap &headers)
{
std::string line;
std::ostringstream oss;
do {
line = ReadLine();
if (line.empty()) {
break;
}
if ((line[0] == ' ') || (line[0] == '\t')) {
LTrimLWS(line);
oss << ' ' << line;
}
else {
ParseHeader(oss.str(), headers);
RTrimLWS(line);
oss.str(line);
}
}
while (true);
ParseHeader(oss.str(), headers);
}
void Read(void *buffer, int buflen)
{
unsigned char *pbuffer = (unsigned char *) buffer;
while (buflen > 0) {
if (m_buf.empty()) {
FillFromSocket();
}
ByteVec::size_type len = std::max(m_buf.size(), buflen);
ByteVec::iterator start = m_buf.begin();
ByteVec::iterator finish = start+len;
std::copy(start, finish, pbuffer);
m_buf.erase(start, finish);
pbuffer += len;
buflen -= len;
}
}
};
int __cdecl main(void)
{
try {
// Initialize Winsock
TWSAInit wsa;
// Resolve the server address and port
Socket ListenSocket;
{
struct addrinfo hints;
AddrInfoPtr result;
ZeroMemory(&hints, sizeof(hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
if (iResult != 0) {
throw WinsockError(iResult, "getaddrinfo");
}
// Create a TCP listening socket
ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (!ListenSocket) {
throw WinsockError("socket");
}
// Setup the TCP listening socket
iResult = bind(ListenSocket, result->ai_addr, (int)result->ai_addrlen);
if (iResult == SOCKET_ERROR) {
throw WinsockError("bind");
}
}
iResult = listen(ListenSocket, SOMAXCONN);
if (iResult == SOCKET_ERROR) {
throw WinsockError("listen");
std::cout << "Listening" << std::endl;
do {
// Accept a client socket
Socket ClientSocket = accept(ListenSocket, NULL, NULL);
if (!ClientSocket) {
throw WinsockError("accept");
}
std::cout << "Client connected" << std::endl;
// TODO: move the following to a thread so you can
// service multiple clients at the same time...
try {
// read HTTP requests until disconnect
InputBuffer io(ClientSocket);
bool keepAlive;
do {
std::string line, value;
std::string method, resource;
int majorVersion, minorVersion;
NameValueMap headers;
ByteVec body;
std::ostringstream response;
line = io.ReadLine();
// parse request line
{
std::istringstream iss(line);
std::getline(iss, method, ' ');
UpperCaseStr(method);
std::getline(iss, resource, ' ');
std::getline(iss, value);
}
{
std::istringstream iss(value);
std::getline(iss, value, '/');
char dot;
if ((value != "HTTP") ||
!(iss >> majorVersion >> dot >> minorVersion) ||
(dot != '.')) {
response << "HTTP/1.1 400 Bad Request\r\n"
<< "Connection: close\r\n"
<< "Content-Length: 0\r\n"
<< "\r\n";
ClientSocket.Send(response.str());
throw JustStopNow();
}
}
// read request headers
io.ReadHeaders(headers);
// HTTP 1.1+ requires a "Host" header
if (
(majorVersion > 1) ||
((majorVersion == 1) && (minorVersion >= 1))
) {
if (headers["HOST"].empty()) {
response << "HTTP/1.1 400 Bad Request\r\n"
<< "Connection: close\r\n"
<< "Content-Length: 0\r\n"
<< "\r\n";
ClientSocket.Send(response.str());
throw JustStopNow();
}
}
// check if client wants a 100 response before it sends the request body
value = headers["EXPECT"];
UpperCaseStr(value);
if (value.find("100-CONTINUE") != std::string::npos) {
if (!io.HasPendingData()) {
if (!io.FillFromSocket(0)) {
ClientSocket.Send("HTTP/1.1 100 Continue\r\n");
}
}
}
// determine if any body is being sent, and if so then read it
value = headers["TRANSFER-ENCODING"];
UpperCaseStr(value);
if ((!value.empty()) && (value != "IDENTITY")) {
do {
line = io.ReadLine();
std::istringstream iss(line);
ByteVec::size_type size = 0;
if (!(iss >> std::hex >> size)) {
response << "HTTP/1.1 400 Bad Request\r\n"
<< "Connection: close\r\n"
<< "Content-Length: 0\r\n"
<< "\r\n";
ClientSocket.Send(response.str());
throw JustStopNow();
}
if (size > 0) {
ByteVec::size_type offset = body.size();
body.resize(offset+size);
io.Read(&body[offset], size);
io.ReadLine();
}
else if (size == 0) {
break;
}
else {
response << "HTTP/1.1 400 Bad Request\r\n"
<< "Connection: close\r\n"
<< "Content-Length: 0\r\n"
<< "\r\n";
ClientSocket.Send(response.str());
throw JustStopNow();
}
}
while (true);
io.ReadHeaders(headers);
}
else {
value = headers["CONTENT-LENGTH"];
if (!value.empty()) {
std::istringstream iss(value);
ByteVec::size_type size = 0;
if (!(iss >> size)) {
response << "HTTP/1.1 400 Bad Request\r\n"
<< "Connection: close\r\n"
<< "Content-Length: 0\r\n"
<< "\r\n";
ClientSocket.Send(response.str());
throw JustStopNow();
}
if (size > 0) {
body.resize(size);
io.Read(&body[0], size);
}
else if (size != 0) {
response << "HTTP/1.1 400 Bad Request\r\n"
<< "Connection: close\r\n"
<< "Content-Length: 0\r\n"
<< "\r\n";
ClientSocket.Send(response.str());
throw JustStopNow();
}
}
else if ((method == "POST") || (method == "PUT")) {
response << "HTTP/1.1 411 Length Required\r\n"
<< "Connection: close\r\n"
<< "Content-Length: 0\r\n"
<< "\r\n";
ClientSocket.Send(response.str());
throw JustStopNow();
}
}
// determine if a keep-alive is requested
value = headers["CONNECTION"];
UpperCaseStr(value);
keepAlive = (
(majorVersion > 1) ||
((majorVersion == 1) && (minorVersion >= 1))
)
? (value != "CLOSE")
: (value == "KEEP-ALIVE");
// process method, resource, headers, and body as needed
response << "HTTP/1.1 200 OK\r\n";
<< "Connection: " << (keepAlive ? "keep-alive" : "close") << "\r\n"
<< "Content-Type: text/plain\r\n"
<< "Content-Length: 4\r\n"
<< "\r\n"
<< "Ahoj";
ClientSocket.Send(response.str());
}
while (keepAlive);
}
catch (const JustStopNow &) {
}
catch (const WinsockDisconnected &e) {
std::cout << "Client disconnected " << e.WasGraceful ? "gracefully" : "abnormally" << std::endl;
}
catch (const WinsockError &e) {
std::cerr << "Client disconnected because " << e.FuncName << " failed with error: " << e.ErrCode << std::endl;
}
}
while (true);
}
catch (const WinsockError &e) {
std::cerr << e.FuncName << " failed with error: " << e.ErrCode << std::endl;
return 1;
}
return 0;
}
http://samuel-beckett.net/Waiting_for_Godot_Part1.html – user0042
請在** minimal **上創建[MCVE]重點** – bolov
我不明白爲什麼您希望單個應用程序運行單個應用程序,並使用阻止調用來充當服務器及其客戶端。 –