基于c++的简易web服务器搭建(初尝socket编程)

基于c++的简易web服务器搭建(初尝socket编程),第1张

实验的目的与要求:

用 Socket 编程实现一个 Web 服务器(端口号 8080) 实验要求:

  1. 该 Web 服务器在一台主机上运行,支持多台主机同时访问
  2. 有服务控制界面,开启和关闭按钮
  3. 能够指定主页(index.html) 测试方法:
    在同组其他主机上,打开浏览器,输入 http://主机 IP 地址:8080,浏览器上显示主页
    完成实验报告
系统设计原理

ocket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。
socket起源于Unix,而Unix/Linux基本哲学之一就是“一切皆文件”,都可以用“打开open –> 读写write/read –> 关闭close”模式来 *** 作。我的理解就是Socket就是该模式的一个实现,socket即是一种特殊的文件,一些socket函数就是对其进行的 *** 作(读/写IO、打开、关闭),这些函数我们在后面进行介绍。
将socket通信类比为打电话这一生活场景。这里我把TCP服务器比作政府某一服务部门能,TCP客户端比作企业中某一部门电话,描述这一过程,恰好就像是socket通信,服务部门提供服务,企业部门申请服务。


socket中TCP的三次握手建立连接
我们知道tcp建立连接要进行“三次握手”,即交换三个分组。大致流程如下:
客户端向服务器发送一个SYN J
服务器向客户端响应一个SYN K,并对SYN J进行确认ACK J+1
客户端再想服务器发一个确认ACK K+1

从图中可以看出,当客户端调用connect时,触发了连接请求,向服务器发送了SYN J包,这时connect进入阻塞状态;服务器监听到连接请求,即收到SYN J包,调用accept函数接收请求向客户端发送SYN K ,ACK J+1,这时accept进入阻塞状态;客户端收到服务器的SYN K ,ACK J+1之后,这时connect返回,并对SYN K进行确认;服务器收到ACK K+1时,accept返回,至此三次握手完毕,连接建立。

总结:客户端的connect在三次握手的第二个次返回,而服务器端的accept在三次握手的第三次返回,这也是dos攻击的基本原理。
socket中TCP的四次握手释放连接
上面介绍了socket中TCP的三次握手建立过程,及其涉及的socket函数。现在我们介绍socket中的四次握手释放连接的过程,请看下图:

图示过程如下:
某个应用进程首先调用close主动关闭连接,这时TCP发送一个FIN M;
另一端接收到FIN M之后,执行被动关闭,对这个FIN进行确认。它的接收也作为文件结束符传递给应用进程,因为FIN的接收意味着应用进程在相应的连接上再也接收不到额外数据;
一段时间之后,接收到文件结束符的应用进程调用close关闭它的socket。这导致它的TCP也发送一个FIN N;
接收到这个FIN的源发送端TCP对它进行确认。
这样每个方向上都有一个FIN和ACK。
socket通信流程
socket是"打开—读/写—关闭"模式的实现,以使用TCP协议通讯的socket为例,其交互流程基本如下图所示:

实验过程

1、导入相关头文件

#include
#include
#include // 包含网络编程头文件,进入静态库
#pragma comment(lib,"ws2_32.lib")
#include//多线程相关

2、申请socket

WSADATA wsdata;
	int is_ok = WSAStartup(MAKEWORD(2,2), &wsdata); // 确定socke版本信息

3、创建socket,并设置监听ip和端口以及设置最大连接数量

	/*
	param1:协议族 -> socket的地址类型	af_inet使用ipv4地址
	param2: 传输类型 SOCK_STREAM 流传输
	param3: 指定的传输协议 tcp
	*/
	SOCKET server =  socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); // af_inet使用ipv4地址
	merror(server,INVALID_SOCKET, "创建socket失败");

	SOCKADDR_IN seraddr;
	seraddr.sin_family = AF_INET; // 和创建时一样 ipv4
	seraddr.sin_port = htons(80); //网络是大端存储, pc是小端存储,需要转换
	seraddr.sin_addr.s_addr = INADDR_ANY;// 监听任意地址
	is_ok = bind(server,(SOCKADDR *)&seraddr,sizeof(seraddr));
	merror(is_ok, SOCKET_ERROR, "socket绑定失败!");

	is_ok = listen(server, 5); // 客户端连接数量

4、创建客户机socket对象,等待连接该客户机对象和服务端对象

	SOCKADDR_IN claddr; // 客户端信息
	int cllen = sizeof(claddr);

	while (1) {
		SOCKET client = accept(server,(SOCKADDR *) &claddr,&cllen); // 谁连进来了
		merror(client, INVALID_SOCKET, "连接失败!");

		char revdata[1024] = "";
		recv(client, revdata, 1024, 0);
		cout << "接收到的数据:" << revdata << endl;
		cout << "共接收数据大小" << strlen(revdata) << endl;
		//char senddata[1024] = "hello this is server end";
		//send(client, senddata, strlen(senddata), 0);
		char *filename = (char*)"index.html";
		//sendhtml(client, filename);
		//开启新线程
		HANDLE hTread = (HANDLE)_beginthreadex(NULL, 0, RequestHandler, (void*)client, 0, (unsigned*)&dwThreadTD);

	}


接收到的客户端信息

5、连接成功后就可以向客户端发送我们指定的index.html文件了

unsigned WINAPI RequestHandler(void* arg)
{
	SOCKET client = (SOCKET)arg;
	char *fileName = (char*)"index.html";
	sendhtml(client, fileName);
	closesocket(client);
	return 0;
}

void sendhtml(SOCKET s, char *filename) {
	char protocol[] = "HTTP/1.0 200 OK\r\n";
	char serName[] = "server:simple web server\r\n";
	char cntLen[] = "Conten-length:2048\r\n";
	char cntType[100];
	sprintf(cntType, "Content-type:%s\r\n\r\n", "text/html");
	cout << cntType << endl;

	send(s, protocol, strlen(protocol), 0);
	send(s, serName, strlen(serName), 0);
	send(s, cntLen, strlen(cntLen), 0);
	send(s, cntType, strlen(cntType), 0);

	FILE* pfile = fopen(filename, "r");
	if (pfile == NULL) {
		cout << "can not open the file!" << endl;
		return;
	}
	char temp[1024] = "";
	do {
		fgets(temp, 1024,pfile);
		send(s, temp, strlen(temp), 0);
	} while (!feof(pfile));
	
}

index.html文件内容如下所示




  
  
  黑白左右分色创意时钟 - 桌面时钟
  
  
  
  



  
    
      26 . December
      10:33 AM
      Sunday
    
    
      
      123456789101112
      01234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
      01234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
      01234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
    
  
  
  




实验结果

电脑端:

手机端:

平板端:

经过测试,本次实验成功完成。

实验代码

最后附上全部实验代码

#include
#include
#include // 包含网络编程头文件,进入静态库
#pragma comment(lib,"ws2_32.lib")
#include//多线程相关
using namespace std;
unsigned WINAPI RequestHandler(void* arg);
int merror(int redata, int error,const char* showinfo) {
	if (redata == error) {
		perror(showinfo);
		getchar();
		return -1;
	}
}
void sendhtml(SOCKET s, char *filename);
int main() {
	DWORD dwThreadTD;

	printf("welcome to my web server!\n");
	WSADATA wsdata;
	int is_ok = WSAStartup(MAKEWORD(2,2), &wsdata); // 确定socke版本信息
	merror(is_ok, WSAEINVAL,"申请socket失败!");
	/*
	param1:协议族 -> socket的地址类型	af_inet使用ipv4地址
	param2: 传输类型 SOCK_STREAM 流传输
	param3: 指定的传输协议 tcp
	*/
	SOCKET server =  socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); // af_inet使用ipv4地址
	merror(server,INVALID_SOCKET, "创建socket失败");

	SOCKADDR_IN seraddr;
	seraddr.sin_family = AF_INET; // 和创建时一样 ipv4
	seraddr.sin_port = htons(80); //网络是大端存储, pc是小端存储,需要转换
	seraddr.sin_addr.s_addr = INADDR_ANY;// 监听任意地址

	is_ok = bind(server,(SOCKADDR *)&seraddr,sizeof(seraddr));
	merror(is_ok, SOCKET_ERROR, "socket绑定失败!");

	is_ok = listen(server, 5); // 客户端连接数量
	merror(is_ok, SOCKET_ERROR, "监听失败!");

	SOCKADDR_IN claddr; // 客户端信息
	int cllen = sizeof(claddr);

	while (1) {
		SOCKET client = accept(server,(SOCKADDR *) &claddr,&cllen); // 谁连进来了
		merror(client, INVALID_SOCKET, "连接失败!");

		char revdata[1024] = "";
		recv(client, revdata, 1024, 0);
		cout << "接收到的数据:" << revdata << endl;
		cout << "共接收数据大小" << strlen(revdata) << endl;
		//char senddata[1024] = "hello this is server end";
		//send(client, senddata, strlen(senddata), 0);
		char *filename = (char*)"index.html";
		//sendhtml(client, filename);
		//开启新线程
		HANDLE hTread = (HANDLE)_beginthreadex(NULL, 0, RequestHandler, (void*)client, 0, (unsigned*)&dwThreadTD);

	}
	closesocket(server);
	WSACleanup();

	getchar();
	return 0;
}


unsigned WINAPI RequestHandler(void* arg)
{
	SOCKET client = (SOCKET)arg;
	char *fileName = (char*)"index.html";
	sendhtml(client, fileName);
	closesocket(client);
	return 0;
}

void sendhtml(SOCKET s, char *filename) {
	char protocol[] = "HTTP/1.0 200 OK\r\n";
	char serName[] = "server:simple web server\r\n";
	char cntLen[] = "Conten-length:2048\r\n";
	char cntType[100];
	sprintf(cntType, "Content-type:%s\r\n\r\n", "text/html");
	cout << cntType << endl;

	send(s, protocol, strlen(protocol), 0);
	send(s, serName, strlen(serName), 0);
	send(s, cntLen, strlen(cntLen), 0);
	send(s, cntType, strlen(cntType), 0);

	FILE* pfile = fopen(filename, "r");
	if (pfile == NULL) {
		cout << "can not open the file!" << endl;
		return;
	}
	char temp[1024] = "";
	do {
		fgets(temp, 1024,pfile);
		send(s, temp, strlen(temp), 0);
	} while (!feof(pfile));
	
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1295781.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-10
下一篇 2022-06-10

发表评论

登录后才能评论

评论列表(0条)

保存