本文主要是介绍《TCP/IP网络编程》中多线程HTTP服务器实现代码,线程池改编,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
文章目录
- 最初代码
- 线程池代码
- locker.h
- threadpool.h
- task.h
- main.cpp
- index.html
- 编译
- 执行结果
最初代码
#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 100void* 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); //本机IPserv_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 1024class 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 100void 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); //本机IPserv_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
执行结果
这篇关于《TCP/IP网络编程》中多线程HTTP服务器实现代码,线程池改编的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!