【Linux网络编程(六)】HTTP协议 Web服务器实现流程

HTTP协议

基于TCP协议得超文本传送协议。是浏览器与服务器之间得通信协议。
一个连接只能处理一个请求。

特点:

  1. 支持C/S架构
  2. 简单快速:客户向服务器请求服务器时,只需传送请求方法和路径 ,常用方法:GET、POST
  3. 无连接:限制每次连接只处理一个请求
  4. 无状态:即如果后续处理需要前面的信息,它必须重传,这样可能导致每次连接传送的数据量会增大





Webserver通信过程

在这里插入图片描述



Web服务器实现

#include <stdio.h>
#include <sys/socket.h> //socket
#include <netinet/in.h> //struct sockaddr_in
#include <arpa/inet.h>  //inet_pton inet_addr
#include <string.h>     //bzero
#include <stdlib.h>     //_exit
#include <pthread.h>    //线程相关函数
#include <unistd.h>

//open
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

char head[] = "HTTP/1.1 200 OK\r\n"				\
				"Content-Type: text/htm1\r\n"	\
				"\r\n";
char err[] = "HTTP/1.1 404 Not Found\r\n"		\
				"Content-Type: text/htm1\r\n"	\
				"\r\n"							\
				"<HTML><BODY>File not found</BODY></HTML>";

typedef struct {
	int cfd;  //存放已连接套接字
	struct sockaddr_in addr;  //存放客户端的信息
}CLIENT_MSG;

void *deal_client_fun(void *arg)
{
	CLIENT_MSG *p = (CLIENT_MSG *)arg;
	
	//打印客户端的信息
	char ip[16] = "";
	unsigned short port = 0;
	inet_ntop(AF_INET, &p->addr.sin_addr.s_addr, ip, 16);
	port = ntohs(p->addr.sin_port);
	printf("%s %hu connected\n", ip, port);

	//获取浏览器的请求
	unsigned char buf[1500] = "";
	recv(p->cfd, buf, sizeof(buf), 0);

	//提取请求中的文件名
	char file_name[512] = "./htm1";  		 //存放本地的路劲
	sscanf(buf, "GET %s", file_name+6);
	printf("##%s##\n", file_name);
	if(file_name[7] == '\0')  //没有提取到文件名
	{
		strcpy(file_name,"./html/index.htm1"); //默认提出的文件
	}
	
	//从本地打开file_name文件
	int fd = open(file_name, O_RDONLY);
	if(fd < 0) //本地没有该文件
	{
		//send 404
		send(p->cfd, err, strlen(err), 0);
		
		//退出线程
		close(p->cfd);
		
		//释放堆区空间
		if(p != NULL)
		{
			free(p);
			p = NULL;
		}
	
		return NULL:
	}

	//本地文件打开成功
	//send 200
	send(p->cfd, head, strlen(head), 0);
	
	//循环读取本地文件 发送给浏览器
	while(1)
	{
		unsigned char file_data[1024] = "";
		//读取文本文件数据
		int len = read(fd, file_data, sizeof(file_data));
		//将file_data发送给浏览器
		send(p->cfd, file_data, len, 0);
		
		if(len < 1024)
			break;	
	}

	//释放堆区空间
	close(p->cfd);
	if(p != NULL)
	{
		free(p);
		p = NULL;
	}

	//线程结束
	pthread_exit(NULL);

	return NULL:
}

	//1. 创建tcp监听套接字
	int lfd = socket(AF_INET, SOCK_STREAM, 0);
	if(lfd < 0)
	{
		perror("socket")
		_exit(-1);
	}
	
	//设置端口复用
	int opt = 1;
	setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

	//2. bind给服务器的监听套接字绑定固定的IP、port
	struct sockaddr_in my_addr;
	bzero(&my_addr, sizeof(my_addr));
	my_addr.sin_family = AF_INET;
	my_addr.sin_port = htons(atoi(argv[1]));
	my_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	int ret = bind(lfd, (struct sockaddr *)&my_addr, sizeof(my_addr));
	if(ret < 0)
	{
		perror("bind");
		_exit(-1);
	}

	//3. listen将服务器的套接字主动变被动 创建连接队列 进行监听
	ret = listen(lfd, 128)
	if(ret < 0)
	{
		perror("listen");
		_exit(-1);
	}

	//4. while-->accept提取客户端
	while(1)
	{
		struct sockaddr_in cli_addr;
		socklen_t cli_len = sizeof(cli_addr);
		int cfd = accept(lfd, (struct sockaddr *)&cli_addr, &cli_len);

		CLIENT_MSG *p = (CLIENT_MSG *)calloc(1, sizeof(CLIENT_MSG));
		p->cfd = cfd;
		p->addr = cli_addr;

		//5. 一个客户端创建一个线程
		pthread_t tid;
		pthread_create(&tid, NULL, deal_client_fun, (void *)p);
		//线程分离
		pthread_detach(tid);
	}

	//关闭监听套接字
	close(lfd);
	return 0;
}
	

	



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Eiker_3169

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值