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

2024-03-12 14:52

本文主要是介绍《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服务器实现代码,线程池改编的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/801622

相关文章

Linux线程之线程的创建、属性、回收、退出、取消方式

《Linux线程之线程的创建、属性、回收、退出、取消方式》文章总结了线程管理核心知识:线程号唯一、创建方式、属性设置(如分离状态与栈大小)、回收机制(join/detach)、退出方法(返回/pthr... 目录1. 线程号2. 线程的创建3. 线程属性4. 线程的回收5. 线程的退出6. 线程的取消7.

Linux下进程的CPU配置与线程绑定过程

《Linux下进程的CPU配置与线程绑定过程》本文介绍Linux系统中基于进程和线程的CPU配置方法,通过taskset命令和pthread库调整亲和力,将进程/线程绑定到特定CPU核心以优化资源分配... 目录1 基于进程的CPU配置1.1 对CPU亲和力的配置1.2 绑定进程到指定CPU核上运行2 基于

Linux下删除乱码文件和目录的实现方式

《Linux下删除乱码文件和目录的实现方式》:本文主要介绍Linux下删除乱码文件和目录的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux下删除乱码文件和目录方法1方法2总结Linux下删除乱码文件和目录方法1使用ls -i命令找到文件或目录

SpringBoot+EasyExcel实现自定义复杂样式导入导出

《SpringBoot+EasyExcel实现自定义复杂样式导入导出》这篇文章主要为大家详细介绍了SpringBoot如何结果EasyExcel实现自定义复杂样式导入导出功能,文中的示例代码讲解详细,... 目录安装处理自定义导出复杂场景1、列不固定,动态列2、动态下拉3、自定义锁定行/列,添加密码4、合并

mybatis执行insert返回id实现详解

《mybatis执行insert返回id实现详解》MyBatis插入操作默认返回受影响行数,需通过useGeneratedKeys+keyProperty或selectKey获取主键ID,确保主键为自... 目录 两种方式获取自增 ID:1. ​​useGeneratedKeys+keyProperty(推

Spring Boot集成Druid实现数据源管理与监控的详细步骤

《SpringBoot集成Druid实现数据源管理与监控的详细步骤》本文介绍如何在SpringBoot项目中集成Druid数据库连接池,包括环境搭建、Maven依赖配置、SpringBoot配置文件... 目录1. 引言1.1 环境准备1.2 Druid介绍2. 配置Druid连接池3. 查看Druid监控

Linux在线解压jar包的实现方式

《Linux在线解压jar包的实现方式》:本文主要介绍Linux在线解压jar包的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux在线解压jar包解压 jar包的步骤总结Linux在线解压jar包在 Centos 中解压 jar 包可以使用 u

c++ 类成员变量默认初始值的实现

《c++类成员变量默认初始值的实现》本文主要介绍了c++类成员变量默认初始值,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录C++类成员变量初始化c++类的变量的初始化在C++中,如果使用类成员变量时未给定其初始值,那么它将被

Javaee多线程之进程和线程之间的区别和联系(最新整理)

《Javaee多线程之进程和线程之间的区别和联系(最新整理)》进程是资源分配单位,线程是调度执行单位,共享资源更高效,创建线程五种方式:继承Thread、Runnable接口、匿名类、lambda,r... 目录进程和线程进程线程进程和线程的区别创建线程的五种写法继承Thread,重写run实现Runnab

SpringBoot线程池配置使用示例详解

《SpringBoot线程池配置使用示例详解》SpringBoot集成@Async注解,支持线程池参数配置(核心数、队列容量、拒绝策略等)及生命周期管理,结合监控与任务装饰器,提升异步处理效率与系统... 目录一、核心特性二、添加依赖三、参数详解四、配置线程池五、应用实践代码说明拒绝策略(Rejected