《TCP/IP网络编程》中多线程HTTP服务器实现代码,线程池改编

最初代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <pthread.h>

#define BUF_SIZE 1024
#define SMALL_BUF 100

void* request_handler(void* arg);
void send_data(FILE *fp, char *ct, char *file_name);
const char* content_type(char *file);
void send_error(FILE *fp);
void error_handling(const char* message);

int main(int argc, char *argv[])
{
	if(argc!=2){
		printf("Usage: %s <port>\n", argv[0]);
		exit(1);
	}

	int serv_sock = socket(PF_INET, SOCK_STREAM, 0);

	int option = 1;
	int optlen = sizeof(option);
	setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, (void*)&option, optlen );

	struct sockaddr_in serv_adr;
	memset(&serv_adr,0 ,sizeof(serv_adr));
	serv_adr.sin_family = AF_INET;
	serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);	//本机IP
	serv_adr.sin_port = htons(atoi(argv[1]));	//本机端口

	if(bind(serv_sock,(struct sockaddr*)&serv_adr, sizeof(serv_adr) )== -1 )
	{
		error_handling("bind() error");
	}

	if(listen(serv_sock, 20) == -1 )
	{
		error_handling("listen() error");
	}

	while(1)
	{
		struct sockaddr_in clnt_adr;
		socklen_t clnt_adr_size = sizeof(clnt_adr);

		int clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_adr, &clnt_adr_size );
		printf("Connected Request: %s:%d\n", inet_ntoa(clnt_adr.sin_addr), ntohs(clnt_adr.sin_port) );
		pthread_t t_id;
		pthread_create(&t_id, NULL, request_handler, &clnt_sock);	
		pthread_detach(t_id);
	}
	close(serv_sock);
	return 0;
}

void* request_handler(void *arg)
{
	int clnt_sock =  *((int*)arg);
	char req_line[SMALL_BUF];

	FILE *clnt_read, *clnt_write;
	
	clnt_read = fdopen(clnt_sock, "r");		//读缓存
	clnt_write = fdopen( dup(clnt_sock), "w");	//写缓存
	
	fgets(req_line, SMALL_BUF, clnt_read);	//读数据(浏览器发数据给客户端)
	printf("req_line:%s\n",req_line);
	if(strstr(req_line,"HTTP/")==NULL  )	//不是HTTP协议
	{
		send_error(clnt_write);
		fclose(clnt_read);
		fclose(clnt_write);
		return NULL;
	}

	char method[10],file_name[30],ct[15];
	strcpy( method, strtok(req_line, " /")  );
	strcpy( file_name, strtok(NULL, " /") );
	strcpy( ct, content_type(file_name));
	
	if(strcmp(method, "GET") !=0)	//不是GET方法
	{
		send_error(clnt_write);
		fclose(clnt_read);
		fclose(clnt_write);
		return NULL;
	}
	
	fclose(clnt_read);
	send_data(clnt_write, ct, file_name);

	return NULL;
}

void send_data(FILE *fp, char *ct, char *file_name)
{
	char protocol[] = "HTTP/1.0 200 OK\r\n";
	char server[] = "Server:Linux Web Server \r\n"; 
	char cnt_len[] = "Content-length:2048\r\n";

	char cnt_type[SMALL_BUF];
	char buf[BUF_SIZE];

	sprintf(cnt_type, "Content-type:%s\r\n\r\n",ct);
	FILE* send_file = fopen(file_name, "r");
	if(send_file == NULL)
	{
		send_error(fp);
		fclose(fp);
		return;
	}

	fputs(protocol, fp);
	fputs(server, fp);	
	fputs(cnt_len, fp);
	fputs(cnt_type, fp);

	while(fgets(buf,BUF_SIZE,send_file)!=NULL)
	{
		fputs(buf, fp);
		fflush(fp);
	}
	fflush(fp);
	fclose(fp);
}

const char* content_type(char *file)
{
	char extension[SMALL_BUF];
	char file_name[SMALL_BUF];
	strcpy(file_name, file);

	strtok(file_name,".");
	strcpy(extension, strtok(NULL,".") );

	if(!strcmp(extension, "html")||!strcmp(extension,"htm") )
	{
		return "text/html";
	}
	else
	{
		return "text/plain";
	}
}	

void send_error(FILE *fp)
{
	char protocol[] = "HTTP/1.0 400 Bad Request\r\n";	//状态行
	char server[] = "Server:Linux Web Server\r\n";		//消息头
	char cnt_len[] = "Content-length:2048\r\n";
	char cnt_type[] = "Content-type:text/html\r\n\r\n";	//空行

	char content[] = "<html><head><title>NETWORK </title> </head>"
			  "<body> <font size=+5> <br>404 Error!  </font>   </body>  </html>";
	
	fputs(protocol, fp);
	fputs(server, fp);
		
	fputs(cnt_len, fp);
	fputs(cnt_type, fp);
	fputs(content, fp);
	
	fflush(fp);

}

void error_handling(const char *message)
{
	fputs(message, stderr);
	fputc('\n',stderr);
	exit(1);
}

线程池代码

locker.h

#ifndef LOCKER_H
#define LOCKER_H

#include <exception>
#include <pthread.h>
#include <semaphore.h>

/*封装信号量的类*/
class Sem
{
public:
	/*创建并初始化信号量*/
	Sem()
	{
		if( sem_init( &m_sem, 0, 0 )!=0 )
		{
			/* 构造函数没有返回值,抛出异常来报告错误*/
			throw std::exception();
		}
	}
	/*销毁信号量*/
	~Sem()
	{
		sem_destroy(&m_sem);
	}
	/*等待信号量*/
	bool wait()
	{
		return sem_wait( &m_sem ) == 0;
	}
	/*增加信号量*/
	bool post()
	{
		return sem_post( &m_sem ) == 0;
	}
private:
	sem_t m_sem;
};

/*封装互斥锁*/
class Locker
{
public:
	/*创建并初始化互斥锁*/
	Locker()
	{
		if( pthread_mutex_init(&m_mutex, NULL) != 0 )
		{
			throw std::exception();
		}
	}
	/*销毁互斥锁*/
	~Locker()
	{
		pthread_mutex_destroy( &m_mutex );
	}
	/*获取互斥锁*/
	bool lock()
	{
		return pthread_mutex_lock(&m_mutex) == 0 ;
	}
	/*释放互斥锁*/
	bool unlock()
	{
		return pthread_mutex_unlock(&m_mutex) == 0 ;
	}
private:
	pthread_mutex_t m_mutex;
};

#endif

threadpool.h

#ifndef THREADPOOL_H
#define THREADPOOL_H

#include <list>
#include <cstdio>
#include <exception>
#include <pthread.h>
#include <semaphore.h>
#include "locker.h"

/*线程池类, 将它定义为模板类是为了代码复用。模板参数T是任务类*/
template< typename T >
class ThreadPool
{
public:
	ThreadPool(int thread_number = 8, int max_requests = 10000 );
	~ThreadPool();
	/*往请求队列中添加任务*/
	bool append(T* request);

private:
	/*工作线程运行的函数,它不断从工作队列中取出任务并执行之*/
	static void* worker(void* arg);
	void run();
private:
	int m_thread_number;	//线程池中的线程数
	int m_max_requests;	//请求队列中允许的最大请求数
	pthread_t *m_threads;	//线程描述符数组,大小为m_thread_number	
	std::list<T*> m_workqueue;	/*请求队列 T是任务类*/
	Locker m_queuelocker;	//保护请求队列的互斥锁
	Sem m_queuestat;	//是否有任务需要处理,唤醒
	bool m_stop;		//是否结束线程
};

template <typename T>
ThreadPool<T>::ThreadPool(int thread_number, int max_requests):m_thread_number(thread_number),m_max_requests(max_requests),m_stop(false),m_threads(NULL){
	if(thread_number<=0||max_requests<=0 ) 
	{
		throw std::exception();
	}
	
	m_threads = new pthread_t[m_thread_number];		//动态分配
	if(!m_threads)
	{
		throw std::exception();
	}
	
	/*创建thread_number个线程,并将它们设置为脱离线程*/
	for(int i=0;i<thread_number;i++)
	{
		printf("create the %dth thread\n", i);
		if(pthread_create(m_threads+i, NULL, worker, this ) != 0)	//线程执行worker函数(工作者),把线程池对象传入线程,执行线程的run()函数
		{
			delete[] m_threads;
			throw std::exception();
		}
		if( pthread_detach(m_threads[i]) )
		{
			delete[] m_threads;
			throw std::exception();
		}
	}
}

template< typename T>
ThreadPool<T>::~ThreadPool()
{
	delete[] m_threads;
	m_stop = true;
}

template< typename T>
bool ThreadPool<T>::append(T* request)
{
	/*操作工作队列时一定要加锁*/
	m_queuelocker.lock();
	if(m_workqueue.size() > m_max_requests )	
	{
		m_queuelocker.unlock();
		return false;
	}	
	m_workqueue.push_back(request);	//任务加入队列

	m_queuelocker.unlock();
	m_queuestat.post();		//wait() P, post() V	V操作唤醒run()
	return true;
}

template< typename T>
void* ThreadPool<T>::worker(void* arg)
{
	ThreadPool *pool = (ThreadPool*)arg;
	pool->run();
	return pool;
}

template< typename T>
void ThreadPool<T>::run()
{
	while(!m_stop)
	{
		m_queuestat.wait();	//P操作,有请求
		m_queuelocker.lock();
		if(m_workqueue.empty() )
		{
			m_queuelocker.unlock();
			continue;
		}

		T* request = m_workqueue.front();
		m_workqueue.pop_front();
		m_queuelocker.unlock();

		if(!request)
		{
			continue;
		}
		request->process();
		delete request;
	}
}

#endif

task.h

#ifndef TASK_H
#define TASK_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <pthread.h>

#define SMALL_BUF 100
#define BUF_SIZE 1024

class Task{
public:
	Task(int clnt_sock):m_clnt_sock(clnt_sock){
		
	}
	void process();
	void send_data(FILE *fp, char *ct, char *file_name);
	const char* content_type(char *file);
	void send_error(FILE *fp);
	void error_handling(const char *message);
private:
	int m_clnt_sock;
};

void Task::process()
{	
	int clnt_sock = m_clnt_sock;
	char req_line[SMALL_BUF];

	FILE *clnt_read, *clnt_write;
	
	clnt_read = fdopen(clnt_sock, "r");		//读缓存
	clnt_write = fdopen( dup(clnt_sock), "w");	//写缓存
	
	fgets(req_line, SMALL_BUF, clnt_read);	//读数据(浏览器发数据给客户端)
	printf("req_line:%s\n",req_line);
	if(strstr(req_line,"HTTP/")==NULL  )	//不是HTTP协议
	{
		send_error(clnt_write);
		fclose(clnt_read);
		fclose(clnt_write);
		return;
	}

	char method[10],file_name[30],ct[15];
	strcpy( method, strtok(req_line, " /")  );
	strcpy( file_name, strtok(NULL, " /") );
	strcpy( ct, content_type(file_name));
	
	if(strcmp(method, "GET") !=0)	//不是GET方法
	{
		send_error(clnt_write);
		fclose(clnt_read);
		fclose(clnt_write);
		return;
	}
	
	fclose(clnt_read);
	send_data(clnt_write, ct, file_name);
}


void Task::send_data(FILE *fp, char *ct, char *file_name)
{
	char protocol[] = "HTTP/1.0 200 OK\r\n";
	char server[] = "Server:Linux Web Server \r\n"; 
	char cnt_len[] = "Content-length:2048\r\n";

	char cnt_type[SMALL_BUF];
	char buf[BUF_SIZE];

	sprintf(cnt_type, "Content-type:%s\r\n\r\n",ct);
	FILE* send_file = fopen(file_name, "r");
	if(send_file == NULL)
	{
		send_error(fp);
		fclose(fp);
		return;
	}

	fputs(protocol, fp);
	fputs(server, fp);	
	fputs(cnt_len, fp);
	fputs(cnt_type, fp);

	while(fgets(buf,BUF_SIZE,send_file)!=NULL)
	{
		fputs(buf, fp);
		fflush(fp);
	}
	fflush(fp);
	fclose(fp);
}

const char* Task::content_type(char *file)
{
	char extension[SMALL_BUF];
	char file_name[SMALL_BUF];
	strcpy(file_name, file);

	strtok(file_name,".");
	strcpy(extension, strtok(NULL,".") );

	if(!strcmp(extension, "html")||!strcmp(extension,"htm") )
	{
		return "text/html";
	}
	else
	{
		return "text/plain";
	}
}


void Task::send_error(FILE *fp)
{
	char protocol[] = "HTTP/1.0 400 Bad Request\r\n";	//状态行
	char server[] = "Server:Linux Web Server\r\n";		//消息头
	char cnt_len[] = "Content-length:2048\r\n";
	char cnt_type[] = "Content-type:text/html\r\n\r\n";	//空行

	char content[] = "<html><head><title>NETWORK </title> </head>"
			  "<body> <font size=+5> <br>404 Error!  </font>   </body>  </html>";
	
	fputs(protocol, fp);
	fputs(server, fp);
		
	fputs(cnt_len, fp);
	fputs(cnt_type, fp);
	fputs(content, fp);
	
	fflush(fp);
}		

void Task::error_handling(const char *message)
{
	fputs(message, stderr);
	fputc('\n',stderr);
	exit(1);
}

#endif

main.cpp

#include "locker.h"
#include "threadpool.h"
#include "task.h"
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <pthread.h>

#define BUF_SIZE 1024
#define SMALL_BUF 100

void error_handling(const char* message)
{
	fputs(message, stderr);
	fputc('\n',stderr);
	exit(1);
}

int main(int argc, char *argv[] )
{
	/*ThreadPool<Task> *pool = NULL;
	try
	{
		pool = new ThreadPool<Task>;
	}
	catch(...)
	{
		return 1;
	}
	for(int i = 0; i<5; i++)
	{
		pool->append(new Task());
	}	
	sleep(20);*/
	if(argc!=2){
		printf("Usage: %s <port>\n", argv[0]);
		exit(1);
	}

	int serv_sock = socket(PF_INET, SOCK_STREAM, 0);

	int option = 1;
	int optlen = sizeof(option);
	setsockopt(serv_sock, SOL_SOCKET, SO_REUSEADDR, (void*)&option, optlen );

	struct sockaddr_in serv_adr;
	memset(&serv_adr,0 ,sizeof(serv_adr));
	serv_adr.sin_family = AF_INET;
	serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);	//本机IP
	serv_adr.sin_port = htons(atoi(argv[1]));	//本机端口

	if(bind(serv_sock,(struct sockaddr*)&serv_adr, sizeof(serv_adr) )== -1 )
	{
		error_handling("bind() error");
	}

	if(listen(serv_sock, 20) == -1 )
	{
		error_handling("listen() error");
	}

	ThreadPool<Task> *pool = NULL;
        try
        {
                pool = new ThreadPool<Task>;
        }
        catch(...)
        {
                return 1;
        }
	
	while(1)
	{
		struct sockaddr_in clnt_adr;
		socklen_t clnt_adr_size = sizeof(clnt_adr);

		int clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_adr, &clnt_adr_size );
		printf("Connected Request: %s:%d\n", inet_ntoa(clnt_adr.sin_addr), ntohs(clnt_adr.sin_port) );

		pool->append(new Task(clnt_sock));
		
		/*pthread_t t_id;
		pthread_create(&t_id, NULL, request_handler, &clnt_sock);	
		pthread_detach(t_id);*/
	}
		
	close(serv_sock);

	return 0;
}

index.html

<html>
<head><title>Network</title>  </head>

<body>
<font size= +5><br> net_program interesting! </font>
</body>

</html>

编译

g++ -g locker.h task.h threadpool.h main.cpp -o main -lpthread

执行结果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值