《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

相关文章

Java实现字节字符转bcd编码

《Java实现字节字符转bcd编码》BCD是一种将十进制数字编码为二进制的表示方式,常用于数字显示和存储,本文将介绍如何在Java中实现字节字符转BCD码的过程,需要的小伙伴可以了解下... 目录前言BCD码是什么Java实现字节转bcd编码方法补充总结前言BCD码(Binary-Coded Decima

SpringBoot全局域名替换的实现

《SpringBoot全局域名替换的实现》本文主要介绍了SpringBoot全局域名替换的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录 项目结构⚙️ 配置文件application.yml️ 配置类AppProperties.Ja

Python实现批量CSV转Excel的高性能处理方案

《Python实现批量CSV转Excel的高性能处理方案》在日常办公中,我们经常需要将CSV格式的数据转换为Excel文件,本文将介绍一个基于Python的高性能解决方案,感兴趣的小伙伴可以跟随小编一... 目录一、场景需求二、技术方案三、核心代码四、批量处理方案五、性能优化六、使用示例完整代码七、小结一、

Java实现将HTML文件与字符串转换为图片

《Java实现将HTML文件与字符串转换为图片》在Java开发中,我们经常会遇到将HTML内容转换为图片的需求,本文小编就来和大家详细讲讲如何使用FreeSpire.DocforJava库来实现这一功... 目录前言核心实现:html 转图片完整代码场景 1:转换本地 HTML 文件为图片场景 2:转换 H

C#使用Spire.Doc for .NET实现HTML转Word的高效方案

《C#使用Spire.Docfor.NET实现HTML转Word的高效方案》在Web开发中,HTML内容的生成与处理是高频需求,然而,当用户需要将HTML页面或动态生成的HTML字符串转换为Wor... 目录引言一、html转Word的典型场景与挑战二、用 Spire.Doc 实现 HTML 转 Word1

C#实现一键批量合并PDF文档

《C#实现一键批量合并PDF文档》这篇文章主要为大家详细介绍了如何使用C#实现一键批量合并PDF文档功能,文中的示例代码简洁易懂,感兴趣的小伙伴可以跟随小编一起学习一下... 目录前言效果展示功能实现1、添加文件2、文件分组(书签)3、定义页码范围4、自定义显示5、定义页面尺寸6、PDF批量合并7、其他方法

SpringBoot实现不同接口指定上传文件大小的具体步骤

《SpringBoot实现不同接口指定上传文件大小的具体步骤》:本文主要介绍在SpringBoot中通过自定义注解、AOP拦截和配置文件实现不同接口上传文件大小限制的方法,强调需设置全局阈值远大于... 目录一  springboot实现不同接口指定文件大小1.1 思路说明1.2 工程启动说明二 具体实施2

Python实现精确小数计算的完全指南

《Python实现精确小数计算的完全指南》在金融计算、科学实验和工程领域,浮点数精度问题一直是开发者面临的重大挑战,本文将深入解析Python精确小数计算技术体系,感兴趣的小伙伴可以了解一下... 目录引言:小数精度问题的核心挑战一、浮点数精度问题分析1.1 浮点数精度陷阱1.2 浮点数误差来源二、基础解决

Java实现在Word文档中添加文本水印和图片水印的操作指南

《Java实现在Word文档中添加文本水印和图片水印的操作指南》在当今数字时代,文档的自动化处理与安全防护变得尤为重要,无论是为了保护版权、推广品牌,还是为了在文档中加入特定的标识,为Word文档添加... 目录引言Spire.Doc for Java:高效Word文档处理的利器代码实战:使用Java为Wo

Java实现远程执行Shell指令

《Java实现远程执行Shell指令》文章介绍使用JSch在SpringBoot项目中实现远程Shell操作,涵盖环境配置、依赖引入及工具类编写,详解分号和双与号执行多指令的区别... 目录软硬件环境说明编写执行Shell指令的工具类总结jsch(Java Secure Channel)是SSH2的一个纯J