(自用)实现高并发http服务器

   项目需求

需求分析

  1.   何为Html 页面(略)

     html,全称Hypertext Markup Language,也就是“超文本链接标示语言”。HTML文本是由 HTML命令组成的描述性文本,HTML 命令可以说明文字、 图形、动画、声音、表格、链接等。 即平常上网所看到的的网页。

eg:

<html lang=\"zh-CN\">
<head>
<meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">
<title>This is a test</title>
</head>
<body>
<div align=center height=\"500px\" >
<br/><br/><br/>
<h2>大家好,欢迎来到奇牛学院VIP 课!</h2><br/><br/>
<form action="commit" method="post">
尊姓大名: <input type="text" name="name" />
<br/>芳龄几何: <input type="password" name="age" />
<br/><br/><br/><input type="submit" value="提交" />
<input type="reset" value="重置" />
</form>
</div>
</body>
</html>

2.  何为http 协议

     HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。

请求格式:

实现MINI型http服务器 

生命周期

在回声服务器echo_server.c的基础上修改

接收http请求

相关函数
补充:isspace函数——判断是否为空白字符

补充:strncasecmp区分大小写比较字符串

补充:strchr函数——用于在字符串中查找首次出现指定字符的位置

补充:sprintf函数——把格式化的数据写入某个字符串中

sprintf跟printf在用法上几乎一样,只是打印的目的地不同而已,前者打印到字符串中,后者则直接在命令行上输出。

eg:

sprintf的扩展snprintf 

补充:send函数——在socket上送消息,比write多了一个flag

补充:C语言文件流相关

get_line

函数原型:

int get_line(int sock,char *buf,int size)

代码如下:

int get_line(int sock,char *buf,int size)
{
	char ch = '\0';
	int count = 0;//已经读取了的字符个数
	int len;
	while ((count<size-1) && ch!='\n')
	{
		len = read(sock,&ch,1);
		if (len == 1)
		{
			if (ch == '\t')//遇到回车则忽略
			{

				continue;

			}

			else if (ch == '\n')//遇到换行符则结束
			{
				buf[count] = '\0';
				break;
			}
			buf[count] = ch;
			count++;
		}
		else if (len == -1)//读取出错
		{
			perror("read failed.\n");
			return -1;
		}
		else//len = 0  客户端关闭socket链接
		{
			fprintf(stderr,"clien close.\n");
			return -1;
		}
		
	}
	return count;
}
补充:fileno函数

do_http_request

函数原型:

void do_http_request(int client_sock)

循环调用get_line函数读取客户端发过来的请求.

代码如下:

void do_http_request(int client_sock)
{
	int len = 0;
	char buf[256];
	
	/*读取客户端发送的http请求*/
	
	//1.读取请求行
	do{
		len = get_line(client_sock, buf, sizeof(buf));
		printf(" %s \n",buf);
	} while (len > 0);
	


}

思考题?

如果客户端发送的请求是: GET /baidu.html HTTP/1.1\rHost: 47.100.162.191’\r’’\n’ Connection: keep-alive’\r’’\n’ ,

我们仍然想get_line返回正确的三行,又该怎么写代码呢?

(注意: \r 代表 回车符,Ascii码是13 ,\n代表的是换行符,Ascii码是14)

读取文件

stat函数——返回文件的状态信息

函数原型:

#include <sys/types.h>

#include <sys/stat.h>

#include <unistd.h>



int stat(const char *path, struct stat *buf);

int fstat(int fd, struct stat *buf);

int lstat(const char *path, struct stat *buf);

path:

文件的路径

buf:

传入的保存文件状态的指针,用于保存文件的状态

返回值:

成功返回0,文件不存在或是出错返回-1,设置errno

stat结构体定义如下:

struct stat {
               dev_t     st_dev;     /* ID of device containing file */
               ino_t     st_ino;     /* inode number */
               mode_t    st_mode;    /* S_ISREG(st_mode)  是一个普通文件  S_ISDIR(st_mode)  是一个目录*/
               
               nlink_t   st_nlink;   /* number of hard links */
               uid_t     st_uid;     /* user ID of owner */
               gid_t     st_gid;     /* group ID of owner */
               dev_t     st_rdev;    /* device ID (if special file) */
               off_t     st_size;    /* total size, in bytes */
               blksize_t st_blksize; /* blocksize for filesystem I/O */
               blkcnt_t  st_blocks;  /* number of 512B blocks allocated */
               time_t    st_atime;   /* time of last access */
               time_t    st_mtime;   /* time of last modification */
               time_t    st_ctime;   /* time of last status change */
           };

并发处理

pthread_create函数——创建一个新线程,并行的执行任务

函数原型

#include <pthread.h>
	int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);

返回值:成功:0; 失败:错误号。

参数:

pthread_t:当前Linux中可理解为:typedef  unsigned long int  pthread_t;

参数1:传出参数,保存系统为我们分配好的线程ID

参数2:通常传NULL,表示使用线程默认属性。若想使用具体属性也可以修改该参数。

参数3:函数指针,指向线程主函数(线程体),该函数运行结束,则线程结束。

参数4:线程主函数执行期间所使用的参数。

参数3、4详解

1.函数返回类型为void*,参数类型也要为void*

2.具体传参过程

以传递int型参数client_sock为例

int* pclient_sock;
pclient_sock = (int*)malloc(sizeof(int));
*pclient_sock = client_sock;
pthread_create(&thread_id, NULL, do_http_request, (void *)pclient_sock);

a.分配内存

b.存入分配的内存

c.传递内存并转为(void *)

参数接收(函数内部):

int client_sock = *(int*)pclient_sock;

.....使用参数.....

if (pclient_sock)
	free(pclient_sock);//释放分配的内存

a.转换为原类型内存

b.取值并赋值

c.用完之后记得释放内存

代码示例:

while (done)
{
	struct sockaddr_in client;
	int client_sock, len, i;
	char client_ip[64];//存储连接的客户端ip
	char buf[1024];
	char content[1024*1024]="";
	pthread_t thread_id;
	int* pclient_sock;

	socklen_t client_addr_len;
	client_addr_len = sizeof(client);
	client_sock = accept(sock, (struct sockaddr*)&client, &client_addr_len);
	//打印客户端IP地址和端口号
	printf("client ip:%s\t port:%d \n",inet_ntop(AF_INET,&client.sin_addr.s_addr,client_ip,sizeof(client_ip)),ntohs(client.sin_port));
	pclient_sock = (int*)malloc(sizeof(int));
	*pclient_sock = client_sock;
	pthread_create(&thread_id, NULL, do_http_request, (void *)pclient_sock);
	/*do_http_request(client_sock);*/
	//启动线程处理http请求
	//do_http_response(client_sock);
}

服务端从accept函数返回之后,执行pthread_create函数来创建线程执行do_http_request函数

而主线程继续往下执行,进入accept函数等待下一个客户端的连接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值