简易HTTP服务器(多进程版本)
来源:互联网 发布:怎么设计数据库的表 编辑:程序博客网 时间:2024/05/16 02:03
入职公司的试用期小内容,这个是多进程的版本,其中支持了图片,网页,大致结构比较清晰,拿出来跟大家分享下。
server.c源码如下:
#include <sys/socket.h>#include <netinet/in.h>#include <arpa/inet.h>#include <assert.h>#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <sys/stat.h>#define SERVER_NAME "IAMCAP-SERVER"#define PROTOCOL "HTTP/1.1"#define REQUESTLEN 4096#define RESPONSELEN 1024000#define TIMELEN 55#define TEMPLEN 100#define BUFFERLEN 1024#define HOMEPATH "/Users/iamcap/http/" static char* add_header(int , char *, char *, off_t , char *, int );void parsedata(char*,int);int main(int argc, char **argv){ if(argc <=2){ printf("usage: %s ip_address port_number\n", basename(argv[0])); return 1; } const char *ip = argv[1]; int port = atoi(argv[2]); //初始化通信结构体 struct sockaddr_in address; bzero(&address, sizeof(address)); address.sin_family = AF_INET; inet_pton(AF_INET, ip, &address.sin_addr); address.sin_port = htons(port); //端口重复利用 int sock = socket(PF_INET, SOCK_STREAM, 0); assert( sock >= 0); int opt = 1; setsockopt(sock, SOL_SOCKET,SO_REUSEADDR, &opt, sizeof(opt)); int ret = bind(sock, (struct sockaddr*)&address, sizeof(address)); assert(ret != -1); printf("listening ...\n"); ret = listen(sock, 5); assert(ret != -1); while(1){ //保存客户端的信息 struct sockaddr_in client; socklen_t client_addrlength = sizeof(client); int connectfd = accept(sock,(struct sockaddr*)&client,&client_addrlength); if(connectfd < 0){ printf("errno is: %d\n", errno); }else{ //创建worker进程 pid_t pid = fork(); if(-1 == pid){ printf("wrong!!!\n"); }else if(0 == pid){ //在子进程中 char remote[INET_ADDRSTRLEN]; memset(remote,'\0',INET_ADDRSTRLEN); printf("\nconnected with ip: %s and port: %d\n", inet_ntoa(client.sin_addr),ntohs(client.sin_port)); char recvdata[REQUESTLEN]; memset(recvdata,'\0',REQUESTLEN); //从建立的socket中接收浏览器的请求信息 int ret = recv(connectfd, recvdata,REQUESTLEN-1, 0); printf("\n%s\n",recvdata); //处理接收到的信息 parsedata(recvdata,connectfd); }else{ //在父进程中 close(connectfd); } } } close(sock); return 0;}// send header to the browser static char* add_header(int status, char *title, char *mime_type, off_t length, char *buffer , int buffersize) { char temp[TEMPLEN]; memset(buffer, 0, sizeof(buffersize)); sprintf(temp, "%s %d %s\r\n", PROTOCOL, status, title); //将所有的头部数据格式都按照协议的标准填充到buffer strcat(buffer, temp); sprintf(temp, "Server: %s\r\n", SERVER_NAME); strcat(buffer, temp); sprintf(temp, "Connection: Keep-Alive\r\n"); strcat(buffer, temp); sprintf(temp, "Content-Type: %s\r\n", mime_type); strcat(buffer, temp); sprintf(temp, "Content-Length: %d\r\n", (int)length); strcat(buffer, temp); sprintf(temp, "Content-Language: zh-CN\r\n"); strcat(buffer, temp); strcat(buffer, "\r\n"); return buffer; }static char* get_mime_type(char *filename){ char *postfix; postfix = strrchr(filename, '.'); if (!strcasecmp(postfix, ".html") || !strcasecmp(postfix, ".htm")){ return "text/html; charset=UTF-8"; }else if (!strcasecmp(postfix, ".jpg") || !strcasecmp(postfix, ".jpeg")){ return "image/jpeg"; }else{ return "text/plain; charset=UTF-8"; }}void parsedata(char *recvdata, int connectfd){ //切换到主页目录 if(chdir(HOMEPATH)<0){ printf("chdir error\n"); exit(0); } char method[BUFFERLEN]; char path[BUFFERLEN]; char protocol[BUFFERLEN]; memset(method,'\0',BUFFERLEN); memset(path,'\0',BUFFERLEN); memset(protocol,'\0',BUFFERLEN); sscanf(recvdata, "%[^ ] %[^ ] %[^ ]", method, path, protocol); //打开文件或文件夹 struct stat property; stat(path,&property); if(S_ISDIR(property.st_mode)){ //如果是文件夹 if(0 == strcmp(path,"/")){ //是主页目录 //返回index.html FILE *fp; if(fp = fopen("index.html","r")){ char head[BUFFERLEN]; memset(head,'\0',BUFFERLEN); stat(path+1,&property); stat("index.html",&property); add_header(200, "OK", "text/html", property.st_size, head, BUFFERLEN); char temp[RESPONSELEN]; memset(temp,'\0',RESPONSELEN); strcpy(temp,head); fread(temp+strlen(head),1,property.st_size,fp); send(connectfd, temp, strlen(temp),0); //将应答的html报文回发过去,浏览器可能还会发送图片显示请求报文 close(connectfd); } }else{ //其他目录,直接返回 exit(0); } }else{ //如果是文件 if(0 == strcasecmp(method,"GET")){ FILE *fp; if(fp = fopen(path+1,"rb")){ char head[BUFFERLEN]; memset(head,'\0',BUFFERLEN); stat(path+1,&property); add_header(200, "OK", get_mime_type((path + 1)), property.st_size, head, BUFFERLEN); char temp[RESPONSELEN]; memset(temp,'\0',RESPONSELEN); strcpy(temp,head); fread(temp+strlen(head),1,property.st_size,fp); send(connectfd, temp, strlen(head)+property.st_size,0); close(connectfd); } }else if(0 == strcasecmp(method,"POST")){ }else if(0 == strcasecmp(method,"HEAD")){ }else if(0 == strcasecmp(method,"PUT")){ }else{ exit(0); } }}
index.html代码如下:
<html><body><H1>this is a page</H1><img src="mypic.jpg" /></body></html>
mypic.jpg 是 /Users/iamcap/http/路径下的一张图片。
这里需要注意的有以下几点:
1. http的本身就是一个文本文件,你需要知道http协议的格式,可以http://blog.csdn.net/gueter/article/details/1524447
2. 在显示图片的处理过程中,记得用fread函数,不要企图使用strlen函数来获取长度,可以借助struct stat { } 和 HTTP头部共同拼接出所发内容的长度
3. 浏览器在浏览的过程中,可能会发送GET请求favicon.ico,这个其实就是页面显示的那个小图标,可以不用管。
4. 在使用数组之前,尽量用memset进行初始化,免得垃圾收据会影响内容发送。
0 0
- 简易HTTP服务器(多进程版本)
- 简易HTTP服务器(epoll模型ET版本)
- 简易http服务器
- 简易搭建http服务器
- 简易HTTP服务器
- HTTP服务器简易版
- Linux网络编程【三】:TCP服务器多进程和多线程(http访问)版本
- java 简易http服务器原理
- 一个简易的http服务器。
- 简易HTTP服务器的实现
- JAVA实现简易HTTP服务器
- python的简易HTTP服务器
- python实现简易web多进程静态服务器
- http服务-搭建简易的http服务器
- 使用Java实现简易的HTTP服务器
- Erlang实现的简易HTTP服务器
- Nio--Selector实现简易的http服务器
- java实现的HTTP简易服务器
- 序列号 到2015-7-17
- Docker的退出后进入
- 【学习笔记】JavaScript编码规范-声明提升
- spring4.1与redis集成之aop整合方案
- spark streaming例子
- 简易HTTP服务器(多进程版本)
- Real time H.264 Encoding and Decoding-Using FFmpeg and x264
- 常用的网站压力测试工具有哪些
- 页面刷新一次设置变量加1
- IP数据报详解
- android自定义组件segmentedgroup源码阅读笔记
- Get windows environment variables
- (转)记一次内存优化的分享
- serviceManage