1. 程式人生 > >c++多執行緒模式下的socket程式設計(執行緒池實現)

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)
  
# [email protected]表示所有目標集  
%.o:%.cpp   
	$(CC) -c $(CXXFLAGS) $(INCLUDE) $< -o [email protected]
  
.PHONY : clean
clean:   
	-rm -f $(OBJECTS) $(TARGET)

注意makefile 檔案 編寫中的tab 鍵, 以及編譯連結的庫檔案。

在linux測試,效果良好,可以同時開多個客戶端。與服務端保持通訊。

相關推薦

c++執行模式socket程式設計執行實現

     socket 程式設計可以說是一個基本的技術掌握,而多個客戶端向服務端傳送請求又是一個非常常見的場景,因此多執行緒模式下的socket程式設計則顯得尤為常見與重要。     本文主要利用執行緒池的技術,來實現多執行緒的模式,執行緒池的優點就不多述了,相信大家都能理

[原始碼和文件分享]基於C語言的Linux環境socket程式設計

一 需求分析 柏克萊套接字,又稱為BSD 套接字是一種應用程式介面,用於網際插座與Unix域套接字,包括了一個用C語言寫成的應用程式開發庫,主要用於實現程序間通訊,在計算機網路通訊方面被廣泛使用。 使用Berkeley套接字的系統有很多,本系統是在Ubuntu下用C語言進行socket程式設

海康監控NVR模式+web呼叫附官方開發包+手把手教你接入

最近公司的看板專案需要做一個網路攝像頭模組,客戶正在使用的是海康攝像頭,通過NVR管理。要求我們在web端實現一些功能: 編號對應圖中紅色編號,後面是文字介紹 1、 輸入網路攝像頭的IP地址

Linux socket超時connect超時/recv超時

2008-09-21 11:34 19733人閱讀 評論(1) 收藏 舉報  connect超時: 目前各平臺通用的設定socket connect超時的辦法是通過select(),具體方法如下: 1.建立socket; 2.將該socket設定為非阻塞模式; 3

Linuxsocket程式設計執行TCP伺服器

程式碼如下: thread_server.c #include<string.h> #include<stdlib.h> #include<stdio.h> #include<sys/types.h> #i

windowssocket程式設計執行

sercer端 #include <stdio.h> #include <process.h> #include <Winsock2.h> #pragma comment(lib,"ws2_32.lib") void send(void

C# Socket程式設計 通過執行方式的非同步

前言 TCPSocket通訊 Server端 建立終端(指定ip和埠號) 建立服務端socket 指定協議TCP, 資料為流方式,用IPV4, socket繫結終端 開始監聽(此時會發生阻塞,如果沒有監聽到,就不會向下執行) 接受到一個連線的請求時,會

java網路程式設計:9、基於TCP的socket程式設計伺服器端迴圈監聽接收個客戶端_執行伺服器程式

宣告:本教程不收取任何費用,歡迎轉載,尊重作者勞動成果,不得用於商業用途,侵權必究!!! 文章目錄 一、核心程式碼編寫 1、伺服器端程式的編寫 2、客戶端程式的編寫 3、測試列印輸出 二、系列文章(java網路程式設計) 上篇講了基於tcp的程式設計的一些基礎知識

linuxsocket程式設計 select實現非阻塞模式臺客戶端與伺服器通訊

select函式原型如下: int select (int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); select系統呼叫是用來讓我們的程式

Java Socket程式設計非阻塞執行,NIO

服務端:伺服器Server類public class Server implements Runnable { private int port; private volatile boolean stop; private Selector sele

VS2015 C++ main進行SOCKET執行網路通訊簡單例子聊天軟體的搭建

//伺服器 #include<iostream> #include <Winsock2.h> using namespace std; #pragma comment(lib, "ws2_32.lib") int main() { WSADAT

再說說單例模式執行靜態內部類實現

靜態內部類: package thread.singleton; public class StaticInnerClassSingleton { private static class Singleton{ private static Singleton si

socket程式設計最簡單的c/s模式

      最簡單的C/S模式是經典的入門模式,也是大多數教材講解最多的,總的來說分為下面的幾個步驟 左邊為 客戶端模式,右邊為伺服器模式 伺服器模式相對複雜一點,這一點是肯定的,因為伺服器面對很多不同的客戶端的連線。 相關函式不解釋,入門級別的書本上都有,而且講解的非常

C++伺服器:瞭解Linuxsocket程式設計

最近想要用C++寫個socket的伺服器,用於日常的專案開發。 不過,我是新手,那就慢慢地學習一下吧。 Server #include<iostream> using namespace std; //head files of

Linuxsocket程式設計程序TCP伺服器端

程式碼如下: tcp_server.c #include<string.h> #include<stdlib.h> #include<stdio.h> #include<sys/types.h> #includ

linuxsocket程式設計實現一個伺服器連線個客戶端

使用socekt通訊一般步驟     1)伺服器端:socker()建立套接字,繫結(bind)並

pythonsocket程式設計之TCP連線狀態

1. 引言 python作為一門膠水語言,可以在各個領域上作為快速開發的工具,大大提高開發者處理事務的效率。在網際網路行業中,不管是對網路伺服器的開發,還是對網路客戶端,例如爬蟲的開發中,都會涉及到底層的執行原理,那就是socket程式設計,那麼今天,我們將對python下的socke

linuxsocket程式設計基礎示例

本文主要用於記錄(因為有道雲容易丟失資料),程式碼並不規範,所有的內容都解除安裝main()函式裡面了,主要目的是為了方便自己理清流程。 服務端的程式碼: #include<unistd.h> #include<sys/types.h> #incl

socket程式設計2—— 一對通訊

1 一對多模型,TCP的程式設計步驟 服務端: 1、socket()獲得一個sockfd。注意第二個引數必須SOCK_STREAM. 2、準備通訊地址(必須伺服器的) 3、bind()繫結。(開放了埠,允許客戶端連線) 4、監聽客戶端 listen()函式 5、等待客戶端的

python3 Socket程式設計TCP&UDP

網路程式設計中最重要的兩個協議:TCP協議和UDP協議 TCP協議是網際網路中使用最廣泛的傳輸協議,這得益於其穩定,可靠的優勢。TCP協議是面向連線的傳輸協議,通訊雙方(通常是兩個主機上面的兩個程式)需要先建立連線,才能傳輸資料。資料在傳輸過程中會被分成多個小的資料包,這些資料包都會被新