首页 > 编程语言 > 详细

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

时间:2015-04-06 14:18:03      阅读:281      评论:0      收藏:0      [点我收藏+]

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


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\nContent-Type: %s\nTransfer-Encoding: chunked\nConnection: Keep-Alive\nAccept-Ranges:bytes\nContent-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);
}


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;//连接到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>


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

原文:http://blog.csdn.net/waldmer/article/details/44300799

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!