【C/C++学院】(28)项目实战HttpServer--源码

来源:互联网 发布:mac的usb接口没反应 编辑:程序博客网 时间:2024/06/06 13:56

项目实战HttpServer--源码下载地址


myhttp脚本文件

#!/bin/shWHOAMI=`whoami`PID=`ps -u $WHOAMI | grep myhttpd | awk '{print $1}'`if (test "$#" = 0) thenecho "Usage: $0 [stop] [start] [status]"exit 0fiif (test "$1" = "start") thenif (test "$PID" = "") then./myhttpd 8080elseecho "myhttp is running"fiexit 0fiif (test "$1" = "stop") thenif (test "$PID" != "") thenkill -s 2 $PIDfiexit 0fiif (test "$1" = "status") thenif (test "$PID" = "") thenecho "myhttp is not run"elseecho "myhttp is running"fiexit 0fiecho "Usage: $0 [stop] [start] [status]"


makefile

.SUFFIXES: .c .oCC=gccPROC=procORACLE_HOME=/opt/oracle/product/11.2.0ORAFLAGS1=/usr/include/linuxORAFLAGS2=/usr/lib/gcc/i686-redhat-linux/4.4.4/includePROCSRCS=myoracle.pcDBSRCS=$(PROCSRCS:.pc=.c)SRCS=server.c\pub.c\work.c\$(DBSRCS)OBJS=$(SRCS:.c=.o)EXEC=myhttpdORCFLAGS1=-L${ORACLE_HOME}/libORCFLAGS2=-lclntshall: $(OBJS)$(CC) -o $(EXEC) $(OBJS) -lpthread $(ORCFLAGS1) $(ORCFLAGS2)@echo '-------------ok--------------'.c.o: $(DBSRCS)$(CC) -Wall -g -o $@ -c $<$(DBSRCS):${PROC} INAME=$(PROCSRCS) INCLUDE=$(ORAFLAGS1) INCLUDE=$(ORAFLAGS2) CPOOL=YES MODE=ANSI CODE=ANSI_C PARSE=PARTIAL THREADS=YES ONAME=$(DBSRCS)clean:rm -f $(OBJS)rm -f $(DBSRCS)rm -f core*

server.c

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <signal.h>#include "pub.h"#include "myoracle.h"int main(int arg, char *args[]){if (arg < 2)//如果没有参数,main函数返回{printf("usage:myserver port\n");return EXIT_FAILURE;}int iport = atoi(args[1]);//将第一个参数转化为整数if (iport == 0){printf("port %d is invalid\n", iport);return EXIT_FAILURE;}if (sql_connect("dbuser1", "dbuser1", "orcl") == -1)//连接到数据库return EXIT_FAILURE;int st = socket_create(iport);//建立socketif (st == 0)return EXIT_FAILURE;printf("myhttp is begin\n");setdaemon();//设置进程为daemon状态signal1(SIGINT, catch_Signal);//捕捉SIGINT信号socket_accept(st);close(st);sql_disconnect();printf("myhttp is end\n");return EXIT_SUCCESS;}

work.c

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <errno.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <pthread.h>#include <sys/socket.h>#include <netinet/in.h>#include "work.h"#include "pub.h"#include "myoracle.h"//8192是8k#define BUFSIZE 8192#define HEAD "HTTP/1.0 200 OK\n\Content-Type: %s\n\Transfer-Encoding: chunked\n\Connection: Keep-Alive\n\Accept-Ranges:bytes\n\Content-Length:%d\n\n"#define TAIL "\n\n"#define EXEC "s?wd="void gethttpcommand(const char *sHTTPMsg, char *command) //从http请求中读出GET后面的命令行{int i;int istart = 0;int iend = 0;for (i = 0; i < strlen(sHTTPMsg); i++){if ((sHTTPMsg[i] == ' ') && (istart == 0))//第一个空格{istart = i + 2;} else{if (sHTTPMsg[i] == ' ')//第二个空格{iend = i;break;//得到位置了,退出循环}}}strncpy(command, &sHTTPMsg[istart], (iend - istart));}int gettempletcontent(char *buf) //得到模板文件templet.html的内容{struct stat t;memset(&t, 0, sizeof(t));FILE *fd = fopen("templet.html", "rb");if (fd != NULL){stat("templet.html", &t);fread(buf, t.st_size, 1, fd);return t.st_size;} else{printf("open %s failed %s\n", "templet.html", strerror(errno));return 0;}}int getdynamicccontent(const char *query, char **buf) //动态设置http请求内容,query为条件,buf为动态内容{char templetcontent[1024];memset(templetcontent, 0, sizeof(templetcontent));if (gettempletcontent(templetcontent) == 0)return 0;*buf = malloc(BUFSIZE);char *body = NULL;if (query_result(query, &body) == -1){body = malloc(128);memset(body, 0, 128);strcpy(body, "抱歉,没有查询结果");}sprintf(*buf, templetcontent, query, body);free(body);return strlen(*buf);}int make_http_content(const char *command, char **buf) //根据get提供的文件名,生成静态http reponse消息内容{char *contentbuf = NULL;int icontentlen = 0;if (command[0] == 0) //GET请求后面为空,得到默认页面内容图{icontentlen = getfilecontent("default.html", &contentbuf);} else{if (strncmp(command, EXEC, strlen(EXEC)) == 0) //GET请求后面为s?wd={char query[1024];memset(query, 0, sizeof(query));httpstr2stdstr(&command[strlen(EXEC)], query); //得到s?wd=字符串后面的转义字符内容icontentlen = getdynamicccontent(query, &contentbuf);} else{icontentlen = getfilecontent(command, &contentbuf);//动态设置http请求内容,query为条件,buf为动态内容}}if (icontentlen > 0)//组成一个http的回复{char headbuf[1024];memset(headbuf, 0, sizeof(headbuf));sprintf(headbuf, HEAD, getfiletype(command), icontentlen); //设置消息头int iheadlen = strlen(headbuf);//得到消息头长度int itaillen = strlen(TAIL);//得到消息尾长度int isumlen = iheadlen + icontentlen + itaillen;//得到消息总长度*buf = malloc(isumlen);//根据消息总长度,动态分配内存char *tmp = *buf;memcpy(tmp, headbuf, iheadlen); //安装消息头memcpy(&tmp[iheadlen], contentbuf, icontentlen); //安装消息体memcpy(&tmp[iheadlen + icontentlen], TAIL, itaillen); //安装消息尾printf("headbuf:\n%s", headbuf);if (contentbuf)free(contentbuf);return isumlen;//返回消息总长度} else{return 0;}}void *socket_contr(void *arg)//线程入口函数{printf("thread is begin\n");int st = *(int *) arg;//得到来自client端的socketfree((int *) arg);char buf[BUFSIZE];memset(buf, 0, sizeof(buf));int rc = recv(st, buf, sizeof(buf), 0);//接收来自client端socket的消息if (rc <= 0){printf("recv failed %s\n", strerror(errno));} else{printf("recv:\n%s", buf);char command[1024];memset(command, 0, sizeof(command));gethttpcommand(buf, command); //得到http 请求中 GET后面的字符串char *content = NULL;int ilen = make_http_content(command, &content);//根据用户在GET中的请求,生成相应的回复内容if (ilen > 0){send(st, content, ilen, 0);//将回复的内容发送给client端socketfree(content);}}close(st);//关闭client端socketprintf("thread_is end\n");//count--;return NULL;}

pub.c

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <errno.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <pthread.h>#include <sys/socket.h>#include <netinet/in.h>#include <signal.h>#include <fcntl.h>#include "work.h"#include "pub.h"void setdaemon(){pid_t pid, sid;pid = fork();if (pid < 0){printf("fork failed %s\n", strerror(errno));exit (EXIT_FAILURE);;}if (pid > 0){exit (EXIT_SUCCESS);}if ((sid = setsid()) < 0){printf("setsid failed %s\n", strerror(errno));exit (EXIT_FAILURE);}/* if (chdir("/") < 0) { printf("chdir failed %s\n", strerror(errno)); exit(EXIT_FAILURE); } umask(0); close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); */}void catch_Signal(int Sign){switch (Sign){case SIGINT:printf("signal SIGINT\n");break;}}int signal1(int signo, void (*func)(int)){struct sigaction act, oact;act.sa_handler = func;sigemptyset(&act.sa_mask);act.sa_flags = 0;return sigaction(signo, &act, &oact);}int hex2dec(const char hex) //将16进制的字符转化为十进制的整数,例如:'a'转化为整数10,‘B’转化为整数11{switch (hex){case '0':return 0;case '1':return 1;case '2':return 2;case '3':return 3;case '4':return 4;case '5':return 5;case '6':return 6;case '7':return 7;case '8':return 8;case '9':return 9;case 'a':return 10;case 'A':return 10;case 'b':return 11;case 'B':return 11;case 'c':return 12;case 'C':return 12;case 'd':return 13;case 'D':return 13;case 'e':return 14;case 'E':return 14;case 'f':return 15;case 'F':return 15;default:return -1;}}//将两位16进制的字符串转化为十进制的unsigned char,例如:'10'转化为16,‘1A'转化为17unsigned char hexstr2dec(const char *hex) {return hex2dec(hex[0]) * 16 + hex2dec(hex[1]);}//将HTTP GET请求中的转义符号转化为标准字符,注意,空格被转义为'+'号void httpstr2stdstr(const char *httpstr, char *stdstr){int index = 0;int i;for (i = 0; i < strlen(httpstr); i++){if (httpstr[i] == '%'){stdstr[index] = hexstr2dec(&httpstr[i + 1]);i += 2;} else{stdstr[index] = httpstr[i];}index++;}}const char *getfiletype(const char *filename) //根据扩展名返回文件类型描述{////////////得到文件扩展名///////////////////int len = strlen(filename);int i;char sExt[32];memset(sExt, 0, sizeof(sExt));for (i = 0; i < len; i++){if (filename[i] == '.'){strncpy(sExt, &filename[i + 1], sizeof(sExt));break;}}////////根据扩展名返回相应描述///////////////////if (strncmp(sExt, "bmp", 3) == 0)return "image/bmp";if (strncmp(sExt, "gif", 3) == 0)return "image/gif";if (strncmp(sExt, "ico", 3) == 0)return "image/x-icon";if (strncmp(sExt, "jpg", 3) == 0)return "image/jpeg";if (strncmp(sExt, "avi", 3) == 0)return "video/avi";if (strncmp(sExt, "css", 3) == 0)return "text/css";if (strncmp(sExt, "dll", 3) == 0)return "application/x-msdownload";if (strncmp(sExt, "exe", 3) == 0)return "application/x-msdownload";if (strncmp(sExt, "dtd", 3) == 0)return "text/xml";if (strncmp(sExt, "mp3", 3) == 0)return "audio/mp3";if (strncmp(sExt, "mpg", 3) == 0)return "video/mpg";if (strncmp(sExt, "png", 3) == 0)return "image/png";if (strncmp(sExt, "ppt", 3) == 0)return "application/vnd.ms-powerpoint";if (strncmp(sExt, "xls", 3) == 0)return "application/vnd.ms-excel";if (strncmp(sExt, "doc", 3) == 0)return "application/msword";if (strncmp(sExt, "mp4", 3) == 0)return "video/mpeg4";if (strncmp(sExt, "ppt", 3) == 0)return "application/x-ppt";if (strncmp(sExt, "wma", 3) == 0)return "audio/x-ms-wma";if (strncmp(sExt, "wmv", 3) == 0)return "video/x-ms-wmv";return "text/html";}int getfilecontent(const char *filename, char **buf) //得到文件内容{//如果一个函数内部要给参数分配空间,那么这个参数必须是二级指针。struct stat t;memset(&t, 0, sizeof(t));FILE *fd = fopen(filename, "rb");//从只读方式打开参数filename指定的文件if (fd != NULL){stat(filename, &t);*buf = malloc(t.st_size);//根据文件大小,动态分配内存buffread(*buf, t.st_size, 1, fd);//将文件读取到buffclose(fd);return t.st_size;} else{printf("open %s failed %s\n", filename, strerror(errno));return 0;}}int socket_create(int port)//根据参数port,建立server端socket{int st = socket(AF_INET, SOCK_STREAM, 0);//建立TCP的socket描述符if (st == -1){printf("socket failed %s\n", strerror(errno));return 0;}int on = 1;if (setsockopt(st, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1){printf("setsockopt failed %s\n", strerror(errno));return 0;}struct sockaddr_in addr;memset(&addr, 0, sizeof(addr));addr.sin_family = AF_INET;addr.sin_port = htons(port);addr.sin_addr.s_addr = htonl(INADDR_ANY);if (bind(st, (struct sockaddr *) &addr, sizeof(addr)) == -1){printf("bind failed %s\n", strerror(errno));return 0;}if (listen(st, 100) == -1){printf("listen failed %s\n", strerror(errno));return 0;}printf("listen %d success\n", port);return st;//返回listen的socket描述符}void sockaddr_toa(const struct sockaddr_in *addr, char *IPAddr)//将struct sockaddr_in转化为IP地址字符串{unsigned char *p = (unsigned char *)&(addr->sin_addr.s_addr);sprintf(IPAddr, "%u.%u.%u.%u", p[0], p[1], p[2], p[3]);}void socket_accept(int st){pthread_t thr_d;pthread_attr_t attr;pthread_attr_init(&attr);pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);//设置线程为可分离状态int client_st = 0;struct sockaddr_in client_addr;socklen_t len = sizeof(client_addr);while (1)//循环执行accept{memset(&client_addr, 0, sizeof(client_addr));//accept函数阻塞,知道有client端连接到达,或者accept错误返回client_st = accept(st, (struct sockaddr *)&client_addr, &len);/*//加一个计数器,count++;if(count > 200)//如果客户端连接个数为200个上限值{close(client);continue;}*/if (client_st == -1){printf("accept failed %s\n", strerror(errno));break;//accept错误,循环break} else{char sIP[32];memset(sIP, 0, sizeof(sIP));sockaddr_toa(&client_addr, sIP);printf("accept by %s\n", sIP);int *tmp = malloc(sizeof(int));*tmp = client_st;{//将来自client端的socket做为参数,启动一个可分离线程pthread_create(&thr_d, &attr, socket_contr, tmp);}}}pthread_attr_destroy(&attr);}


myoracle.c
#include <stdio.h>#include <stdlib.h>#include <string.h>EXEC SQL BEGIN DECLARE SECTION; sql_context pContext;long SQLCODE;EXEC SQL END DECLARE SECTION;extern void sqlglmt(void*, char*, size_t*, size_t* ); void sql_error()//定义一个错误安装函数{char sErrorString[512];size_t tMessageSize = 0;size_t tErrorSize = sizeof(sErrorString);memset(sErrorString, 0, sizeof(sErrorString));sqlglmt(pContext, sErrorString, &tErrorSize, &tMessageSize);sErrorString[tMessageSize] = 0;printf("%s\n", sErrorString);}void sql_init()//初始化oracle{SQLCODE = 0;pContext = NULL;EXEC SQL ENABLE THREADS;//标明可以在线程中使用EXEC SQL CONTEXT ALLOCATE :pContext;//为pContext分配资源EXEC SQL CONTEXT USE :pContext;//使用m_pContext}int sql_free(){SQLCODE = 0;EXEC SQL CONTEXT FREE :pContext;if (SQLCODE != 0){sql_error();return -1;}else{return 0;}}int sql_connect(const char *User, const char *Password, const char *DBName){sql_init();EXEC SQL BEGIN DECLARE SECTION;const char *sUser;const char *sPassword;const char *sServer;EXEC SQL END DECLARE SECTION;SQLCODE = 0;sUser = User;sPassword = Password;sServer = DBName;EXEC SQL CONNECT :sUser IDENTIFIED BY :sPassword USING :sServer;//连接到oracleif (SQLCODE != 0){sql_error();//连接失败,打印错误原因return -1;}else{return 0;}}int sql_disconnect()//断开数据库连接{SQLCODE = 0;EXEC SQL ROLLBACK WORK RELEASE;return sql_free();}void addurl(const char *url, const char *name, const char *description,char **buf)//向动态消息体中添加一个url链接{char content[1024];memset(content, 0, sizeof(content));sprintf(content, "<a href=\"http://%s\">%s</a></br>%s%s</br></br>", url,name, name, description);//格式化字符串if (*buf != NULL)//addurl函数已经调用过了,所以buf的值不等于NULL{int buflen = strlen(*buf);//得到buf中字符串的长度int contentlen = strlen(content);//得到conntent中字符串的长度int sumlen = buflen + contentlen;//得到buf中字符串和content中字符串的长度之和char *tmp = malloc(sumlen + 1);//分配一个新的临时缓冲区tmp,大小为buf + contextmemset(tmp, 0, sumlen + 1);strncpy(tmp, *buf, buflen);//将buf中的字符串拷贝到tmpstrncpy(&tmp[buflen], content, contentlen);//将content中的字符串追加到tmp后面free(*buf);//释放buf之前的内存*buf = tmp;//将buf指向tmp的内存区域} else//第一次调用addurl函数{int contentlen = strlen(content);//得到content中字符串的长度*buf = malloc(contentlen + 1);//根据content中字符串的长度动态分配内存空间bufmemset(*buf, 0, contentlen + 1);strncpy(*buf, content, contentlen);//将content中字符串拷贝到buf}}//以name为参数,执行“select url, name, description from baidu where name like” SQL语句int query_result(const char *name, char **buf){EXEC SQL BEGIN DECLARE SECTION;int iOccurs;short iInd;char sData1[1024];//result buffer;char sData2[1024];//result buffer;char sData3[1024];//result buffer;char sOutput[64];char sInput[64];const char *sDySQL;EXEC SQL END DECLARE SECTION;int res = -1;char sSQL[1024];memset(sSQL, 0, sizeof(sSQL));sprintf(sSQL,"select url, name, description from baidu where name like '%%%s%%'",name);printf("%s\n", sSQL);SQLCODE = 0;sDySQL = sSQL;sprintf(sOutput, "output%p", pContext);//生成一个在系统中不会重复的字符串sprintf(sInput, "input%p", pContext);//生成一个在系统中不会重复的字符串EXEC SQL ALLOCATE DESCRIPTOR :sOutput;//为输出空间分配资源EXEC SQL ALLOCATE DESCRIPTOR :sInput;//为输入空间分配资源EXEC SQL PREPARE S FROM :sDySQL;//准备执行指定的SELECT语句if (SQLCODE != 0){sql_error();//如果错误,打印错误原因EXEC SQL DEALLOCATE DESCRIPTOR :sInput;//释放已经分配的输入空间资源EXEC SQL DEALLOCATE DESCRIPTOR :sOutput;//释放已经分配的输出空间资源return res;}EXEC SQL DECLARE C CURSOR FOR S;//定义一个游标CEXEC SQL OPEN C USING DESCRIPTOR :sInput;//打开游标C/*选择输出区域*/EXEC SQL DESCRIBE OUTPUT S USING DESCRIPTOR :sOutput;EXEC SQL WHENEVER NOT FOUND DO BREAK;//如果查询不到记录,下面的while循环breakwhile(1){/*行数据,输出描述区*/ EXEC SQL FETCH C INTO DESCRIPTOR :sOutput;memset(sData1, 0, sizeof(sData1));memset(sData2, 0, sizeof(sData2));memset(sData3, 0, sizeof(sData3));iInd = 0;iOccurs = 1;EXEC SQL GET DESCRIPTOR :sOutputVALUE :iOccurs :sData1 = DATA, :iInd = INDICATOR;//得到第一个字段值if (iInd == -1){strcpy(sData1, "NULL");//如果得到值为NULL}iInd = 0;iOccurs = 2;EXEC SQL GET DESCRIPTOR :sOutputVALUE :iOccurs :sData2 = DATA, :iInd = INDICATOR;//得到第二个字段值if (iInd == -1){strcpy(sData2, "NULL");//如果得到值为NULL}iInd = 0;iOccurs = 3;EXEC SQL GET DESCRIPTOR :sOutputVALUE :iOccurs :sData3 = DATA, :iInd = INDICATOR;//得到第三个字段值if (iInd == -1){strcpy(sData3, "NULL");//如果得到值为NULL}addurl(sData1, sData2, sData3, buf);//调用addurl,将查询到的行记录转化为HTML形式的字符串res++;}EXEC SQL CLOSE C;//关闭游标CEXEC SQL DEALLOCATE DESCRIPTOR :sOutput;//释放已经分配的输出空间资源EXEC SQL DEALLOCATE DESCRIPTOR :sInput;//释放已经分配的输入空间资源return res;}

createtable.sql

CREATE SEQUENCE seq1increment by 1start with 1maxvalue 99999999999;CREATE TABLE baidu (ID int NOT NULL,url varchar2(100), name varchar(100),description varchar2(200));CREATE UNIQUE INDEX baidu_idON baidu (ID);CREATE INDEX baidu_nameON baidu (name);

insert.sql

insert into baidu (ID, url, name, description) values (seq1.nextval, 'www.sina.com', 'sina新浪', '新浪的首页');insert into baidu (ID, url, name, description) values (seq1.nextval, 'www.itcast.cn', 'itcast传智播客', '北京传智播客教育科技有限公司——专注于Java、.Net、iOS、C/C++、php、网页平面设计工程师的培养');insert into baidu (ID, url, name, description) values (seq1.nextval, '192.168.1.254', 'baidu百度', '百度的首页');commit;

templet.html

<head><meta http-equiv="content-type" content="text/html;charset=utf8"></head><html><title>百度一下,你就知道</title><body><img src="bdlogo.gif"><form action="s" method="get"><input type="text" name="wd" value="%s" style="font-size:20px;width:400"><input type="submit" value="百度一下" style="font-size:20px"></form>%s</body></html>


0 0
原创粉丝点击