myhttp脚本文件
#!/bin/sh
WHOAMI=`whoami`
PID=`ps -u $WHOAMI | grep myhttpd | awk '{print $1}'`
if (test "$#" = 0) then
echo "Usage: $0 [stop] [start] [status]"
exit 0
fi
if (test "$1" = "start") then
if (test "$PID" = "") then
./myhttpd 8080
else
echo "myhttp is running"
fi
exit 0
fi
if (test "$1" = "stop") then
if (test "$PID" != "") then
kill -s 2 $PID
fi
exit 0
fi
if (test "$1" = "status") then
if (test "$PID" = "") then
echo "myhttp is not run"
else
echo "myhttp is running"
fi
exit 0
fi
echo "Usage: $0 [stop] [start] [status]"
makefile
.SUFFIXES: .c .o
CC=gcc
PROC=proc
ORACLE_HOME=/opt/oracle/product/11.2.0
ORAFLAGS1=/usr/include/linux
ORAFLAGS2=/usr/lib/gcc/i686-redhat-linux/4.4.4/include
PROCSRCS=myoracle.pc
DBSRCS=$(PROCSRCS:.pc=.c)
SRCS=server.c\
pub.c\
work.c\
$(DBSRCS)
OBJS=$(SRCS:.c=.o)
EXEC=myhttpd
ORCFLAGS1=-L${ORACLE_HOME}/lib
ORCFLAGS2=-lclntsh
all: $(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);//建立socket
if (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端的socket
free((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端socket
free(content);
}
}
close(st);//关闭client端socket
printf("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'转化为17
unsigned 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);//根据文件大小,动态分配内存buf
fread(*buf, t.st_size, 1, fd);//将文件读取到buf
fclose(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);
}
#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;//连接到oracle
if (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 + context
memset(tmp, 0, sumlen + 1);
strncpy(tmp, *buf, buflen);//将buf中的字符串拷贝到tmp
strncpy(&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中字符串的长度动态分配内存空间buf
memset(*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;//定义一个游标C
EXEC SQL OPEN C USING DESCRIPTOR :sInput;//打开游标C
/*选择输出区域*/
EXEC SQL DESCRIBE OUTPUT S USING DESCRIPTOR :sOutput;
EXEC SQL WHENEVER NOT FOUND DO BREAK;//如果查询不到记录,下面的while循环break
while(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 :sOutput
VALUE :iOccurs :sData1 = DATA, :iInd = INDICATOR;//得到第一个字段值
if (iInd == -1)
{
strcpy(sData1, "NULL");//如果得到值为NULL
}
iInd = 0;
iOccurs = 2;
EXEC SQL GET DESCRIPTOR :sOutput
VALUE :iOccurs :sData2 = DATA, :iInd = INDICATOR;//得到第二个字段值
if (iInd == -1)
{
strcpy(sData2, "NULL");//如果得到值为NULL
}
iInd = 0;
iOccurs = 3;
EXEC SQL GET DESCRIPTOR :sOutput
VALUE :iOccurs :sData3 = DATA, :iInd = INDICATOR;//得到第三个字段值
if (iInd == -1)
{
strcpy(sData3, "NULL");//如果得到值为NULL
}
addurl(sData1, sData2, sData3, buf);//调用addurl,将查询到的行记录转化为HTML形式的字符串
res++;
}
EXEC SQL CLOSE C;//关闭游标C
EXEC SQL DEALLOCATE DESCRIPTOR :sOutput;//释放已经分配的输出空间资源
EXEC SQL DEALLOCATE DESCRIPTOR :sInput;//释放已经分配的输入空间资源
return res;
}
createtable.sql
CREATE SEQUENCE seq1
increment by 1
start with 1
maxvalue 99999999999;
CREATE TABLE baidu
(ID int NOT NULL,
url varchar2(100), name varchar(100),
description varchar2(200));
CREATE UNIQUE INDEX baidu_id
ON baidu (ID);
CREATE INDEX baidu_name
ON 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>