色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

并發服務器的設計與實現

CHANBAEK ? 來源:嵌入式攻城獅 ? 作者:嵌入式攻城獅 ? 2023-04-25 15:35 ? 次閱讀

并發服務器

1.基于多線程的并發服務器

并發服務器支持多個客戶端的連接,最大可接入的客戶端數取決于內核控制塊的個數。 當使用Socket API時,要使服務器能夠同時支持多個客戶端的連接,必須引入多任務機制,為每個連接創建一個單獨的任務來處理連接上的數據,我們將這個設計方式稱作并發服務器的設計。

由于多線程并發服務器涉及到子任務的動態創建和銷毀,用戶需要自己完成對任務堆棧的管理和回收,因此并發服務器的設計流程也相對復雜。

以下并發服務器實例完成的功能為:服務器能夠同時支持多個客戶端的連接,并能夠將每個連接上接收到的小寫字母轉換成大寫字母回顯到客戶端,其實現步驟如下

參考Socket API編程優化一文,在該文的工程源碼基礎上進行修改

在工程中創建socket_thread_server.c和對應的頭文件

/******socket_thread_server.c******/
#include "socket_tcp_server.h"
#include "socket_wrap.h"
#include "FreeRTOS.h"
#include "task.h"
#include "cmsis_os.h"
#include "ctype.h"

static char ReadBuff[BUFF_SIZE];
/**
  * @brief  Function implementing the defaultTask thread.
  * @param  argument: Not used 
  * @retval None
  */
/* USER CODE END Header_StartDefaultTask */
void vNewClientTask(void const * argument){
  // 每一個任務,都有獨立的棧空間
  int cfd = * (int *)argument;
  int n, i;
  while(1){
    //等待客戶端發送數據
    n = Read(cfd, ReadBuff, BUFF_SIZE);
    if(n <= 0){
      close(cfd);
      vTaskDelete(NULL);
    }
    //進行大小寫轉換
    for(i = 0; i < n; i++){	
      ReadBuff[i] = toupper(ReadBuff[i]);		
    }
    //寫回客戶端
    n = Write(cfd, ReadBuff, n);
    if(n < 0){
      close(cfd);
      vTaskDelete(NULL);			
    }
  }
}
/**
  * @brief  多線程服務器
  * @param  none
  * @retval none
  */
void vThreadServerTask(void){
  int sfd, cfd;
  struct sockaddr_in server_addr, client_addr;
  socklen_t	client_addr_len;
  //創建socket
  sfd = Socket(AF_INET, SOCK_STREAM, 0);
  server_addr.sin_family = AF_INET;
  server_addr.sin_port   = htons(SERVER_PORT);
  server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  //綁定socket
  Bind(sfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
  //監聽socket
  Listen(sfd, 5);
  //等待客戶端連接
  client_addr_len = sizeof(client_addr);
  while(1){
    /*每創建一個socket,lwip都會分配一片內存空間
    宏NUM_SOCKETS就定義了一共支持多少個socket,即能分配多少fd
    #define NUM_SOCKETS		MEMP_NUM_NETCONN
    #define MEMP_NUM_NETCONN	8		
    */
    cfd = Accept(sfd,(struct sockaddr *)&client_addr, &client_addr_len);
    printf("client is connect cfd = %d\\r\\n",cfd);
    if(xTaskCreate((TaskFunction_t) vNewClientTask,
		   "Client",
		   128,//1k
		   (void *)&cfd,
		   osPriorityNormal,
		   NULL) != pdPASS){	
      printf("create task fail!\\r\\n");		
    }
  }									
}

在freertos.c文件中的默認任務里面添加代碼

void StartDefaultTask(void const * argument){
  /* init code for LWIP */
  MX_LWIP_Init();
  /* USER CODE BEGIN StartDefaultTask */
  printf("TCP thread server started!\\r\\n",cfd);
  /* Infinite loop */
  for(;;){
    vThreadServerTask();
    osDelay(100);
  }
  /* USER CODE END StartDefaultTask */
}

編譯無誤下載到開發板后,打開串口助手可以看到相關調試信息,使用網絡調試工具可以創建多個PC客戶端(串口會返回對應的cfd),輸入任意小寫字母,Server將返回對應的大寫字母

圖片

圖片

2.基于Select的并發服務器

基于多線程的socket并發服務器,必須使用多線程的方式來實現,即為每個連接創建一個單獨的任務來處理數據。 但是,這種多線程的方式是有缺陷的,在大型服務器的設計中,一個服務器上可能存在成千上萬條連接,如果為每個連接都創建一個線程,這對系統資源來說無疑是比巨大的開銷,也是種不太現實的做法。 事實上,在socket編程中,通常使用一種叫做Select的機制來實現并發服務器的設計。

Select函數實現的基本思想為:先構造一張有關描述符的表,然后調用一個函數。 當這些文件描述符中的一個或多個已準備好進行I/O時函數才返回; 函數返回時告訴進程哪個描述符已就緒,可以進行I/O操作

/*****select()函數*****/
函數原型:int select(int maxfd, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
傳 入 值:maxfd 監控的文件描述符集里最大文件描述符加1
	readfds 監控有讀數據到達文件描述符集合,傳入傳出參數
	writefds 監控有寫數據到達文件描述符集合,傳入傳出參數
	exceptfds 監控異常發生達文件描述符集合,傳入傳出參數
	timeout 超時設置 
	-->NULL:一直阻塞,直到有文件描述符就緒或出錯
	-->0:僅僅檢測文件描述符集的狀態,然后立即返回,輪詢
	-->不為0:在指定時間內,如果沒有事件發生,則超時返回
返 回 值:成功:所監聽的所有監聽集合中,滿足條件的總數!
	失敗:0 超時
	錯誤:-1
//timeval結構體
struct timeval {
    long tv_sec; /* seconds */
    long tv_usec; /* microseconds */
};

調用 select() 函數時進程會一直阻塞直到有文件可讀、有文件可寫或者超時時間到。 為了設置文件描述符需要使用幾個宏:

  • select能監聽的文件描述符個數受限于FD_SETSIZE,一般為1024,單純改變進程打開的文件描述符個數并不能改變select監聽文件個數
  • 解決1024以下客戶端時使用select是很合適的,但如果鏈接客戶端過多,select采用的是輪詢模型,會大大降低服務器響應效率,不應在select上投入更多精力
#include 
int FD_ZERO(fd_set *fdset);		//從fdset中清除所有的文件描述符
int FD_CLR(int fd,fd_set *fdset);	//將fd從fdset中清除
int FD_SET(int fd,fd_set *fdset);	//將fd加入到fdset
int FD_ISSET(int fd,fd_set *fdset);	//判斷fd是否在fdset集合中
/*例如*/
fd_set rset;
int fd;
FD_ZERO(&rset);
FD_SET(fd,&rset);
FD_SET(stdin,&rset);
//在select返回之后,可以使用FD_ISSET(fd,&rset)測試給定的位置是否置位。
if(FD_ISSET(fd,&rset))
{......}

select編程模型如下圖示

圖片

以下并發服務器實例完成的功能為:服務器能夠同時支持多個客戶端的連接,并能夠將每個連接上接收到的小寫字母轉換成大寫字母回顯到客戶端,其實現步驟如下:

參考Socket API編程優化一文,在該文的工程源碼基礎上進行修改

在工程中創建socket_socket_server.c和對應的頭文件

#include "socket_wrap.h"
#include "socket_select_server.h"
#include "socket_tcp_server.h"
#include "string.h"
#include "FreeRTOS.h"
#include "task.h"
#include "ctype.h"

static char ReadBuff[BUFF_SIZE];
/**
  * @brief  select 并發服務器
  * @param  none
  * @retval none
  */
void vSelectServerTask(void){
  int sfd, cfd, maxfd, i, nready, n, j;
  struct sockaddr_in server_addr, client_addr;
  socklen_t	client_addr_len;
  fd_set all_set, read_set;
  //FD_SETSIZE里面包含了服務器的fd
  int clientfds[FD_SETSIZE - 1];	
  //創建socket
  sfd = Socket(AF_INET, SOCK_STREAM, 0);
  server_addr.sin_family = AF_INET;
  server_addr.sin_port = htons(SERVER_PORT);
  server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  //綁定socket
  Bind(sfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
  //監聽socket
  Listen(sfd, 5);	
  client_addr_len = sizeof(client_addr);
  //初始化 maxfd 等于 sfd
  maxfd = sfd;	
  //清空fdset
  FD_ZERO(&all_set);	
  //把sfd文件描述符添加到集合中	
  FD_SET(sfd, &all_set);
  //初始化客戶端fd的集合
  for(i = 0; i < FD_SETSIZE -1 ; i++){
    //初始化為-1
    clientfds[i] = -1;
  }
  while(1){
    //每次select返回之后,fd_set集合就會變化,再select時,就不能使用,
    //所以我們要保存設置fd_set 和 讀取的fd_set
    read_set = all_set;
    nready = select(maxfd + 1, &read_set, NULL, NULL, NULL);
    //沒有超時機制,不會返回0
    if(nready < 0){
      printf("select error \\r\\n");
      vTaskDelete(NULL);
    }
    //判斷監聽的套接字是否有數據
    if(FD_ISSET(sfd, &read_set)){	
      //有客戶端進行連接了
      cfd = accept(sfd, (struct sockaddr *)&client_addr, &client_addr_len);
      if(cfd < 0){
        printf("accept socket error\\r\\n");
        //繼續select
        continue;
      }
      printf("new client connect fd = %d\\r\\n", cfd);
      //把新的cfd 添加到fd_set集合中
      FD_SET(cfd, &all_set);
      //更新要select的maxfd
      maxfd = (cfd > maxfd)?cfd:maxfd;
      //把新的cfd 保存到cfds集合中
      for(i = 0; i < FD_SETSIZE -1 ; i++){
        if(clientfds[i] == -1){
          clientfds[i] = cfd;
          //退出,不需要添加
          break;		
        }
      }
      //沒有其他套接字需要處理:這里防止重復工作,就不去執行其他任務
      if(--nready == 0){
        //繼續select
        continue;
      }	
    }
    //遍歷所有的客戶端文件描述符
    for(i = 0; i < FD_SETSIZE -1 ; i++){
      if(clientfds[i] == -1){
        //繼續遍歷
        continue;
      }
      //是否在我們fd_set集合里面
      if(FD_ISSET(clientfds[i], &read_set)){
        n = Read(clientfds[i], ReadBuff, BUFF_SIZE);
        //Read函數已經關閉了這個客戶端的fd
        if(n <= 0){
          //從集合里面清除
          FD_CLR(clientfds[i], &all_set);
          //當前的客戶端fd 賦值為-1
          clientfds[i] = -1;
        }else{
          //進行大小寫轉換
          for(j = 0; j < n; j++){		
            ReadBuff[j] = toupper(ReadBuff[j]);		
          }
          //寫回客戶端
          n = Write(clientfds[i], ReadBuff, n);
          if(n < 0){
            //從集合里面清除
            FD_CLR(clientfds[i], &all_set);
            //當前的客戶端fd 賦值為-1
            clientfds[i] = -1;		
          }				
        }
      }
    }		
  }
}

在freertos.c文件中的默認任務里面添加代碼

void StartDefaultTask(void const * argument){
  /* init code for LWIP */
  MX_LWIP_Init();
  /* USER CODE BEGIN StartDefaultTask */
  printf("TCP thread server started!\\r\\n",cfd);
  /* Infinite loop */
  for(;;){
    vSocketServerTask();
    osDelay(100);
  }
  /* USER CODE END StartDefaultTask */
}

編譯無誤下載到開發板后,打開串口助手可以看到相關調試信息,使用網絡調試工具可以創建多個PC客戶端(串口會返回對應的cfd),輸入任意小寫字母,Server將返回對應的大寫字母

圖片

圖片

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 服務器
    +關注

    關注

    12

    文章

    9123

    瀏覽量

    85324
  • API
    API
    +關注

    關注

    2

    文章

    1499

    瀏覽量

    61964
  • 調試
    +關注

    關注

    7

    文章

    578

    瀏覽量

    33923
  • 編程
    +關注

    關注

    88

    文章

    3614

    瀏覽量

    93686
  • 多線程
    +關注

    關注

    0

    文章

    278

    瀏覽量

    19943
收藏 人收藏

    評論

    相關推薦

    我國首款億級并發服務器系統實現量產

    我國高性能計算領軍企業中科曙光29日在天津宣布,曙光星河云服務器系統正式量產。這是我國首款億級并發服務器系統,也是“十二五”期間國家863計劃信息技術領域“億級并發
    發表于 11-30 15:47 ?838次閱讀

    基于Select/Poll實現并發服務器(一)

    LWIP:2.0.2 ? 并發服務器支持多個客戶端的同時連接,最大可接入的客戶端數取決于內核控制塊的個數。當使用Socket API時,要使服務器能夠同時支持多個客戶端的連接,必須引入多任務機制,為每個
    的頭像 發表于 06-20 00:20 ?3808次閱讀
    基于Select/Poll<b class='flag-5'>實現</b><b class='flag-5'>并發</b><b class='flag-5'>服務器</b>(一)

    嵌入式Linux系統開發學習路線

    方法和并發服務器實現,了解HTTP協議及其實現方法,熟悉UDP廣播、多播的原理及編程方法,掌握混合CS架構網絡通信系統的設計,熟悉HTML,Javascript等Web編程技術及
    發表于 09-21 10:09

    Linux基礎

    TCP協議服務器的編程方法和并發服務器實現,了解HTTP協議及其實現方法,熟悉UDP廣播、多播的原理及編程方法,掌握混合C/S架構網絡通信
    發表于 08-03 09:46

    在DragonBoard 410c上實現并發處理TCP服務器

    服務,讓傳感和相關的控制設備接入,為此,本期blog將向大家介紹如何使用gevent高性能的并發處理庫在draognbaord 410c上來實現一個高性能的TCP
    發表于 09-25 15:53

    嵌入式FTP服務器實現什么功能?

    FTP服務是目前廣泛應用的因特網應用服務之一,為了在國產嵌入式實時操作系統平臺上開發FTP服務,采用多線程并發服務器的體系結構設計了一種嵌入
    發表于 03-11 08:27

    高性能高并發服務器架構分享

    由于自己正在做一個高性能大用戶量的論壇程序,對高性能高并發服務器架構比較感興趣,于是在網上收集了不少這方面的資料和大家分享。希望能和大家交流 msn: ——————————————————————————————————————— ? 初創網站與開源軟件 6 ? 談談大型
    發表于 09-16 06:45

    如何利用多線程去構建一種TCP并發服務器

    TCP并發服務器,并實現客戶端和服務器的傳輸(多個并發用戶同時訪問服務器)實驗原理:TCP的傳輸
    發表于 12-22 08:03

    【沁恒微CH32V307評估板試用體驗】基于LWIP實現并發服務器

    程,這是最常用的并發服務器設計。但是多線程/多進程消耗資源多,處理起來也比較復雜,本文將基于LWIP協議棧的Select/Poll機制實現并發服務器
    發表于 06-01 23:27

    Linux環境并發服務器設計技術研究

    講述并發服務器設計的主要技術,包括多進程服務器、多線程服務器和I/ O 復用服務器,同時對以上服務器
    發表于 04-24 10:02 ?16次下載

    阿里云2核4G服務器租賃的并發怎樣算

    阿里云2核4G服務器租賃的并發怎樣算?我們要知道我們租用的服務器能支持多少人同時訪問,并發數是一個很重要的參考值。很多人不了解服務器
    的頭像 發表于 07-07 17:19 ?1977次閱讀

    單臺服務器支持的TCP并發連接數

    總之,65535只是Linux系統中可使用端口port數量的上限,端口port數量與TCP連接數量并非完全一一對應的關系,服務器支持的TCP并發連接數量主要跟服務器的內存以及允許單個進程同時打開
    的頭像 發表于 11-06 19:36 ?1603次閱讀

    服務器的高并發能力如何提升?

    服務器的高并發能力如何提升? 服務器并發能力體現著服務器在單位時間內的很強數據處理能力,一般來說,如果企業的互聯網業務需要面對大量的同時在
    的頭像 發表于 03-17 17:07 ?1014次閱讀

    網站服務器并發數的計算方法是什么?

    并發數也就是指同時訪問服務器站點的連接數,所以站長為了后期避免主機服務器等資源出現過剩浪費及資源不足等問題的出現,都會對服務器并發數進行計
    的頭像 發表于 04-12 15:22 ?3231次閱讀

    服務器并發的概念

    自己調整系統的相關參數 并發的概念是什么?什么是并發? 對于服務器并發的概念,下面幾點是錯誤的定義 ①服務器處理客戶端請求的數量:沒有時間、
    的頭像 發表于 11-10 10:05 ?5032次閱讀
    <b class='flag-5'>服務器</b><b class='flag-5'>并發</b>的概念
    主站蜘蛛池模板: 嗯呐啊唔高H兽交| 老板吻我下身好爽到高潮| 国产一区日韩二区欧美三区| 国产亚洲精品久久综合阿香 | cctv论坛| 丰满的大白屁股ass| 国产精品午夜福利在线观看| 后入内射国产一区二区| 狼与美女谐音歌词| 秋霞电影网伦大理电影在线观看| 天堂岛www| 一级无毛片| 爱豆剧果冻传媒在线播放| 国产精品久久久久永久免费看| 激情综合色| 欧美jizz19性欧美| 午夜国产高清精品一区免费| 伊人久久综合| 成人特级毛片| 寂寞夜晚视频高清观看免费| 暖暖免费 高清 日本社区中文| 色欲国产麻豆一精品一AV一免费| 一本道本线中文无码| www.av一区| 国拍在线精品视频免费观看| 免费看亚洲| 亚洲成 人a影院青久在线观看| 精品国产乱码久久久久久口爆| 毛片大全网站| 天天操夜夜噜| 69夫妇交友群| 国产午夜人做人免费视频中文| 伦理片97影视网| 午夜影院一区二区三区| 94色94色永久网站| 国产树林野战在线播放| 暖暖视频大全免费观看| 亚洲国产在线综合018| YIN荡的老师系列第6部分视频| 狠狠色丁香婷婷久久综合| 日本边添边摸边做边爱边|