写一个最简单的web服务器

基于TCP的SOCKET编程模型如下:
基于TCP的SOCKET编程模型
客户端我们不用管,直接使用浏览器做测试

服务器通过bind(),listen(),accept确定链接,然后用recv()处理浏览器发送的GET请求

GET request 格式如下:(不同浏览器请求不一样,可以全部打出来自己研究)
GET /filename HTTP/1.1
Host: localhost:8888
Connection: keep-alive
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1 …

其中最重要的就是filename,他决定了访问服务器的哪个文件,我们需要利用字符串操作把这部分取出来。如果浏览器中没有指定访问哪个文件,这部分会是空值,我们需要自己指定默认访问哪个文件。因此,我写了如下的函数

void GetPage(const char* recv_buff, const int length, string& filename)
{
	string buff = recv_buff;
	int location = buff.find(" HTTP", 5); //filename starts at 5th byte of buffer
	filename = string(buff, 5, location - 5);

	if (filename == "") //default page
		filename = "1.HTML";
}

作为服务器,我们也需要返回GET response, 需要填充 response 头,response 头 格式如下:
HTTP/1.1 CODE CODEDESCRIPTION \r\n
Content-Type: text/html\r\n
Accept-Ranges: bytes\r\n
Content-Length: %d\r\n\r\n;

CODE和CODEDESCRIPTION中最常见的就是 200 OK和 404 NOT FOUND
因此我们准备如下2个头

const char OKHeadFormat[] =
"HTTP/1.1 200 OK \r\n"
"Content-Type: text/html\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: %d\r\n\r\n";

const char NotFoundHeadFormat[] =
"HTTP/1.1 404 NOT FOUND \r\n"
"Content-Type: text/html\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: 0\r\n\r\n";

其中OKHead需要继续填充发送的长度,使用时需要调用sprintf函数填充

然后还需要一点文件操作,把HTML文件打开后读入buffer中,通过上面Getpage函数读到的文件名去找,如果找到了返回1,并引用写入buffer,填充并发送200头,如果找不到返回0,发送404头

int FileToBuffer(const string filename, char * &buffer, int &bufferlength)
{
	FILE* pFile = NULL;
	fopen_s(&pFile, filename.data(), "r");
	if (pFile == NULL)
		return 0;
	fseek(pFile, 0, SEEK_END);//move fseek to EOF
	bufferlength = ftell(pFile); //This is the length of file
	bufferlength++; // place for'\0'
	fseek(pFile, 0, SEEK_SET); //move back
	
	fread(buffer, bufferlength, 1, pFile); //read file to buff
	fclose(pFile);
	return 1;
}

测试:
我写了1.HTML和2.HTML放在server.CPP所在目录中,端口设置为8888,打开Chrome进行访问
访问默认页面:(chrome中端口后的/可以省略)
访问默认页面
访问指定页面:
访问1.html
访问2.html
访问不存在的页面:
访问不存在的页面

完整代码如下:

//Server.cpp
//Author MaverickY
//version 00

#define _WINSOCK_DEPRECATED_NO_WARNINGS 1
#define _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <WinSock2.h>
#include <cstring>
#include <string>
#pragma comment (lib, "Ws2_32.lib")

using namespace std;

const char OKHeadFormat[] =
"HTTP/1.1 200 OK \r\n"
"Content-Type: text/html\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: %d\r\n\r\n";

const char NotFoundHeadFormat[] =
"HTTP/1.1 404 NOT FOUND \r\n"
"Content-Type: text/html\r\n"
"Accept-Ranges: bytes\r\n"
"Content-Length: 0\r\n\r\n";

const int PORT = 8888;
const int BUFFER_MAX = 5000;

int FileToBuffer(const string filename, char*& buffer, int& bufferlength);
void GetPage(const char* recv_buff, const int length, string& page);

int main()
{
	
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2, 2), &wsaData);

	SOCKET s = socket(AF_INET, SOCK_STREAM, 0);

	struct sockaddr_in serveraddr;
	memset((void*)& serveraddr, 0, sizeof(serveraddr));
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_addr.s_addr = inet_addr("127.0.0.1");
		serveraddr.sin_port = htons(PORT);

	bind(s, (struct sockaddr*) & serveraddr, sizeof(serveraddr));
	listen(s, SOMAXCONN);

	while (1)
	{
		SOCKET ss = accept(s, NULL, NULL);
		cout << "accept one\n" << endl;

		string filename;
		char recvbuf[BUFFER_MAX];
		recv(ss, recvbuf, sizeof(recvbuf), 0);

		GetPage(recvbuf,strlen(recvbuf),filename);

		char Head[BUFFER_MAX];		

		int send_buffer_length = BUFFER_MAX;
		char* send_buffer = (CHAR*)malloc(send_buffer_length);
		memset(send_buffer, 0, send_buffer_length); // set pszBuff all to 0
		
		if (FileToBuffer(filename, send_buffer, send_buffer_length))
		{
			//send head 
			sprintf(Head, OKHeadFormat, send_buffer_length);
			int byte_sent = send(ss, Head, strlen(Head), 0);
			cout << "200 head sent" << endl;
			//send content
			byte_sent = send(ss, send_buffer, send_buffer_length, 0);
			cout << byte_sent << "bytes have been sent" << endl;
		}
		else // return 404
		{
			//send head only
			sprintf(Head, NotFoundHeadFormat);
			int byte_sent = send(ss, Head, strlen(Head), 0);
			cout << "404 head sent" << endl;
		}
		closesocket(ss);
	}
	

	int Err = WSAGetLastError();
	cout << Err << endl;

	WSACleanup();

	return 0;
}



int FileToBuffer(const string filename, char * &buffer, int &bufferlength)
{
	FILE* pFile = NULL;
	fopen_s(&pFile, filename.data(), "r");
	if (pFile == NULL)
		return 0;
	fseek(pFile, 0, SEEK_END);//move fseek to EOF
	bufferlength = ftell(pFile); //This is the length of file
	bufferlength++; // place for'\0'
	fseek(pFile, 0, SEEK_SET); //move back
	
	fread(buffer, bufferlength, 1, pFile); //read file to buff
	fclose(pFile);
	return 1;
}

void GetPage(const char* recv_buff, const int length, string& filename)
{
	string buff = recv_buff;
	int location = buff.find(" HTTP", 5);//filename starts at 5th byte of buffer
	filename = string(buff, 5, location - 5);

	if (filename == "")//default page
		filename = "1.HTML";

	/*
	NOTE:Get head is in this format
		GET /filename HTTP/1.1
		Host: localhost:8888
		Connection: keep-alive
		Cache-Control: max-age=0
		Upgrade-Insecure-Requests: 1 ...
	*/
}

觉得有帮助请点个赞,谢谢

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值