中建二局核电建设分公司网站,搜索推广,免费静态网站托管平台,wordpress仿蛙壳网主题hello #xff01;大家好呀#xff01; 欢迎大家来到我的网络编程系列之洪水网络攻击#xff0c;在这篇文章中#xff0c;你将会学习到在网络编程中如何搭建一个高性能的并发服务器#xff0c;并且我会给出源码进行剖析#xff0c;以及手绘UML图来帮助大家来理解#xf… hello 大家好呀 欢迎大家来到我的网络编程系列之洪水网络攻击在这篇文章中你将会学习到在网络编程中如何搭建一个高性能的并发服务器并且我会给出源码进行剖析以及手绘UML图来帮助大家来理解希望能让大家更能了解网络编程技术 希望这篇文章能对你有所帮助大家要是觉得我写的不错的话那就点点免费的小爱心吧 目录
一.网络服务器 1.1 普通循环网络服务器 2.2 简单并发网络服务器
2.2.1简单的并发服务器模型
2.2.2使用进程的并发服务器
2.2.3使用线程的并发服务器
2.2.4其他并发服务器模型
二.使用互斥锁实现单线程处理单个客户
2.1 具体步骤
2.2服务器代码模板
三.源码剖析 一.网络服务器 1.1 普通循环网络服务器
对于普通的循环网络服务器其实就是服务器使用循环的方法逐个对客户的连接进行处理处理完一个连接后再处理下一个连接其过程如下 最简单的代码模型我还是给大家
#includet_stdio.h
#includesys/types.h
#include sys/socket.h
#includearpa/inet.h
#include sys/socket.h
#includectype.h
#includeunistd.h
int main(void){struct sockaddr_in serv,cli;socklen_t cli_len;char buf[128];char IP[32];//创建一个通讯端点返回该端点的文件描述符//创建一个ipv4的tcp连接端口int s_fdsocket( AF_INET ,SOCK_STREAM ,0);//需要对server变量成员初始化serv.sin_familyAF_INET;serv.sin_porthtons(5556);serv.sin_addr.s_addrhtonl(INADDR_ANY);//将s_fd和本地地址端口号绑定int bbind(s_fd,(struct sockaddr *)serv,sizeof(serv));if(b-1)E_MSG(bind,-1);if(s_fd-1)E_MSG(socket,-1);//将s_fd设置为被动连接监听客户端连接的到来 //将客户端到来的连接放入未决连接队列中//指定未决连接队列的长度listen(s_fd,5);while(1){//从s_fd设备的未连接队列中提取一个进程进行处理//返回一个连接描述符使用这个连接描述符与客户端进行通讯int c_fdaccept(s_fd,(struct sockaddr *)cli,cli_len);if(c_fd-1)E_MSG(accept,-1);//binary---textinet_ntop(AF_INET,cli.sin_addr,IP,32); printf(client ip: %s\n,IP);//代码执行到这里三次握手以及完成可以进行数据传输了//从c_fd中读取客户端发送过来的请求信息int r read(c_fd,buf,128);//处理客户端的请求信息int i;for(i0;ir;i){buf[i]toupper(buf[i]);}//将处理结果回送客户端write(c_fd,buf,r);//关闭本次连接close(c_fd);}return 0;
}
这是最简单的循环服务器代码功能是将客户传过来的字符串全部转换为大写这个最简单代码希望大家能全部弄懂关于里面还有不懂的可以去看我我前面的博客[C/Linux] socket套接字函数-CSDN博客 2.2 简单并发网络服务器
并发网络服务器是指能够同时处理多个客户端请求的网络服务器。这种服务器的设计允许它在任何时刻处理多个客户端的连接和请求而不会因为某个请求的处理而阻塞其他请求。并发服务器可以提高资源的利用率增强服务器的响应能力是现代网络应用的基础。下面我将介绍几种常见的并发网络服务器模型
2.2.1简单的并发服务器模型 迭代服务器Iterative Server 这种服务器一次处理一个请求。它接收一个请求处理完该请求然后才接收下一个请求。这种模型简单但效率低下因为它在处理一个请求时不能处理其他请求。 并发服务器Concurrent Server 并发服务器可以同时处理多个请求。这通常通过多进程或多线程来实现。服务器的主进程或线程监听端口接受新的连接然后为每个连接创建一个新的进程或线程来处理请求。
2.2.2使用进程的并发服务器 多进程服务器Multiprocess Server 在这个模型中服务器的主进程监听端口接受新的连接。每当有一个新的连接时主进程就fork一个子进程来处理这个连接。每个子进程都可以独立地与客户端通信处理请求。这种模型的优点是代码简单缺点是进程创建和销毁的开销较大。 预派生子进程服务器Pre-forking Server 这种服务器在启动时就预先创建一定数量的子进程每个子进程都阻塞在accept调用上等待新的连接。当一个连接到达时其中一个子进程接受连接并处理请求。这种模型减少了进程创建的开销但需要预先分配资源。
这里我手绘一个UML图来帮助大家理解如何利用进程池
2.2.3使用线程的并发服务器 多线程服务器Multithreaded Server 在这个模型中服务器的主线程监听端口接受新的连接。每当有一个新的连接时主线程就创建一个新的线程来处理这个连接。由于线程共享内存空间因此它们之间可以更容易地共享数据但这也带来了同步问题。 线程池服务器Thread Pool Server 线程池服务器预先创建一定数量的工作线程这些线程都阻塞在等待任务队列上。当一个新的连接到达时主线程将连接放入任务队列工作线程从队列中取出连接并处理请求。这种模型可以限制线程的数量减少线程创建和销毁的开销。
这里我手绘一个UML图来帮助大家理解如何利用线程池 对于进程和线程大家有不了解的可以看我前面博客[C/Linux] Linux线程详解-CSDN博客 2.2.4其他并发服务器模型 事件驱动服务器Event-Driven Server 事件驱动服务器使用非阻塞IO和事件循环来处理多个客户端连接。服务器注册感兴趣的事件如可读、可写事件然后在一个循环中等待这些事件的发生。当事件发生时服务器处理相应的事件。这种模型可以非常高效地处理大量连接。 异步IO服务器Asynchronous I/O Server 异步IO服务器使用操作系统提供的异步IO接口来处理请求。服务器发起IO操作然后继续处理其他任务。当IO操作完成时操作系统通知服务器。这种模型可以充分利用CPU资源因为它不需要为每个请求都创建一个线程或进程。 二.使用互斥锁实现单线程处理单个客户
这里我们使用互斥锁来对每个进行上锁实现单客户单进程处理
2.1 具体步骤 初始化互斥锁在服务器启动时初始化一个互斥锁。 接受连接服务器的主线程循环接受客户端连接。 创建服务线程每当接受一个新连接时服务器创建一个新的服务线程来处理该连接。 加锁处理在每个服务线程中当开始处理客户请求之前首先尝试获取互斥锁。如果互斥锁已被其他线程持有线程将阻塞直到互斥锁被释放。 处理请求线程获取互斥锁后开始处理客户请求。 释放锁处理完请求后线程释放互斥锁以便其他线程可以获取该锁并处理下一个请求。 线程退出处理完成后线程退出或返回到池中等待下一个请求
2.2服务器代码模板
#include pthread.hpthread_mutex_t lock PTHREAD_MUTEX_INITIALIZER;void *handle_client(void *client_socket) {int socket *(int *)client_socket;// 加锁pthread_mutex_lock(lock);// 处理客户请求// ...// 释放锁pthread_mutex_unlock(lock);// 关闭客户端套接字close(socket);return NULL;
}int main() {// 创建监听套接字// ...while (1) {int client_socket accept(listen_socket, NULL, NULL);// 创建线程来处理客户端pthread_t thread;pthread_create(thread, NULL, handle_client, client_socket);pthread_detach(thread); // 使线程独立运行}// 关闭监听套接字// ...return 0;
}三.源码剖析
#includet_stdio.h
#includet_file.h
#includestdlib.h
#include sys/types.h
#include sys/socket.h
#include string.h
#include arpa/inet.h
#includeunistd.h
#includetime.h
#includepthread.h
#include string.h#define bufferlen 1024 //发送/接收数据缓冲区大小
#define server_port 8888 //端口
#define backlog 5 //监听队列
#define max_pthread 3 //最大线程数//线程处理业务函数
pthread_mutex_t ALOCK PTHREAD_MUTEX_INITIALIZER ;//创建互斥量static void * handle_request(void * argv){int s_s *((int *) argv);int s_c;struct sockaddr_in from ;socklen_t len sizeof(from);for(;;){time_t now;char buf [bufferlen];int n0;pthread_mutex_lock(ALOCK); //进入互斥区s_c accept(s_s , (struct sockaddr *)from , len);//接收请求pthread_mutex_unlock(ALOCK); //离开互斥区memset(buf , 0 ,bufferlen);n recv(s_c , buf , bufferlen , 0);//接收数据if(n 0 !strncmp(buf , TIME , 4))//判断是否为合法接收数据{memset(buf ,0 ,bufferlen);now time(NULL);sprintf(buf , %24s\r\n,ctime(now));//时间写入bufsend(s_c , buf , strlen(buf) , 0);//发送给客户端}close(s_c);}return ;}//线程创建函数
static void handle_connect(int s){int s_s s;pthread_t thread_do[max_pthread];//创建线程数组int i0;//创建线程每一次创建调用线程处理函数for(i 0; imax_pthread;i){pthread_create(thread_do[i] , NULL, handle_request , (void *)s_s);}//等待线程结束for(i 0; imax_pthread;i){pthread_join(thread_do[i] , NULL);}}int main(int argc ,char * argvp[]){int s_s;struct sockaddr_in local ;//本地地址s_s socket(AF_INET , SOCK_STREAM , 0);memset(local , 0 , sizeof(local));local.sin_family AF_INET;local.sin_addr.s_addr htonl(INADDR_ANY);local.sin_port htons(server_port);bind(s_s , (struct sockaddr *)local ,sizeof(local));//连接本地地址listen(s_s , backlog);//创建监听队列handle_connect(s_s);close(s_s);return 0;
}
这段代码是一个简单的网络服务器示例它使用了 POSIX 线程pthread来处理客户端请求。下面我将逐行解释代码的功能
#include t_stdio.h
#include t_file.h
#include stdlib.h
#include sys/types.h
#include sys/socket.h
#include string.h
#include arpa/inet.h
#include unistd.h
#include time.h
#include pthread.h
#include string.h这里包含了必要的头文件包括标准输入输出、文件操作、网络编程、字符串操作、IP地址转换、非阻塞I/O等。
#define bufferlen 1024 //发送/接收数据缓冲区大小
#define server_port 8888 //端口
#define backlog 5 //监听队列
#define max_pthread 3 //最大线程数定义了一些宏用于设置缓冲区大小、服务器端口、监听队列大小和最大线程数。
//线程处理业务函数
pthread_mutex_t ALOCK PTHREAD_MUTEX_INITIALIZER ;//创建互斥量定义了一个互斥量 ALOCK用于线程间的同步。
static void * handle_request(void * argv){int s_s *((int *) argv);int s_c;struct sockaddr_in from ;socklen_t len sizeof(from);for(;;){time_t now;char buf [bufferlen];int n0;pthread_mutex_lock(ALOCK); //进入互斥区s_c accept(s_s , (struct sockaddr *)from , len);//接收请求pthread_mutex_unlock(ALOCK); //离开互斥区memset(buf , 0 ,bufferlen);n recv(s_c , buf , bufferlen , 0);//接收数据if(n 0 !strncmp(buf , TIME , 4))//判断是否为合法接收数据{memset(buf ,0 ,bufferlen);now time(NULL);sprintf(buf , %24s\r\n,ctime(now));//时间写入bufsend(s_c , buf , strlen(buf) , 0);//发送给客户端}close(s_c);}return ;
}handle_request 函数是线程处理业务的核心。它接受一个整数参数 s_s这是服务器套接字。函数进入一个无限循环接收客户端的连接accept 调用接收数据recv 调用处理数据如果数据是以 “TIME” 开头的则返回当前时间然后关闭客户端套接字。 好啦到这里这篇文章就结束啦关于实例代码中我写了很多注释如果大家还有不懂得可以评论区或者私信我都可以哦 感谢大家的阅读我还会持续创造网络编程相关内容的记得点点小爱心和关注哟