项目需求
需求分析
- 何为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’\r’Host: 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函数等待下一个客户端的连接