日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

socket网络通信实现与优化

發布時間:2023/12/16 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 socket网络通信实现与优化 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

多進程通信

進程通信方式

  • 管道:
  • 消息隊列
  • 信號
  • 共享內存
  • 信號量
  • 套接字socket
  • 多進程通信的優化

  • 將socket通信的客戶端以及服務端進行封裝,讓代碼更清晰高效
  • 解決了僵尸進程的問題,防止子進程占用過多的內存資源
  • 關閉多余的socket,防止系統打開的文件描述符過多:ulimit -a
  • 程序退出資源的釋放,利用exit(0)退出程序,調用析構函數,釋放資源
  • 多進程服務進程的運行日志,多進程日志文件不能切換。一般分開寫日志
  • gdb對多進程進行調試。
  • 增加業務代碼:實現登錄用戶與密碼驗證
  • 服務端程序

    #include "../_freecplus.h"CTcpServer TcpServer; // 創建服務端對象。 CLogFile logfile; // 程序退出時調用的函數void FathEXIT(int sig);void ChldEXIT(int sig); // 父進程退出時調用的函數void FathEXIT(int sig){if(sig>0){signal(sig,SIG_IGN);signal(SIGINT,SIG_IGN);signal(SIGTERM,SIG_IGN);printf("catch the signal %d\n",sig);}kill(0,15);//父進程退出,子進程也全部退出printf("父進程退出\n");exit(0);}void ChldEXIT(int sig) {if(sig>0){signal(sig,SIG_IGN);signal(SIGINT,SIG_IGN);signal(SIGTERM,SIG_IGN);}printf("子進程退出\n");exit(0); }int main(int argc,char *argv[]) {//關閉全部的信號logfile.Open("/tmp/log/demo1.log","a+");for(int i=0;i<100;i++){signal(i,SIG_IGN);}//設置信號,在shell下可用kill+進程號正常終止進程signal(SIGINT,FathEXIT);signal(SIGTERM,FathEXIT);signal(SIGCHLD,SIG_IGN);//忽略子進程退出的信號,避免產生僵尸進程if (TcpServer.InitServer(5005)==false) // 初始化TcpServer的通信端口。{// printf("TcpServer.InitServer(5005) failed.\n"); return -1;logfile.Write("TcpServer.InitServer(5005) failed.\n");return -1;} while(true) {if (TcpServer.Accept()==false) // 等待客戶端連接。{printf("TcpServer.Accept() failed.\n"); return -1;}//printf("客戶端(%s)已連接。\n",TcpServer.GetIP());logfile.Write("客戶端(%s)已經連接。\n",TcpServer.GetIP()); if(fork()>0) {TcpServer.CloseClient();//父進程關閉多余的客戶端連接上來的socketcontinue; }TcpServer.CloseListen();//子進程關閉監聽的socket char strbuffer[1024]; // 存放數據的緩沖區。signal(SIGINT,ChldEXIT);signal(SIGTERM,ChldEXIT);while (true){memset(strbuffer,0,sizeof(strbuffer));if (TcpServer.Read(strbuffer,300)==false) break; // 接收客戶端發過來的請求報文。//printf("接收:%s\n",strbuffer);logfile.Write("接收:%s\n",strbuffer);strcat(strbuffer,"ok"); // 在客戶端的報文后加上"ok"。//printf("發送:%s\n",strbuffer);logfile.Write("發送:%s\n",strbuffer);if (TcpServer.Write(strbuffer)==false) break; // 向客戶端回應報文。}//printf("客戶端已斷開。\n"); // 程序直接退出,析構函數會釋放資源。logfile.Write("客戶端已經斷開。\n");exit(0); } }

    客戶端程序

    #include "../_freecplus.h"int main(int argc,char *argv[]) {CTcpClient TcpClient; // 創建客戶端的對象。if (TcpClient.ConnectToServer("192.168.74.10",5005)==false) // 向服務端發起連接請求。{printf("TcpClient.ConnectToServer(\"172.16.0.15\",5858) failed.\n"); return -1;}char strbuffer[1024]; // 存放數據的緩沖區。printf("pid=%d\n",getpid());for (int ii=0;ii<20;ii++) // 利用循環,與服務端進行5次交互。{memset(strbuffer,0,sizeof(strbuffer));snprintf(strbuffer,50,"pid=%d:這是第%d個超級女生,編號%03d。",getpid(),ii+1,ii+1);printf("發送:%s\n",strbuffer);if (TcpClient.Write(strbuffer)==false) break; // 向服務端發送請求報文。memset(strbuffer,0,sizeof(strbuffer));if (TcpClient.Read(strbuffer,20)==false) break; // 接收服務端的回應報文。printf("接收:%s\n",strbuffer);sleep(1);}// 程序直接退出,析構函數會釋放資源。 }

    運行窗口

  • 服務端后臺運行
  • 客戶端發起連接
  • 查看服務端運行的進程,無僵尸進程的產生
  • 查看服務端的打印日志,日志正常打印輸出
  • 殺死父進程

  • 業務代碼
    服務端業務主體代碼
  • while (true){memset(strrecvbuffer,0,sizeof(strrecvbuffer));memset(strsendbuffer,0,sizeof(strsendbuffer));if (TcpServer.Read(strrecvbuffer,30)==false) break; // 接收客戶端發過來的請求報文。logfile.Write("接收:%s\n",strrecvbuffer);// 處理業務的主函數。if (_main(strrecvbuffer,strsendbuffer)==false) ChldEXIT(-1);logfile.Write("發送:%s\n",strsendbuffer);if (TcpServer.Write(strsendbuffer)==false) break; // 向客戶端回應報文。}bool _main(const char *strrecvbuffer,char *strsendbuffer) // 處理業務的主函數。 {int ibizcode=-1;GetXMLBuffer(strrecvbuffer,"bizcode",&ibizcode);switch (ibizcode){case 0: // 心跳biz000(strrecvbuffer,strsendbuffer); break;case 1: // 身份驗證。biz001(strrecvbuffer,strsendbuffer); break;case 2: // 查詢余額。biz002(strrecvbuffer,strsendbuffer); break;default:logfile.Write("非法報文:%s\n",strrecvbuffer); return false;}return true; }// 身份驗證業務處理函數。 bool biz001(const char *strrecvbuffer,char *strsendbuffer) {char username[51],password[51];memset(username,0,sizeof(username));memset(password,0,sizeof(password));GetXMLBuffer(strrecvbuffer,"username",username,50);GetXMLBuffer(strrecvbuffer,"password",password,50);if ( (strcmp(username,"wwc")==0) && (strcmp(password,"19960207")==0) )sprintf(strsendbuffer,"<retcode>0</retcode><message>成功。</message>");elsesprintf(strsendbuffer,"<retcode>-1</retcode><message>用戶名或密碼不正確。</message>");return true; }

    客戶端主體代碼

    while(1){if(biz001()==false);break;} // 身份驗證。 bool biz001() {char strbuffer[1024]; // 存放數據的緩沖區。memset(strbuffer,0,sizeof(strbuffer)); char name[10]; memset(name,0,sizeof(name)); char password[10]; memset(password,0,sizeof(password)); printf("請輸入用戶名:"); scanf("%s",name); printf("請輸入密碼:"); scanf("%s",password);snprintf(strbuffer,1000,"<bizcode>1</bizcode><username>%s</username><password>%s</password>",name,password);printf("發送:%s\n",strbuffer);if (TcpClient.Write(strbuffer)==false) return false; // 向服務端發送請求報文。memset(strbuffer,0,sizeof(strbuffer));if (TcpClient.Read(strbuffer,20)==false) return false; // 接收服務端的回應報文。printf("接收:%s\n",strbuffer);int iretcode=-1;GetXMLBuffer(strbuffer,"retcode",&iretcode);if (iretcode==0) { printf("身份驗證成功。\n"); return true; }printf("身份驗證失敗。\n");return false; }

    多線程通信

    多線程服務程序退出

    思路

  • 在主進程中設置程序退出的信號2 和15的信號處理函數
  • 在信號處理函數中取消主進程的監聽,設置全部的線程取消
  • 在線程處理函數中首先注冊線程清理函數,將線程設置為分離,將取消類型設置為(立即取消、延遲取消),并且最后執行線程清理函數(注冊清理函數和執行函數成對出現)
  • 編寫清理函數,將線程連接的socket關閉

    需要注意的點:收到2,15信號,執行信號函數取消同樣會調用線程清理函數,我這里usleep(200),即可發現這個問題
  • 服務端代碼

    #include "../_freecplus.h" void* pthmain(void* arg); void mainexit(int sig); vector<pthread_t>vpthid; CTcpServer TcpServer; // 創建服務端對象。 int main(int argc,char *argv[]) { signal(2,mainexit);signal(5,mainexit); if (TcpServer.InitServer(5005)==false) // 初始化TcpServer的通信端口。{ printf("TcpServer.InitServer(5858) failed.\n"); return -1;}while(true){ if (TcpServer.Accept()==false) // 等待客戶端連接。{ printf("TcpServer.Accept() failed.\n"); return -1;}printf("客戶端(%s)已連接。\n",TcpServer.GetIP());pthread_t pthid1;if(pthread_create(&pthid1,NULL,pthmain,(void*)(long)TcpServer.m_connfd)!=0){printf("creat pthid1 fail\n");return -1;}vpthid.push_back(pthid1);}return 0; } //2,15信號處理函數 void mainexit(int sig) {printf("mainexit begin.\n");TcpServer.CloseListen();//關閉監聽的socketfor(int i=0;i<vpthid.size();i++){printf("cancel %ld\n",vpthid[i]);pthread_cancel(vpthid[i]);//取消全部的線程}printf("mainexit end.\n");usleep(20);exit(0); } //設置線程清理函數 void pthmainexit(void* arg) {printf("線程清理函數開始。\n");close((int)(long)arg);//關閉線程連接的socket for(int i=0;i<vpthid.size();i++){if(vpthid[i]==pthread_self()){vpthid.erase(vpthid.begin()+i);}}printf("線程清理函數結束.\n"); } void* pthmain(void* arg) {pthread_cleanup_push(pthmainexit,arg);//注冊線程清理函數pthread_detach(pthread_self());pthread_setcanceltype(PTHREAD_CANCEL_DISABLE,NULL);int sockfd=(int)(long)(arg);while (true){char strbuffer[1024]; // 存放數據的緩沖區。int buflen=0;memset(strbuffer,0,sizeof(strbuffer));if (TcpRead(sockfd,strbuffer,&buflen,300)==false) break; // 接收客戶端發過來的請求報文。printf("接收:%s\n",strbuffer);strcat(strbuffer,"ok"); // 在客戶端的報文后加上"ok"。printf("發送:%s\n",strbuffer);if (TcpWrite(sockfd,strbuffer,buflen)==false) break; // 向客戶端回應報文。}printf("客戶端已斷開。\n");pthread_cleanup_pop(1);//執行線程清理函數 }

    運行窗口

    性能測試

    服務端并發量測試

    • 實驗環境:兩臺獨立的虛擬機,客戶端給服務端只發送心跳報文,測試1000臺客戶端連接服務端后,服務端均為多進程,查看服務端內存和cpu的變化情況
      實驗前
      cpu的運行情況:

      內存的消耗情況:

      實驗后
      服務端運行的進程數

      cpu的運行情況:

      內存的消耗情況:

    服務端業務性能測試

    • 實驗環境:兩臺獨立的虛擬機,客戶端給服務端只發送心跳報文,測試100臺客戶端同時連接服務端后,服務端均為多進程,查看服務端cpu的變化情況,以及每秒接受的信息量。
      == 客戶端業務==
    for (int ii=0;ii<2000;ii++){if (biz000()==false) break;usleep(10000);} bool biz000() // 發送心跳報文。 {char strbuffer[1024]; // 存放數據的緩沖區。memset(strbuffer,0,sizeof(strbuffer));snprintf(strbuffer,1000,"<bizcode>0</bizcode>");//printf("發送:%s\n",strbuffer);if (TcpClient.Write(strbuffer)==false) return false; // 向服務端發送請求報文。memset(strbuffer,0,sizeof(strbuffer));if (TcpClient.Read(strbuffer,20)==false) return false; // 接收服務端的回應報文。//printf("接收:%s\n",strbuffer);return true; }

    服務端消耗內存
    之前:

    之后:

    結論:消耗內存大概為16M
    服務端cpu情況:

    服務端每秒接收的數據量:

    多進程/多線程服務端性能

    多線程:
    內存消耗對比:
    之前:

    之后:

    cpu消耗情況:

    多線程1s處理報文的數量:

    結論:1.連接相同客戶端,處理相同的業務,多線程消耗內存為7M,多進程為16M
    2.多進程cpu剩余30%,多進程剩余20%
    3.多進程和多線程1s處理的報文數基本相同,都為8000多。

    客戶端業務響應時間

    環境:兩臺centos7局域網上客戶端在不同階段的測試響應時間
    1.客戶端
    2.客戶端給服務端發送心跳報文,此時服務端閑時,計算從發送到接受的耗時

    3.客戶端給服務端發送心跳報文,此時服務端忙時,計算從發送到接受的耗時

    總結

    以上是生活随笔為你收集整理的socket网络通信实现与优化的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。