色yeye在线视频观看_亚洲人亚洲精品成人网站_一级毛片免费播放_91精品一区二区中文字幕_一区二区三区日本视频_成人性生交大免费看

當前位置:首頁 > 嵌入式培訓 > 嵌入式學習 > 講師博文 > 網絡編程中的并發控制

網絡編程中的并發控制 時間:2017-11-27      來源:未知

在Linux網絡編程中,一般建立在兩端之間,服務器端和客戶端。客戶端是面向用戶的應用,而服務器端要處理客戶端所提出的請求。通常一個服務器要面向多個客戶端,保證對每個客戶端都能高效的處理,這時候需要并發操作。實現并發控制的方法有兩個,一個是并發服務器,另一個是多路復用I/O,現在就給大家介紹一下這兩種方法。

方法一:并發服務器

這個方法可以通過進程(線程)來實現,主要根據子進程(子線程)之間并行運行的特點。將對客戶端請求的處理工作,交于子進程(子線程)來處理,達到一個服務器同時處理多個客戶端的效果。通過2個例子實現一個簡單的服務器與客戶端的一對多。

例1:進程實現并發服務器(TCP通信)

首先,服務器端代碼如下:

#include<stdio.h>

#include<sys/types.h>

#include<arpa/inet.h>

#include<string.h>

#include<sys/socket.h>

#include<unistd.h>

#include<stdlib.h>

int main()

{

int sockfd, newfd, r;

struct sockaddr_in myaddr;

struct sockaddr fromaddr;

socklen_t len = 16;

char buf[100] = {0};

pid_t pid;             

sockfd = socket(AF_INET, SOCK_STREAM, 0); // 創建TCP通信的套接字--流式套接字  

myaddr.sin_family = AF_INET;         // 地址信息填寫  

myaddr.sin_port = htons(56666); // 要綁定的端口號

myaddr.sin_addr.s_addr = inet_addr("127.0.0.1");// 要綁定的地址 這里以 127.0.0.1為例

r = bind(sockfd, (struct sockaddr *)&myaddr, sizeof(myaddr));   // 綁定地址信息 

if( listen(sockfd, 10) < 0){    // 設置監聽 同一時刻能客戶端的連接請求的大數

perror("listen ");return -1;

}

while(1) {       // 循環 

newfd = accept(sockfd, &fromaddr, &len);   // 阻塞接收 客戶端的連接請求

pid = fork(); // 創建新進程

if(pid == 0){ // 子進程   處理以連接成功的客戶端

while(1){

r = recv(newfd, buf, 100, 0);     //處理客戶端  接收信息

if(r <= 0){ printf("客戶端已退出:%d \n",newfd);break; }

printf("%d : %s\n", newfd, buf);

bzero(buf, strlen(buf));

}

close(newfd);      // 關閉 連接

exit(0);    // 處理完  子進程退出

}else if(pid < 0){   exit(0);  }

}

close(sockfd);

}

客戶端代碼如下:

int main()

{

int sockfd,r;

char buf[100] = {0};

struct sockaddr_in toaddr;

sockfd = socket(AF_INET, SOCK_STREAM, 0); // 創建TCP通信的套接字--流式套接字

printf("sockfd = %d\n", sockfd);

toaddr.sin_family = AF_INET;         // 地址信息填寫  

toaddr.sin_port = htons(56666); // 對方的端口號

toaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 對方的IP地址

    // 發送連接請求 與對方建立連接

r = connect(sockfd, (struct sockaddr *)&toaddr, sizeof(toaddr)); 

    if(r == -1){ perror("connect ");  return -1;  }

printf("connect OK\n");          // 連接成功

while(1){ // 循環 向服務器端發送信息

scanf("%s", buf);

send(sockfd, buf, strlen(buf), 0);

}

close(sockfd);

}

然后,編譯服務器端 和 客戶端,終端執行如圖1命令:

圖1 編譯文件

用一個終端執行服務器,多個終端執行客戶端,結果如圖2:

左邊第一個是服務器端,先開啟;右邊2個是客戶端,同時訪問服務器;服務器能同時處理這些客戶端。

圖2 執行結果

例2:線程實現并發服務器(TCP通信)

首先,服務器端代碼如下:

……

#include<pthread.h>

void * fun(void *p)     // 線程處理函數 

{

int fd = *((int *)p);  // 獲取傳參  得到 套接字描述符 

char buf[100] = {0};

int r;

printf("pthread fd = %d start\n", fd);

while(1){ //循環 接受信息 

r = recv(fd, buf, 100, 0);

if(r <= 0){ printf("客戶端已退出 : %d\n",fd); break;  }

printf("%d : %s\n", fd, buf);

bzero(buf,strlen(buf));

}

close(fd);    // 關閉套接字 線程結束  

}

int main()

{

int sockfd, newfd, r;

struct sockaddr_in myaddr;

struct sockaddr fromaddr;

socklen_t len = 16;

char buf[100] = {0};

pthread_t tid;

sockfd = socket(AF_INET, SOCK_STREAM, 0);  // 創建TCP通信的套接字--流式套接字

myaddr.sin_family = AF_INET; // 地址信息填寫  

myaddr.sin_port = htons(56666); // 端口號

myaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // IP地址

r = bind(sockfd, (struct sockaddr *)&myaddr, sizeof(myaddr)); // 綁定

if( listen(sockfd, 10) < 0) {  perror("listen "); return -1;  } //監聽

while(1){ // 進程 循環接受客戶端的請求

newfd = accept(sockfd, &fromaddr, &len);  // 阻塞 接受  并建立連接

printf("newfd = %d\n", newfd);

pthread_create(&tid, NULL, fun, &newfd); //創建線程  將連接好的套接字傳給線程

}

    close(sockfd);

}

客戶端代碼,同例1中客戶端代碼。

編譯服務器和客戶端,終端執行命令,如圖3:注意線程編譯時加載庫。

圖3 gcc編譯

用一個終端執行服務器,多個終端執行客戶端,結果如圖4。左邊第一個是服務器端,先開啟;右邊2個是客戶端,同時訪問服務器;服務器為客戶端創建線程,同時處理這些客戶端。

圖4 執行結果

方法二:多路復用I/O

基本思想:有一個存儲文件描述符的表,有固定的函數(select)可以檢測表中的文件描述符狀態,當這些文件描述符中的一個或多個已準備好進行I/O時函數才返回。

函數返回時告訴進程那個描述符已就緒,可以進行I/O操作。

解決問題:多進程(多線程)情況下程序的復雜性較高,阻塞模式/非阻塞模式下效率低。IO多路復用是更好的方法,邏輯簡單、效率高。

IO多路復用涉及函數 :第一:select函數  功能:檢測表中文件描述符的狀態

函數原型 : #include<sys/select.h>  #include<sys/types.h> #include<unistd.h>

int select(int n, fd_set * read_fds, fd_set *write_fds, fd_set *except_dst, struct timeval *timeout);

n : 文件描述符大值+1

read_fds : 所有讀文件描述符的集合

write_fds : 寫 文件描述符集合

except_fds : 其他的 文件描述符集合

timeout :  阻塞等待的時間   毫秒 

struct timeval t = {5, 600};    &t    5.6秒

NULL/0              無限等待

struct timeval t = {0, 0};   &t    0秒  不等待

返回值 :  就緒描述符的數目

               超時返回 0

      失敗返回 -1 

第二:文件描述符操作函數(宏定義) 

void FD_SET(int fd, fd_set *fds);   將文件描述符 添加到 表中

void FD_CLR(int fd, fd_set *fds);   刪除 一個文件描述符 

void FD_ZERO(fd_set *fds);       清零

int FD_ISSET(int fd, fd_set *fds);      判斷 fd  是否已經準備I/O

服務器端可以采用多路IO復用實現一對多處理,代碼如下:

……

#include<sys/select.h>

int main()

{

int sockfd, newfd, r, i, maxfd;

struct sockaddr_in myaddr;

struct sockaddr fromaddr;

socklen_t len = 16;

char buf[100] = {0};

fd_set fds;

sockfd = socket(AF_INET, SOCK_STREAM, 0);// 創建TCP通信的套接字--流式套接字

myaddr.sin_family = AF_INET; // 地址信息填寫  

myaddr.sin_port = htons(56667); // 端口號

myaddr.sin_addr.s_addr = inet_addr("127.0.0.1");// IP地址

r = bind(sockfd, (struct sockaddr *)&myaddr, sizeof(myaddr)); // 綁定

if( listen(sockfd, 10) < 0){  //監聽

  perror("listen "); return -1;  }

FD_ZERO(&fds);      // 清空表

FD_SET(sockfd, &fds);  // 添加 套接字描述符 到表中

maxfd = sockfd;   // 記錄 描述符 的 大值

while(1){

   // 阻塞 等待是否 有訪問到來

r = select(maxfd+1, &fds, NULL, NULL, NULL);  

if(r <=0){ return -1;  }

for(i = 0; i <= maxfd;i++){

if(FD_ISSET(i, &fds)) {  //找出 I/O操作的套接字描述符

if(i == sockfd){     //  客戶端 發送 連接請求

newfd = accept(i, &fromaddr, &len);  // 接受 并建立連接

printf("newfd = %d  start\n", newfd);

FD_SET(newfd, &fds); // 將新套接字描述符 添加到表中

maxfd = maxfd > newfd  ? maxfd : newfd;  // 更新 大值

else{       // 客戶端 接收/發送 信息 

r = recv(i, buf, 100, 0);

if(r <= 0){

   close(i);

FD_CLR(i, &fds); // 從表中刪除該套接字

}else {

send(i, buf, strlen(buf), 0);

printf("%d : %s\n", i, buf);

bzero(buf, strlen(buf));

}

}}}}}

客戶端代碼如下:

int main()

{

int sockfd,r;

char buf[100] = {0};

struct sockaddr_in toaddr;

sockfd = socket(AF_INET, SOCK_STREAM, 0); // 創建TCP通信的套接字--流式套接字

printf("sockfd = %d\n", sockfd);

toaddr.sin_family = AF_INET;         // 地址信息填寫  

toaddr.sin_port = htons(56667); // 對方的端口號

toaddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // 對方的IP地址

    // 發送連接請求 與對方建立連接

r = connect(sockfd, (struct sockaddr *)&toaddr, sizeof(toaddr));

if(r == -1){ perror("connect ");  return -1; }

printf("connect OK\n");          // 連接成功

    scanf("%s", buf);

send(sockfd, buf, strlen(buf), 0); //向服務器端發送信息

bzero(buf, strlen(buf));

recv(sockfd, buf, 100, 0);    //收取對方的回發信息

printf("recv : %s\n", buf);

close(sockfd);

}

然后,gcc編譯服務器端和客戶端,分別生成可執行文件,在不同終端執行(左邊第一個為服務器端,之后的是客戶端),執行后結果如圖5所示:

圖5 執行結果圖

在多路復用I/O中例子中,服務器端用的是for循環依次遍歷描述符表,所以造成后面客戶端的等待問題。

以上就是在網絡編程中常用的并發操作,希望可以為你提供一定的幫助。

上一篇:U-BOOT 使用串口運行4412裸板程序

下一篇:FS4412中uboot對emmc的分區解析

熱點文章推薦
華清學員就業榜單
高薪學員經驗分享
熱點新聞推薦
前臺專線:010-82525158 企業培訓洽談專線:010-82525379 院校合作洽談專線:010-82525379 Copyright © 2004-2022 北京華清遠見科技集團有限公司 版權所有 ,京ICP備16055225號-5京公海網安備11010802025203號

回到頂部

主站蜘蛛池模板: 四虎永久在线精品免费播放 | 国产成人AV大片在线观看 | 男人的天堂视频网站 | 加勒比中文无码久久综合色 | 色噜噜狠狠一区二区三区果冻 | 国产人妻精品一区二区三区 | 色情无码www视频无码区小黄鸭 | 丰满岳妇乱一区二区三区 | 老头霸占人妻中文字幕 | 欧美高清久久 | 狠狠躁夜夜躁人人爽天天不卡软件 | 国产欧美精品免费观看久 | 无码毛片视频一区二区三区 | 亚洲AV无码乱码国产精品久久 | 我被黑人一夜P了10次 | 欧洲精品视频在线 | 麻豆媒体| 脱了美女内裤猛烈啪啪爽 | 亚洲综合另类小说色区色噜噜 | 日韩成全视频观看免费观看高清 | 伊人久久综合热线大杳蕉 | 色综合久久成人综合网五月 | 日本内射精品一区二区视频 | 黄瓜视频在线观看网址 | 国模无码视频一区二区三区 | 欧美三级a做爰在线观看 | 一区二区美女视频 | 超级色观看在线 | 国产艳情熟女视频 | 日日鲁鲁鲁夜夜爽爽狠狠 | 黑人太大太长疼死我了 | 国产在线精品国自产拍影院同性 | 亚洲精品成人网线在线播放va | 高清国产一区二区三区在线 | 久久久人人人婷婷色东京热 | 日韩欧美在线看 | 日本护士吞精囗交gif | 乌克兰18极品XX00喷水 | 亚洲欧洲综合有码无码 | 美女内射毛片在线看免费人动物 | 亚洲国产精品无码久久久蜜芽 |