这里的html的页面可以自己去搜索一个,但是需要注意的是在linux系统上的文件路径和代码上需要一致,不然会报错!!!
#include<stdio.h>
#include<errno.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<string.h>
#include<ctype.h>
#include<arpa/inet.h>
#include<string.h>
#include <sys/stat.h>
#include<pthread.h>
#define SERVER_PORT 80
//#define IP "1.1.1.1"
static int debug = 1;
void * do_http_request(void *client_Sock);
int get_line(int client_Sock,char *buf,int buf_size);
void do_http_response(int client_Sock,const char *path);
void not_found(int client_Sock);//404
void unimplemented(int client_Sock);//501
void bad_request(int client_Sock);//400
int headers(int client_Sock,FILE*resource);
void cat(int client_Sock,FILE*resource);
void innter_error(int client_Sock);
//出错报错函数
void perror_fun(const char*des){
fprintf(stderr,"%s error, reason: %s!!!\n",des,strerror(errno));
exit(1);
}
int main(void){
int sock;//信箱
int ret=0;
struct sockaddr_in server_addr;
pthread_t id;
int *pclient_sock=NULL;
//创建信箱
sock=socket(AF_INET,SOCK_STREAM,0);
if(sock==-1){
perror_fun("sock");
}
//选择协议IPV4
server_addr.sin_family=AF_INET;
//inet_pton(AF_INET,IP,&server_addr.sin_addr.s_addr);
//监听本地所有IP地址
server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
//绑定端口
server_addr.sin_port=htons(SERVER_PORT);
//把标签绑定到收信的信箱上
ret = bind(sock,(struct sockaddr*)&server_addr,sizeof(server_addr));
if(ret==-1){
perror_fun("bind");
}
//把信箱挂到传达室,这样就可以接收信件了
ret = listen(sock,0);
if(ret==-1){
perror_fun("listen");
}
//等待来信
printf("等待客户端的连接!!!!\n");
while(1){
struct sockaddr_in client;
int client_Sock=0;
int buf_len=0;
char client_ip[64]={0};
char buf[256]={0};
socklen_t client_addr_len;
client_addr_len=sizeof(client);
client_Sock = accept(sock,(struct sockaddr*)&client,&client_addr_len);
//打印客户端的IP地址和端口
printf("客户端已连接: ip:%s\t port:%d\n",
inet_ntop(AF_INET,&client.sin_addr.s_addr,client_ip,sizeof(client_ip)),
ntohs(client.sin_port));
//处理http请求,读取客户端发送的数据
//do_http_request(client_Sock);
//启动线程处理http请求
pclient_sock = (int*)malloc(sizeof(int));
*pclient_sock = client_Sock;
pthread_create(&id,NULL,do_http_request,(void*)pclient_sock);
//close(client_Sock);
}
close(sock);
return 0;
//在Windows下 使用telnet + ip + 端口 测试
}
void * do_http_request(void * pclient_Sock){
int len=0;
char buf[256]={0};
char method[64]={0};
char url[256]={0};
char path[512]={0};
struct stat st;
int client_Sock=*(int*)pclient_Sock;
//读取客户端发送的http请求
//1.读取请求行
len = get_line(client_Sock,buf,sizeof(buf));
if(len>0){//读到请求行
int i=0,j=0;
while(!isspace(buf[j])&&i<sizeof(method)-1){//isspace(表示白空格)
method[i]=buf[j];
i++;
j++;
}
method[i]='\0';//在行尾添加结束符
if(debug){
printf("request method: %s\n",method);
}
if(strncasecmp(method,"GET",strlen(method))==0){//只处理get请求
if(debug){
printf("method = GET\n");
}
//获取URL
while(isspace(buf[j++]));//跳过白空格
i=0;
while(!isspace(buf[j])&&i<sizeof(url)-1){//isspace(表示白空格)
url[i]=buf[j];
i++;
j++;
}
url[i]='\0';
if(debug){
printf("URL: %s\n",url);
}
//继续读取http头部
do{
len = get_line(client_Sock,buf,sizeof(buf));
if(debug){
printf("read : %s\n",buf);
}
}while(len>0);
/**定位服务器本地的html文件**/
//处理url中的?号
{
char *pos = strchr(url,'?');//判断是否为'?'
if(pos){//如果是
*pos='\0';
printf("real url: %s\n",url);
}
}
sprintf(path,"./demo_http/%s",url);
if(debug){
printf("path :%s\n",path);
}
//执行http响应
//判断文件是否存在,如果存在就响应200 OK 同时发送相应的html文件,不存在就响应404 NOT FOUND
if(stat(path,&st)==-1){//文件不存在或是出错
fprintf(stderr,"stat %s failed, reason:%s\n",path,strerror(errno));
not_found(client_Sock);
}else{//文件存在
if(S_ISDIR(st.st_mode)){
strcat(path,"/index.html");
}
do_http_response(client_Sock,path);
}
}else{//非get请求,读取http头部,并响应客户端 501 method not implemented
//继续读取http头部
do{
fprintf(stderr,"other request: %s\n",method);
len = get_line(client_Sock,buf,sizeof(buf));
if(debug){
printf("read : %s\n",buf);
}
}while(len>0);
unimplemented(client_Sock); //请求未实现
}
}else{//请求格式有问题,出错处理
bad_request(client_Sock); //在响应时再实现
}
close(client_Sock);
if(pclient_Sock){
free(pclient_Sock);
}
return NULL;
}
void do_http_response(int client_Sock,const char *path){
int ret = 0;
FILE *resource = NULL;
resource = fopen(path,"r");
if(resource==NULL){
not_found(client_Sock);
return;
}
//1.发送http头部
ret = headers(client_Sock,resource);
//2.发送http body.
if(!ret){
cat(client_Sock,resource);
}
fclose(resource);
}
int headers(int client_Sock,FILE*resource){
struct stat st;
int file_id=0;
char tmp[64]={0};
char buf[1024]={0};
//http头部
strcpy(buf,"HTTP/1.0 200 OK\r\n");
strcat(buf,"server: liangheng serverver\r\n");
strcat(buf,"content-type: text/html\r\n");
strcat(buf,"connection: close\r\n");
file_id = fileno(resource);
if(fstat(file_id,&st)==-1){
innter_error(client_Sock);
exit(-1);
}
snprintf(tmp,64,"Content-Length: %ld\r\n\r\n",st.st_size);
strcat(buf,tmp);
if(debug){
fprintf(stdout,"header:%s\n",buf);
}
if(send(client_Sock,buf,strlen(buf),0)<0){
fprintf(stderr,"send failed data:%s\n reason :%s\n",buf,strerror(errno));
return -1;
}
return 0;
}
//实现html文件的内容按行读取并送给客户端
void cat(int client_Sock,FILE*resource){
char buf[1024]={0};
fgets(buf,sizeof(buf),resource);
while(!feof(resource)){
int len = write(client_Sock,buf,strlen(buf));
if(len<0){//发送body的过程中出现问题,怎么办? 1.重试
fprintf(stderr,"send body error, reason:%s\n",strerror(errno));
break;
}
if(debug){
fprintf(stdout,"%s\n",buf);
}
fgets(buf,sizeof(buf),resource);
}
}
/*
void do_http_response(int client_Sock){
const char *main_header="HTTP/1.0 200 OK\r\n server: liangheng serverver\r\ncontent-type: text/html\r\n connection: close";
//字符串内,文本再包含字符串需要使用\ \转义符
const char *http_val="\
<html lang=\"zh-CN\">\n\
<head>\n\
<meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\n\
<title>This is a test</title>\n\
</head>\n\
<body>\n\
<div align=center height=\"500px\" >\n\
<br/><br/><br/>\n\
<h2>游戏王: 决斗链接!!</h2><br/><br/>\n\
<form action=\"commit\" method=\"post\">\n\
账号: <input type=\"text\" name=\"name\" />\n\
<br/>密码: <input type=\"password\" name=\"age\" />\n\
<br/><br/><br/><input type=\"submit\" value=\"确定\" />\n\
<input type=\"reset\" value=\"取消\" />\n\
</form>\n\
</div>\n\
</body>\n\
</html>";
//1.送main_header
int len = write(client_Sock,main_header,strlen(main_header));
if(debug){
fprintf(stderr,"...do_http_response...\n");
}
if(debug){
fprintf(stderr,"write[%d]: %s\n",len,main_header);
}
//2.生成content-length 行并发送
char send_buf[64]={0};
int http_len = strlen(http_val);
len = snprintf(send_buf,64,"content-length: %d\r\n\r\n",http_len);
len = write(client_Sock,send_buf,len);
if(debug){
fprintf(stdout,"write[%d]:%s",len,send_buf);
}
len = write(client_Sock,http_val,http_len);
if(debug){
fprintf(stdout,"write[%d]:%s",len,http_val);
}
//3.发送html 文件内容
}
*/
//返回值: -1表示读取出错, 0表示读到一个空行, 大于0表示成功读取一行
int get_line(int client_Sock,char *buf,int buf_size){
int count=0;
char ch='\0';
int len=0;
while((count<buf_size-1)&&ch!='\n'){
len=read(client_Sock,&ch,1);
if(len==1){
if(ch=='\r'){//'\r'表示回车
continue;
}else if(ch=='\n'){
//buf[count]='\0';
break;
}
//处理一般的字符
buf[count]=ch;
count++;
}else if(len==-1){//读取出错
perror("read failed");
count=-1;
break;
}else{//返回0; 客户端关闭sock链接
fprintf(stderr,"clien clsoe ...\n");
count=-1;
break;
}
}
if(count>=0){
buf[count]='\0';
}
return count;
}
void not_found(int client_Sock){
const char*reply = "HTTP/1.0 404 NOT FOUND\r\n\
\r\n\
<HTML>\
<HEAD>\
<TITLE>NOT FOUND</TITLE>\
</HEAD>\
<BODY>\
<html lang=\"zh-CN\">\n\
<head>\n\
<meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\n\
<title>This is a test</title>\n\
</head>\n\
<body>\n\
<div align=center height=\"500px\" >\n\
<br/><br/><br/>\n\
<h2>文件不存在!!</h2><br/><br/>\n\
<P>The server could not fulfill your request because the resource specified is unavailable or nonexistent...\
</BODY>\
</HTML>";
int len = write(client_Sock,reply,strlen(reply));
if(debug){
fprintf(stderr,"%s\n",reply);
}
if(len<=0){
fprintf(stderr,"send reply failed reason: %s\n",strerror(errno));
}
}
void unimplemented(int client_Sock){
const char*reply = "HTTP/1.0 501 method NOT Implemented\r\n\
\r\n\
<HTML>\
<HEAD>\
<TITLE>NOT FOUND</TITLE>\
</HEAD>\
<BODY>\
<html lang=\"zh-CN\">\n\
<head>\n\
<meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\n\
<title>This is a test</title>\n\
</head>\n\
<body>\n\
<div align=center height=\"500px\" >\n\
<br/><br/><br/>\n\
<h2>找不到响应的文件!!</h2><br/><br/>\n\
<P>The server could not fulfill your request because the resource specified is unavailable or nonexistent...\
</BODY>\
</HTML>";
int len = write(client_Sock,reply,strlen(reply));
if(debug){
fprintf(stderr,"%s\n",reply);
}
if(len<=0){
fprintf(stderr,"send reply failed reason: %s\n",strerror(errno));
}
}
void bad_request(int client_Sock){
const char*reply = "HTTP/1.0 400 bad request\r\n\
\r\n\
<HTML>\
<HEAD>\
<TITLE>NOT FOUND</TITLE>\
</HEAD>\
<BODY>\
<html lang=\"zh-CN\">\n\
<head>\n\
<meta content=\"text/html; charset=utf-8\" http-equiv=\"Content-Type\">\n\
<title>This is a test</title>\n\
</head>\n\
<body>\n\
<div align=center height=\"500px\" >\n\
<br/><br/><br/>\n\
<h2>文件不存在或是出错!!</h2><br/><br/>\n\
<P>The server could not fulfill your request because the resource specified is unavailable or nonexistent...\
</BODY>\
</HTML>";
int len = write(client_Sock,reply,strlen(reply));
if(debug){
fprintf(stderr,"%s\n",reply);
}
if(len<=0){
fprintf(stderr,"send reply failed reason: %s\n",strerror(errno));
}
}
void innter_error(int client_Sock){
const char*reply = "HTTP/1.0 501 Method Not Implemented\r\n\
Content-Type: text/html\r\n\
\r\n\
<HTML>\r\n\
<HEAD>\r\n\
<TITLE>Inner Error</TITLE>\r\n\
</HEAD>\r\n\
<BODY>\
<P>服务器内部出错...\r\n\
</BODY>\r\n\
</HTML>";
int len = write(client_Sock,reply,strlen(reply));
if(debug){
fprintf(stderr,"%s\n",reply);
}
if(len<=0){
fprintf(stderr,"send reply failed reason: %s\n",strerror(errno));
}
}