高新区免费网站建设,上海网站推广优化,网站平台建设所需开发工具,wordpress 引用页面目录 前言
1.存在的问题
2.多进程版
3.多线程版
4.线程池版
总结 前言 在上一篇文章中使用TCP协议实现了一个简单的服务器#xff0c;可以用来服务端和客户端通信#xff0c;但是之前的服务器存在一个问题#xff0c;就是当有多个客户端连接服务器的时候#xff0c;服…
目录 前言
1.存在的问题
2.多进程版
3.多线程版
4.线程池版
总结 前言 在上一篇文章中使用TCP协议实现了一个简单的服务器可以用来服务端和客户端通信但是之前的服务器存在一个问题就是当有多个客户端连接服务器的时候服务器只能和一个客户端通信其它的客户端是无法通信的这是为什么呢有该如何解决呢将在这篇文章中为大家介绍
1.存在的问题
如图所示 为什么会存在这样的问题呢 如上图所示之前我们实现的服务器是单进程版的所以当第一次和客户端建立连接完成之后就死循环处理读取信息的逻辑了所以就无法再和新的客户端建立连接了 。找到问题之后很明显解决方式就是将建立连接和通信分开执行此时我们就可以使用多进程和多线程来解决了下面我们就具体来实现以下如何使用多进程和多线程。
2.多进程版
实现思路与客户端建立连接完成fork创建子进程让父进程继续建立连接让子进程实现后续的数据通信。
这样实现存在的问题当父进程创建完子进程之后需要使用waitpid回收子进程的资源否则子进程就会变为僵尸进程导致资源泄漏但是使用waitpid回收子进程资源程序变为串行化执行了就无法实现之前的需求让父进程负责建立连接子进程负责数据通信了。
解决方式有两种 1.fork创建子进程在子进程内部再fork创建子进程然后让父进程直接退出此时之前的子进程作为父进程退出新创建的子进程就变为孤儿进程被操作系统领养并不会造成资源泄漏并且让该子进程负责通信 2.因为子进程退出之后操作系统会发送一个SIGCHLD信号所以可以使用signal函数捕捉SIGCHLD信号将默认处理动作设置为SIG_IGN,在这个默认动作里会回收子进程的资源并不会造成资源泄漏并且让该子进程负责通信 思路1代码
pid_t id fork();
if(id 0)//child
{close(_sock);if(fork() 0)exit(0);serviceIO(sock);close(sock);exit(0);
}
close(sock);
waitpid(id,nullptr,0);
思路2代码
signal(SIGCHLD,SIG_IGN);
if(id 0)//child
{// 子进程会继承父进程的文件描述符表当子进程不需要时进行关闭// 防止子进程文件描述符资源泄露close(_sock);serviceIO(sock);close(sock);exit(0);
}
运行截图
[mylVM-8-12-centos tcp]$ ./tcpServer 8080 create socket success bind socket success listen socket success accept a new link success sock: 4 accept a new link success sock: 4 recvice message: 你好我是客户端1 recvice message: 你好我是客户端2
此时就实现了一个客户端可以被多个服务端连接并且实现通信。
3.多线程版
说明相比于多进程多线程的创建和销毁对操作系统是更轻量的消耗的资源也是更少的所以实现数据通信可以采用多线程的方式让主线程负责建立让从线程负责数据通信
代码实现
class TcpServerData
{
public:TcpServerData(TcpServer* self,int sock):_self(self),_sock(sock) {}
public:TcpServer* _self;int _sock;
};
cout 我是主线程 endl;
pthread_t tid;
TcpServerData* tsd new TcpServerData(this,sock);
pthread_create(tid,nullptr,start_routine,tsd);//因为是类内成员函数,必须包含this指针但是start_routine作为参数是没有this指针的
//所以start_routine函数必须加上static,静态成员方法是不能访问类内成员的所以参数传递this
//调用serviceIO,但是serviceIO函数需要传递参数sock,所以可以封装一个结构体在结构体中包含成员
//属性sock和this
static void* start_routine(void* args) {
//设置与主线程分离此时主线程不需要等待从线程退出了而是继续建立连接
cout 我是从线程 endl;
pthread_detach(pthread_self());
TcpServerData* t static_castTcpServerData*(args);
t-_self-serviceIO(t-_sock);
close(t-_sock);
delete t;
return nullptr;
运行截图
[mylVM-8-12-centos tcp]$ ./tcpServer 8080 create socket success bind socket success listen socket success accept a new link success sock: 4 我是主线程 我是从线程 recvice message: 你好我是客户端1 accept a new link success sock: 5 我是主线程 我是从线程 recvice message: 你好我是客户端2
4.线程池版
说明线程池版的实现思路是基于多线程虽然多线程创建和销毁的消耗比多进程的低但是为了更进一步提升效率可以预先创建好一批线程主线程负责建立连接获取任务然后将任务加入到队列中让预先创建好的线程从队列中获取任务然后处理获取到的任务。
代码实现
void start()
{//线程池初始化预先创建好一批线程ThreadPoolTask::getInstance()-run();for (;;){// 建立连接struct sockaddr_in peer;socklen_t len sizeof(peer);int sock accept(_sock, (struct sockaddr *)peer, len); if (sock 0){logMessage(ERROR, accept error, next);continue;}logMessage(NORMAL, accept a new link success);std::cout sock: sock std::endl;//未来通信全部用sock,面向字节流的后续全部都是文件操作ThreadPoolTask::getInstance()-push(Task(sock,serviceIO));}
}
运行截图
[mylVM-8-12-centos tcp]$ ./tcpServer 8080 create socket success bind socket success listen socket success thread-1 start ... thread-2 start ... thread-3 start ... thread-4 start ... thread-5 start ... thread-6 start ... thread-7 start ... thread-8 start ... thread-9 start ... thread-10 start ... accept a new link success sock: 4 accept a new link success sock: 5 recv message: 你好我是客户端1 recv message: 你好我是客户端2 注关于线程池详细的设计与实现可以观看线程池这篇文章里面有相信的代码实现
总结 以上就是关于TCP服务器实现多进程版多线程版线程池版的详细介绍可以通过这篇文章发现之前在系统中学习的知识在网络中全部结合起来了今天的介绍就到这里了感谢大家的阅读我们下次再见