c++多线程模式下的socket编程(线程池实现)

     socket 编程可以说是一个基本的技术掌握,而多个客户端向服务端发送请求又是一个非常常见的场景,因此多线程模式下的socket编程则显得尤为常见与重要。

    本文主要利用线程池的技术,来实现多线程的模式,线程池的优点就不多述了,相信大家都能理解,就是减少了线程创建于销毁的时间,提高多线程的性能。

    首先,先写个线程池:    下面分别是头文件 和cpp文件

#ifndef __THREAD_H  
#define __THREAD_H  

/*********************
** Filename: Thread.h
** Dsricbe: 线程池头文件
** Date: 2018.7.18
** @author: xionglei

***/


#include <deque>
#include <string>  
#include <pthread.h>  
  
using namespace std;  
  
/** 
 * 执行任务的类,设置accept()描述符并执行 
 */  
class CTask {
protected:
    string m_strTaskName;   //任务的名称
    int connfd;    //接收的地址

public:
    CTask() = default;
    CTask(string &taskName): m_strTaskName(taskName), connfd(NULL) {}
    virtual int Run() = 0;
    void SetConnFd(int data);   //设置接收的套接字连接号。
    int GetConnFd();
    virtual ~CTask() {}
    
};




/** 
 * 线程池类的实现 
 */  
class CThreadPool  
{  
private:  
    static  deque<CTask*> m_deqTaskList;     /** 任务队列 */  
    static  bool shutdown;                    /** 线程退出标志 */           
    int     m_iThreadNum;                     /** 线程池中启动的线程数 */  
    pthread_t   *pthread_id;  
      
    static pthread_mutex_t m_pthreadMutex;    /** 线程同步锁 */  
    static pthread_cond_t m_pthreadCond;      /** 线程同步的条件变量 */  
  
protected:  
    static void* ThreadFunc(void * threadData); /** 新线程的线程回调函数 */  
    static int MoveToIdle(pthread_t tid);       /** 线程执行结束后,把自己放入到空闲线程中 */  
    static int MoveToBusy(pthread_t tid);       /** 移入到忙碌线程中去 */  
      
    int Create();          /** 创建线程池中的线程 */  
  
public:  
    CThreadPool(int threadNum = 10);  
    int AddTask(CTask *task);      /** 把任务添加到任务队列中 */  
    int StopAll();                 /** 使线程池中的线程退出 */  
    int getTaskSize();             /** 获取当前任务队列中的任务数 */  
};  
  
#endif  

Thread.cpp
 

/******************
** Fliename: Thread.cpp
** Dscribe: 线程池实现文件
** Date: 2018.7.18
** @author: xionglei
***/


#include "Thread.h"  
#include <iostream>  
#include <stdio.h> 
#include <deque>


void CTask::SetConnFd(int data)  
{  
    connfd = data;  
}  

int CTask::GetConnFd()
{
    return connfd;
}
/**
* 初始化数据
*/
deque<CTask*> CThreadPool::m_deqTaskList;         //任务列表  
bool CThreadPool::shutdown = false;  
      
pthread_mutex_t CThreadPool::m_pthreadMutex = PTHREAD_MUTEX_INITIALIZER;   
pthread_cond_t CThreadPool::m_pthreadCond = PTHREAD_COND_INITIALIZER;  
  
/** 
 * 线程池管理类构造函数 
 */  
CThreadPool::CThreadPool(int threadNum)  
{  
    this->m_iThreadNum = threadNum;  
    cout << "I will create " << threadNum << " threads" << endl;  
    Create();       //*创建对象时便创建线程。
}


/** 
 * 线程回调函数 
 */  
void* CThreadPool::ThreadFunc(void* threadData)  
{  
    pthread_t tid = pthread_self();  
    while (1)  
    {  

        //* 线程开启时先上锁 */
        pthread_mutex_lock(&m_pthreadMutex);  
        while (m_deqTaskList.size() == 0 && !shutdown)  
        {  
            //* 没有任务时,线程等待状态(条件变量)*/
            pthread_cond_wait(&m_pthreadCond, &m_pthreadMutex);  
        }  
          
        if (shutdown)  
        {  
            pthread_mutex_unlock(&m_pthreadMutex);  
            printf("thread %lu will exit\n", pthread_self());  
            pthread_exit(NULL);   
        }  
          
        printf("tid %lu run\n", tid);  
            
        /** 
        * 取任务队列并处理之 
        */ 

        //deque<CTask*>::iterator iter = m_deqTaskList.front();
        CTask* task = m_deqTaskList.front();  
        m_deqTaskList.pop_front();

        //* 取完任务后释放锁*/
        pthread_mutex_unlock(&m_pthreadMutex);  
          
        task->Run(); /** 执行任务 */  
         
    }  
    return (void*)0;  
}  





/** 
 * 往任务队列里边添加任务并发出线程同步信号 
 */  
int CThreadPool::AddTask(CTask *task)  
{  
    pthread_mutex_lock(&m_pthreadMutex);  
    this->m_deqTaskList.push_back(task);  
    pthread_mutex_unlock(&m_pthreadMutex); 

    // * 添加任务 条件变量发信号,非阻塞  */
    pthread_cond_signal(&m_pthreadCond);       
    return 0;  
}  
  
/** 
 * 创建线程 
 */  
int CThreadPool::Create()  
{  
    pthread_id = (pthread_t*)malloc(sizeof(pthread_t) * m_iThreadNum);  
    for(int i = 0; i < m_iThreadNum; i++)  
    {  
        pthread_create(&pthread_id[i], NULL, ThreadFunc, NULL);  
    }  
    return 0;  
}  





/** 
 * 停止所有线程 
 */  
int CThreadPool::StopAll()  
{  
    /** 避免重复调用 */  
    if (shutdown)  
    {  
        return -1;    
    }  
    printf("Now I will end all threads!!\n");  
    /** 唤醒所有等待线程,线程池要销毁了 */  
    shutdown = true;  
    pthread_cond_broadcast(&m_pthreadCond);  
      
    /** 阻塞等待线程退出,否则就成僵尸了 */  
    for (int i = 0; i < m_iThreadNum; i++)  
    {  
        pthread_join(pthread_id[i], NULL);    
    }  
      
    free(pthread_id);  
    pthread_id = NULL;  
      
    /** 销毁条件变量和互斥体 */  
    pthread_mutex_destroy(&m_pthreadMutex);  
    pthread_cond_destroy(&m_pthreadCond);  
      
    return 0;  
}  




/** 
 * 获取当前队列中任务数 
 */  
int CThreadPool::getTaskSize()  
{  
    return m_deqTaskList.size();      
}  







好了,有了线程池这个利器,来实现多线程可以说就比较容易了,先来整理一下思路:首先服务端创建socket,绑定、监听后,每接收到一个客户端的连接请求,就创建一个任务,丢到任务队列里去,任务队列有数据后,唤醒等待的线程,执行任务。

服务端的代码:


/**********************
** FileName: Service.cpp
** Dscribe: 服务端程序
** Date:2018.7.19
** @author: xionglei
***/



#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <pthread.h>
#include <assert.h>
#include "Thread.h"
//#include "BookInfo.h"
//#include "StrInfo.h"

class CMyTask: public CTask 
{   
 public:
    CMyTask() = default;    
    int Run()
    {
        //printf("%s\n", (char*)m_ptrData);
        //int x = rand()%4 + 1;
        //sleep(x);   
        //return 0;
        
        int connfd= GetConnFd();
        while(1)
        {
            char recvbuf[1024];
            char sendbuf[1024];
           // Book book= new Book;
            
            //BookInfo bf=book.find_book("茶花女");
            //sprintf(sendbuf,"res: %d,%s",bf.book_no,bf.book_name);
            
            memset(recvbuf,0x00,sizeof(recvbuf));
            memset(sendbuf,0x00,sizeof(sendbuf));
            
            //printf("function run test.\n");
            int len = recv(connfd,recvbuf,sizeof(recvbuf),0);
            if(len <= 0)
                printf("no buf.\n");
            printf("%s \n",recvbuf);
            printf("Please input: ");
            fflush(stdout);
            fgets(sendbuf,1024,stdin);
            if(strncmp(sendbuf,"end",3)==0)  
            {  
                close(connfd);  
                break;  
            }  
            send(connfd,sendbuf,sizeof(sendbuf),0);
                    
        }
        close(connfd);
        return 0;
    }
};





int main(int argc, char* argv[])
{
    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    assert(sockfd!=-1);
    struct sockaddr_in ser,cli;
    memset(&ser,0,sizeof(ser));
    ser.sin_family=AF_INET;
    inet_aton("127.0.0.1",&ser.sin_addr);
    ser.sin_port=htons(6500);

    int res=bind(sockfd,(struct sockaddr*)&ser,sizeof(ser));
    assert(res!=-1);

    listen(sockfd,5);
    //创建线程池
    CThreadPool Pool(5);

    //for(int i=0;i<5;i++)
    //{
      // Task* ta=new Task;
       //Pool.AddTask(ta);
    //}
    
    while(1)
    {
       socklen_t len=sizeof(cli);
       int connectfd=accept(sockfd,(struct sockaddr*)&cli,&len);
       if(connectfd<0)
       {
           printf("cli connect failed.");
          // throw  std::exception();
       
       }
       //收到客户端请求,即添加到任务队列去
       else
       {
           CTask* ta=new CMyTask;
           ta->SetConnFd(connectfd);
           Pool.AddTask(ta);
       }
      
      
      
    }
    close(sockfd);
    return 0;

    
}



客户端:

#include<iostream>
#include<stdio.h>
#include<unistd.h>
#include<netdb.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netdb.h>
#include<string.h>

int main()
{
    int sockfd=socket(AF_INET,SOCK_STREAM,0);
    // assert(sockfd!=-1);
   
     struct sockaddr_in ser,cli;
     memset(&ser,0x00,sizeof(ser));
     ser.sin_family=AF_INET;
     inet_aton("127.0.0.1",&ser.sin_addr);
     ser.sin_port=htons(6500);

 
    int res=connect(sockfd,(struct sockaddr*)&ser,sizeof(ser));  
   //assert(res!=-1); 

    while(1)
   {
        printf("please input:");  
        fflush(stdout);  
        char buff[128]={0};  
        fgets(buff,128,stdin);
        //std::cin>>buff;
        if(strncmp(buff,"end",3)==0)  
        {  
            close(sockfd);  
            break;  
        }  
        send(sockfd,buff,strlen(buff)-1,0);  
        memset(buff,0,128);  
        recv(sockfd,buff,127,0);  
        printf("%s\n",buff); 
       
   }
   close(sockfd);
   return 0;

}

编写makefile 文件:

CC:= g++
TARGET:= test
INCLUDE:= -I./
LIBS:= -lpthread -lstdc++
# C++语言编译参数  
CXXFLAGS:= -std=c++0x -g -Wall -D_REENTRANT
# C预处理参数
# CPPFLAGS:=
OBJECTS :=Service.o Thread.o
  
$(TARGET): $(OBJECTS)
	$(CC) -o $(TARGET) $(OBJECTS) $(LIBS)
  
# $@表示所有目标集  
%.o:%.cpp   
	$(CC) -c $(CXXFLAGS) $(INCLUDE) $< -o $@
  
.PHONY : clean
clean:   
	-rm -f $(OBJECTS) $(TARGET)

注意makefile 文件 编写中的tab 键, 以及编译链接的库文件。

在linux测试,效果良好,可以同时开多个客户端。与服务端保持通讯。

猜你喜欢

转载自blog.csdn.net/qq_38506897/article/details/81135648