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
Linux下socket程式設計之多執行緒TCP伺服器
程式碼如下: thread_server.c #include<string.h> #include<stdlib.h> #include<stdio.h> #include<sys/types.h> #i
windows下socket程式設計,多執行緒
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的程式設計的一些基礎知識
linux下socket程式設計 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++伺服器(一):瞭解Linux下socket程式設計
最近想要用C++寫個socket的伺服器,用於日常的專案開發。 不過,我是新手,那就慢慢地學習一下吧。 Server #include<iostream> using namespace std; //head files of
Linux下socket程式設計之多程序TCP伺服器端
程式碼如下: tcp_server.c #include<string.h> #include<stdlib.h> #include<stdio.h> #include<sys/types.h> #includ
linux下socket程式設計實現一個伺服器連線多個客戶端
使用socekt通訊一般步驟 1)伺服器端:socker()建立套接字,繫結(bind)並
python下socket程式設計之TCP連線狀態
1. 引言 python作為一門膠水語言,可以在各個領域上作為快速開發的工具,大大提高開發者處理事務的效率。在網際網路行業中,不管是對網路伺服器的開發,還是對網路客戶端,例如爬蟲的開發中,都會涉及到底層的執行原理,那就是socket程式設計,那麼今天,我們將對python下的socke
linux下socket程式設計基礎示例
本文主要用於記錄(因為有道雲容易丟失資料),程式碼並不規範,所有的內容都解除安裝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協議是面向連線的傳輸協議,通訊雙方(通常是兩個主機上面的兩個程式)需要先建立連線,才能傳輸資料。資料在傳輸過程中會被分成多個小的資料包,這些資料包都會被新