采用TCP/IP协议完成,涉及的头文件如下
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <fcntl.h>
- socket()套接字创建:首先在主函数中定义socket套接字,断言套接字创建成功。
int sockfd = socket(AF_INET,SOCK_STREAM,0); assert( sockfd != -1 );
分别定义两个socket_in结构体,saddr代表web服务器,caddr代表浏览器,设置web服务器端对应的信息,
协议族:AF_INET网络协议套接字类型即ipv4协议族,
端口号:80,
IP地址:ifconfig查看本地IP地址
struct sockaddr_in saddr, caddr;
memset(&saddr,0,sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(80);//http
saddr.sin_addr.s_addr = inet_addr("192.168.43.245");
bind()绑定:将相应的地址结构中的地址信息与套接字进行绑定。成功返回0,失败返回-1并设置error,断言绑定成功。
int res = bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
assert( res != -1 );
listen():将套接字设为监听模式,以及监听队列的最大长度5。
listen(sockfd,5);
while循环
accept():接收浏览器的链接请求,执行系统调用的监听套接字,获取浏览器(caddr)socket的地址和长度。成功返回新的socket,该socket唯一标识这个链接,使用整形变量来接收,失败返回-1,程序退出本次循环。
如果链接成功,打印请求链接方的IP地址
int len = sizeof(caddr);
int c = accept(sockfd,(struct sockaddr*)&caddr,&len);
if ( c < 0 )
{
continue;
}
printf("accept ip:%s\n",inet_ntoa(caddr.sin_addr));
recv(): 创建空字符串接收浏览器发送的数据,即请求报头,如果失败返回-1,关闭连接退出本次循环。读取成功,打印接收到的数据,即浏览器的请求报头。
char recvbuff[1024] = {0};
if ( recv(c,recvbuff,1023,0) <= 0 )
{
close(c);
continue;
}
printf("recv:\n%s\n",recvbuff);
从接收到的请求报头中,获取请求方法,并设置即将执行的html文件名。
void get_filename(char buff[],char filename[])
{
if ( buff == NULL || filename == NULL )
{
return;
}
char * s = strtok(buff," ");
if ( s == NULL )
{
return;
}
printf("method:%s\n",s);//GET
s = strtok(NULL," ");
if ( s == NULL || strcmp(s,"/") == 0 )
{
strcpy(filename,"/index.html");
return;
}
strcpy(filename,s);
return ;
}
当判断文件名为空时,回复报头,提示“NO filename”并关闭连接
char filename[128] = {0};
get_filename(recvbuff,filename);//HTTP请求报文
if ( filename[0] == 0 )//没有接收到文件
{
char sendbuff[512] = {0};
strcpy(sendbuff,"HTTP/1.1 200 OK\r\n");
strcat(sendbuff,"Server: myhttp\r\n");//strcat 拼接字符串
strcat(sendbuff,"Content-Length: 12\r\n");
strcat(sendbuff,"\r\n");//头部字段结束后需要添加一个空行
strcat(sendbuff,"NO filename!");
send(c,sendbuff,strlen(sendbuff),0);
close(c);
continue;
}
接收到文件名成功,给出指定路径,将路径与文件名拼接,成为一个完整的可访问的path路径,
char path[256] = {"/home/tulunwang/src/myhttp"};
strcat(path,filename);
printf("path=%s\n",path);
以只读的方式打开文件, 如果文件打开失败,即在给定的路径下资源没找到,回复报头,提示“not find file”,并关闭链接
int fd = open(path,O_RDONLY);
if ( fd == -1 ) //在给定路径下资源没找到
{
char sendbuff[512] = {0};
strcpy(sendbuff,"HTTP/1.1 404 Not Found\r\n");
strcat(sendbuff,"Server: myhttp\r\n");
strcat(sendbuff,"Content-Length: 14\r\n");
strcat(sendbuff,"\r\n");
strcat(sendbuff,"not find file!");
send(c,sendbuff,strlen(sendbuff),0);
close(c);
continue;
}
文件打开成功,使用lseek()函数计算目标文件的大小,一起合并在应答报文中,输出应答报头,同时
send()函数将报头发给浏览器
int size = lseek(fd,0,SEEK_END);
lseek(fd,0,SEEK_SET);
char sendbuff[512] = {0};
strcpy(sendbuff,"HTTP/1.1 200 OK\r\n");//成功,已规避两种风险
strcat(sendbuff,"Server: myhttp\r\n");
sprintf(sendbuff+strlen(sendbuff),"Content-Length:%d\r\n",size);
//strcat(sendbuff,"Content-Length: 10\r\n");
strcat(sendbuff,"\r\n");
printf("sendbuff:\n%s\n",sendbuff);
send(c,sendbuff,strlen(sendbuff),0);
将使用read()函数,读取文件内容,通过send()函数将HTML内容发送给浏览器
char databuff[1024] = {0};
int num = 0;
while( (num = read(fd,databuff,1024)) > 0 )
{
send(c,databuff,num,0);
}
close(fd)关闭文件描述符
close(c)关闭套接字链接
close(fd);
close(c);