HTTP协议
基于TCP协议得超文本传送协议。是浏览器与服务器之间得通信协议。
一个连接只能处理一个请求。
特点:
- 支持C/S架构
- 简单快速:客户向服务器请求服务器时,只需传送请求方法和路径 ,常用方法:GET、POST
- 无连接:限制每次连接只处理一个请求
- 无状态:即如果后续处理需要前面的信息,它必须重传,这样可能导致每次连接传送的数据量会增大
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;
}