LInux先建立线程池,主函数监听套机字,accept后,将连接套接字投入线程池处理。
自己搞的这个线程池,暂时没有发现问题,运行效果还行。
虽然free是线程安全,不可重入的。但是我们并没使用静态变量,所以不会存在问题
下为main函数,运行参数为 ./web n k ;n为想线程池初始化的线程数,k为0为终端运行,k为1则转为守护进程。
运行如图
支持文件的浏览与下载
可能出现在本机上可以访问,在局域网内其他机器不能访问。则解决入下:
service iptables stop 也可以自己配置下iptables的匿名访问
如果想外网能访问,就得配置NAT
#include "unp.h"
#include "threadpoll.h"
#include "work.h"
#include "daemon.h"
int daemon_flag = 0;
int main(int argc, char **argv)
{
int listenfd, *connfd, num;
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
struct in_addr myaddr;
const int on = 1;
if( argc != 3){
exit(0); /*参数错误*/
}
if(atoi(argv[2]) == 1) {
daemon_ser(argv[0],LOG_USER); /*变成守护进程*/
daemon_flag = 1;
} else {
daemon_flag = 0;
}
if((listenfd = Socket(AF_INET, SOCK_STREAM, 0)) < 0){
//PST("socket error");
exit(0);
}
num = atoi(argv[1]);
bzero(&servaddr, sizeof(servaddr));
inet_pton(AF_INET, "192.168.0.108", &myaddr); /*本服务器IP地址*/
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = myaddr.s_addr;
servaddr.sin_port = htons(80); /*web服务为80号端口*/
setsockopt(listenfd,SOL_SOCKET, SO_REUSEADDR, &on, sizeof(int));
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
if(inital_thread_poll(num) != 0) { /*初始化线程池*/
exit(0);
}
signal(SIGINT,clear_pool); /*中断时,释放资源*/
Listen(listenfd, LISTENQ);
for ( ; ; ) {
clilen = sizeof(struct sockaddr);
connfd = (int * )malloc(sizeof(int)); /*每个连接一个字节*/
if(connfd == NULL) {
PST("Malloc connfd error");
exit(0);
}
*connfd = accept(listenfd, (SA *) &cliaddr, &clilen);
if(*connfd < 0) {
free(connfd);
continue;
}
PST("Add a connect");
insert_task_2_poll(connfd); /*增加任务,投入到线程池*/
}
clear_pool(SIGINT); /*退出时释放资源*/
exit(0);
}
以下为线程池,采用条件和互斥锁同步
#include "unp.h"
#include "threadpoll.h"
#include "work.h"
#include <assert.h>
thread_pool *pool;
extern daemon_flag;
/*
*初始化线程池
*/
int inital_thread_poll(int num) {
pthread_attr_t attr;
pthread_t pid;
int i, err;
pool = (thread_pool *)malloc(sizeof(thread_pool)); /*在堆上分配*/
if(pool == NULL) {
#ifdef PRT
PST("Malloc error");
#endif
return -1;
}
assert(pool != NULL);
pthread_mutex_init( &(pool->mutex), NULL); /*锁*/
pthread_cond_init( &(pool->cond), NULL); /*条件*/
pool->nthreads = num;
pool->totaltasks = pool->runtasks = 0; /*当前总接受的任务数和运行任务数*/
pool->shut = 0; /*关闭标志*/
pool->head = pool->tail = NULL;
pool->head = pool->tail = malloc(sizeof(task)); /*初始化头尾指针*/
pthread_attr_init( &attr); /*属性*/
pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED);
for( i =0; i < num; i++) {
if((err = pthread_create( &pid, &attr, thread_func, NULL)) != 0) { /*创建线程*/
return err;
}
}
pthread_attr_destroy( &attr);
return 0;
}
/*
*给线程池中加入任务
*/
void insert_task_2_poll(/*void *(*func)(void *),*/ void *data) { /*添加任务*/
task *mytask;
mytask = (task *)malloc(sizeof(task)); /*在堆上分配*/
if(mytask == NULL) {
#ifdef PRT
PST("Malloc task error");
#endif
return;
}
/* mytask->func = func;*/
mytask->data = data;
pthread_mutex_lock(&(pool->mutex));
pool->tail->next = mytask;
mytask->prev = pool->tail;
pool->tail = mytask;
pool->tail->next = NULL;
pool->totaltasks ++; /*任务数加1*/
pool->waittasks ++; /*等待处理的任务数加1*/
#ifdef PRT
printf("Add a task ,the total tasks is %d, waittask is %d\n",pool->totaltasks,pool->waittasks);
#endif
pthread_cond_signal(&(pool->cond));/*唤醒等待任务的线程*/
pthread_mutex_unlock(&(pool->mutex));
return;
}
/*
*删除线程池中尾任务
*/
void del_task_2_poll() { /*删除任务*/
/* pthread_mutex_lock(&(pool->mutex));*/
free(pool->tail->data);
pool->tail->data = NULL;
pool->tail = pool->tail->prev;
free(pool->tail->next);
pool->tail->next = NULL;
pool->totaltasks --;
#ifdef PRT
printf("Delete a task ,the total tasks is %d, waittask is %d\n",pool->totaltasks,pool->waittasks);
#endif
/* pthread_mutex_lock(&(pool->mutex));*/
}
/*
*线程函数
*/
void *thread_func(void *arg) { /*等待任务,无则阻塞*/
/*void *(*functemp)(void *);*/
int connfd;
for( ; ;) {
pthread_mutex_lock(&(pool->mutex));
while(pool->waittasks == 0)
pthread_cond_wait(&(pool->cond), &(pool->mutex));
/* functemp = pool->tail->func;*/
connfd = *((int *)(pool->tail->data));
pool->runtasks ++; /*处理过的任务数*/
pool->waittasks --; /*等待处理的任务数*/
del_task_2_poll(); /*释放这个任务*/
pthread_mutex_unlock(&(pool->mutex)); /*解锁,使其他线程可以等待其他任务*/
serve_machine(&connfd); /*任务开始执行*/
}
}
/*
*释放线程池资源
*/
void clear_pool(int signo) { /*清除线程池*/
task *temp;
#ifdef PRT
printf("\n服务器正在关闭...\n");
#endif
pthread_mutex_lock(&(pool->mutex));
temp = pool->head->next;
for(; temp != NULL; temp = pool->head->next) {
free(pool->head->data);
pool->head->data = NULL;
if(pool->head->prev != NULL){
free(pool->head->prev);
pool->head->prev = NULL;
}
free(pool->head);
pool->head = NULL;
pool->head = temp;
}
/*free(temp);*/
pthread_mutex_unlock(&(pool->mutex));
pthread_mutex_destroy(&(pool->mutex));
pthread_cond_destroy(&(pool->cond));
free(pool);
pool = NULL;
exit(0);
}
线程池 头文件
#ifndef _THREAD_
#define _THREAD_
#include "unp.h"
typedef struct demytask { /*任务*/
//void *(*func)(void *arg); /*套接字处理函数指针*/
void *data; /*连接套接字*/
struct demytask *next; /*后向索引*/
struct demytask *prev; /*后向索引*/
}task;
typedef struct pool {
pthread_mutex_t mutex; /*锁和条件*/
pthread_cond_t cond;
int nthreads; /*线程池中线程数*/
int totaltasks; /*线程池中总的任务数*/
int runtasks; /*正在服务的任务数*/
int shut; /*线程池关闭状态*/
int waittasks; /*等待空闲线程的任务数*/
task *head; /*任务头指针*/
task *tail; /*任务尾指针*/
/*pthread_t *threads*/ /*线程ID数组,暂无用*/
}thread_pool;
void *thread_func(void *arg); /*线程函数*/
int inital_thread_poll(int num); /*线程池初始化*/
void insert_task_2_poll(/*void *(*func)(void *),*/ void *data);/*增加任务*/
void del_task_2_poll(); /*删除任务*/
void clear_pool(int signo) ; /*线程池资源释放*/
#endif
#include "work.h"
#include <dirent.h>
char *rootdir = "/"; /*初始页*/
char *port = "80";
char *host = "192.168.0.108";
char *dir_up(char *dirpath)
{
static char path[MAXLINE];
char *end;
int len;
strcpy(path, dirpath);
len = strlen(path);
if(strcmp(dirpath, "/") == 0)
return path;
if(len > 1) {
while((path[len-1] == '/')) {
path[len-1] = '\0';
len --;
}
end = (strrchr(path, '/'));
*end = '\0';
}
if(path - end == 0) {
path[0] = '/';
path[1] = '\0';
return path;
}
return path;
}
void serve_machine(int *connfd) {
char buf[MAXLINE], query[MAXLINE+1];
int n;
n = recv(*connfd, buf, sizeof(buf), 0); /*读取客户端指令*/
sscanf(buf, "GET %s HTTP", query);
if(n <= 0) {
close(*connfd);
return;
}
FILE *file;
file =fdopen(*connfd, "w");
send_2_server(file, query);
close(*connfd);
}
void send_2_server(FILE * client_sock, char *path)
{
struct dirent *dirent;
struct stat info;
char filename[MAXLINE];
DIR *dir;
int fd, len, ret;
int space = 1024, nread = 0;
char *p, *realpath, *realfilename, *nport;
len = strlen(rootdir) + strlen(path) + 1;
realpath = malloc(len + 1);
bzero(realpath, len + 1);
sprintf(realpath, "%s/%s", rootdir, path);
len = strlen(port) + 1;
nport = malloc(len + 1);
bzero(nport, len + 1);
sprintf(nport, ":%s", port);
if (stat(realpath, &info)) {
fprintf(client_sock,
"HTTP/1.1 200 OK\r\nServer: DAS by renzhenwen\r\nConnection: close\r\n\r\n<html><head><title>%d - %s</title></head>"
"<body><font size=+4>基于Linux线程池的目录访问服务器</font><br><hr width=\"100%%\"><br><center>"
"<table border cols=3 width=\"100%%\">", errno,strerror(errno));
fprintf(client_sock,
"</table><font color=\"CC0000\" size=+2>不能获取当前信息:\n%s %s</font></body></html>",path, strerror(errno));
goto out;
}
if (S_ISREG(info.st_mode)) {
fd = open(realpath, O_RDONLY);
len = lseek(fd, 0, SEEK_END);
nread = 0;
fprintf(client_sock,
"HTTP/1.1 200 OK\r\nServer: DAS by renzhenwen\r\nConnection: keep-alive\r\nContent-type: application/*\r\nContent-Length:%d\r\n\r\n",len);
p = (char *) malloc(space + 1);
while( len > 0 ) { /*单缓冲读取文件*/
bzero(p, space + 1);
lseek(fd, nread, SEEK_SET);
ret = read(fd, p, space);
fwrite(p, ret, 1, client_sock);
len -= ret;
nread += ret;
}
free(p);
close(fd);
} else if (S_ISDIR(info.st_mode)) {
dir = opendir(realpath);
fprintf(client_sock,
"HTTP/1.1 200 OK\r\nServer: DAS by renzhenwen\r\nConnection: close\r\n\r\n<html><head><title>%s</title></head>"
"<body><font size=+4>基于Linux线程池的目录访问服务器</font><br><hr width=\"100%%\"><br><center>"
"<table border cols=3 width=\"100%%\">", path);
fprintf(client_sock,
"<caption><font size=+3>目录 %s</font></caption>\n",path);
fprintf(client_sock,
"<tr><td>名称</td><td>大小</td><td>修改时间</td></tr>\n");
if (dir == 0) {
fprintf(client_sock,
"</table><font color=\"CC0000\" size=+2>%s</font></body></html>",strerror(errno));
return;
}
while ((dirent = readdir(dir)) != 0) {
if (strcmp(path, "/") == 0)
sprintf(filename, "/%s", dirent->d_name);
else
sprintf(filename, "%s/%s", path, dirent->d_name);
fprintf(client_sock, "<tr>");
len = strlen(rootdir) + strlen(filename) + 1;
realfilename = malloc(len + 1);
bzero(realfilename, len + 1);
sprintf(realfilename, "%s/%s", rootdir, filename);
if (stat(realfilename, &info) == 0) {
if (strcmp(dirent->d_name, "..") == 0)
fprintf(client_sock,
"<td><a href=\"http://%s%s%s\">(parent)</a></td>",
host, atoi(port) == 80 ? "" : nport,
dir_up(path));
else
fprintf(client_sock,
"<td><a href=\"http://%s%s%s\">%s</a></td>",
host, atoi(port) == 80 ? "" : nport, filename,dirent->d_name);
if (S_ISDIR(info.st_mode))
fprintf(client_sock, "<td>目录</td>");
else if (S_ISREG(info.st_mode))
fprintf(client_sock, "<td>%d</td>", info.st_size);
else if (S_ISLNK(info.st_mode))
fprintf(client_sock, "<td>链接</td>");
else if (S_ISCHR(info.st_mode))
fprintf(client_sock, "<td>字符设备</td>");
else if (S_ISBLK(info.st_mode))
fprintf(client_sock, "<td>块设备</td>");
else if (S_ISFIFO(info.st_mode))
fprintf(client_sock, "<td>FIFO</td>");
else if (S_ISSOCK(info.st_mode))
fprintf(client_sock, "<td>Socket</td>");
else
fprintf(client_sock, "<td>(未知)</td>");
fprintf(client_sock, "<td>%s</td>", ctime(&info.st_ctime));
}
fprintf(client_sock, "</tr>\n");
free(realfilename);
}
fprintf(client_sock, "</table></center></body></html>");
} else {
fprintf(client_sock,
"HTTP/1.1 200 OK\r\nServer: DAS by renzhenwen\r\nConnection: close\r\n\r\n<html><head><title>permission denied</title></head>"
"<body><font size=+4>基于Linux线程池的目录访问服务器</font><br><hr width=\"100%%\"><br><center>"
"<table border cols=3 width=\"100%%\">");
fprintf(client_sock,
"</table><font color=\"CC0000\" size=+2>你访问的资源'%s'被禁止访问,请联系管理员解决!</font></body></html>",path);
}
out:
free(realpath);
free(nport);
}
头文件
#ifndef _WORK_
#define _WORK_
#include "unp.h"
void serve_machine(int *connfd) ; /*为客户端服务函数*/
void send_2_server(FILE * client_sock, char *Path);
#endif
普通进程编程守护进程
#include "daemon.h"
#include "unp.h"
/*
* 普通进程变成守护进程,APUE很详细哦
*/
int daemon_ser(const char *name,int facility) {
int i;
pid_t pid;
if(( pid = fork( )) < 0) {
return -1;
} else if (pid > 0) {
_exit(0);
}
if(setsid( ) < 0) {
return -1;
}
signal(SIGHUP, SIG_IGN);
if(( pid = fork( )) < 0) {
return -1;
} else if (pid > 0) {
_exit(0);
}
chdir("/");
for(i = 0; i < MAXFD; i++ ) {
close(i);
}
open("/dev/null", O_RDONLY);
open("/dev/null", O_RDWR);
open("/dev/null", O_RDWR);
openlog(name, LOG_PID, facility);
return 0;
}
/*
* 自定义输出函数,可以递交给syslog守护进程或者终端
*/
void PST(char *ptr) {
syslog(LOG_INFO | LOG_LOCAL7,ptr);
}
头文件
#ifndef _DAEMON_
#define _DAEMON_
#include <syslog.h>
#include <string.h>
#define MAXFD 64
extern int daemon_flag;
int daemon_ser(const char *name,int facility);
void PST(char *ptr);
#endif