久久婷婷香蕉热狠狠综合,精品无码国产自产拍在线观看蜜,寡妇房东在做爰3,中文字幕日本人妻久久久免费,国产成人精品三上悠亚久久

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

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

在(zai)(zai)Linux網絡編程中,一般(ban)建立在(zai)(zai)兩(liang)端之間(jian),服(fu)務器端和客(ke)戶(hu)端。客(ke)戶(hu)端是面向用戶(hu)的(de)(de)應用,而服(fu)務器端要(yao)處理客(ke)戶(hu)端所提出的(de)(de)請求。通常(chang)一個服(fu)務器要(yao)面向多(duo)個客(ke)戶(hu)端,保證對(dui)每個客(ke)戶(hu)端都能高效的(de)(de)處理,這(zhe)時(shi)候需要(yao)并(bing)發操作(zuo)。實現(xian)并(bing)發控(kong)制(zhi)的(de)(de)方法有兩(liang)個,一個是并(bing)發服(fu)務器,另一個是多(duo)路復用I/O,現(xian)在(zai)(zai)就給大家介紹一下這(zhe)兩(liang)種方法。

方法一:并(bing)發服務器

這個方法可(ke)以通過(guo)進(jin)程(線(xian)程)來實(shi)現(xian),主要(yao)根據子(zi)進(jin)程(子(zi)線(xian)程)之(zhi)間并行(xing)運行(xing)的特點。將對(dui)(dui)客(ke)戶(hu)端(duan)請求(qiu)的處理工作,交于子(zi)進(jin)程(子(zi)線(xian)程)來處理,達到一個服務(wu)器(qi)同時(shi)處理多個客(ke)戶(hu)端(duan)的效果(guo)。通過(guo)2個例(li)子(zi)實(shi)現(xian)一個簡單的服務(wu)器(qi)與客(ke)戶(hu)端(duan)的一對(dui)(dui)多。

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

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

#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;         // 地(di)址信息填寫(xie)  

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

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

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

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

perror("listen ");return -1;

}

while(1) {       // 循環 

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

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

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

while(1){

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

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

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

bzero(buf, strlen(buf));

}

close(newfd);      // 關閉 連接

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

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

}

close(sockfd);

}

客(ke)戶端代碼如下:

int main()

{

int sockfd,r;

char buf[100] = {0};

struct sockaddr_in toaddr;

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

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

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

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

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

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

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

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

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

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

scanf("%s", buf);

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

}

close(sockfd);

}

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

圖1 編譯文(wen)件

用一個終端(duan)執(zhi)行服務器,多(duo)個終端(duan)執(zhi)行客戶端(duan),結果如圖(tu)2:

左(zuo)邊第一個(ge)是服務(wu)器端(duan),先開啟;右邊2個(ge)是客戶端(duan),同時(shi)訪(fang)問服務(wu)器;服務(wu)器能同時(shi)處理這些(xie)客戶端(duan)。

圖(tu)2 執行結果

 例(li)2:線程(cheng)實現并(bing)發(fa)服(fu)務器(qi)(TCP通(tong)信)

首先,服務(wu)器端代(dai)碼如下:

……

#include<pthread.h>

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

{

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

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);    // 關閉(bi)套接(jie)字 線程結束  

}

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通信的套(tao)接字--流式套(tao)接字

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

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

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

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

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

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

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

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

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

}

    close(sockfd);

}

客戶端代碼,同例(li)1中(zhong)客戶端代碼。

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

圖3 gcc編譯

用一個(ge)終端執(zhi)行服務(wu)(wu)器(qi)(qi),多個(ge)終端執(zhi)行客戶端,結果如圖4。左邊第(di)一個(ge)是(shi)服務(wu)(wu)器(qi)(qi)端,先(xian)開(kai)啟;右邊2個(ge)是(shi)客戶端,同(tong)時(shi)訪問(wen)服務(wu)(wu)器(qi)(qi);服務(wu)(wu)器(qi)(qi)為客戶端創建線程,同(tong)時(shi)處理這些(xie)客戶端。

圖4 執行(xing)結果(guo)

方(fang)法二:多路(lu)復用I/O

基本(ben)思(si)想(xiang):有一(yi)個(ge)存儲(chu)文件(jian)描述符的表(biao),有固定(ding)的函數(select)可(ke)以檢測表(biao)中的文件(jian)描述符狀態(tai),當這(zhe)些文件(jian)描述符中的一(yi)個(ge)或多個(ge)已準備好進(jin)行(xing)I/O時函數才返回。

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

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

IO多路復用(yong)涉及函數(shu) :第一(yi):select函數(shu)  功能:檢(jian)測(ce)表中(zhong)文(wen)件描述符的狀態

函數(shu)原型 : #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 : 文件(jian)描述符大值+1

read_fds : 所有讀(du)文(wen)件描述符的集合

write_fds : 寫 文件描述符集合

except_fds : 其他的 文件描(miao)述(shu)符集合(he)

timeout :  阻塞(sai)等待的時(shi)間   毫秒(miao) 

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

NULL/0 &nbsp;   &nbsp;        無限等待(dai)

struct timeval t = {0, 0}; &nbsp; &t    0秒  不(bu)等待

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

                超時返回(hui) 0

      失敗返回 -1 

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

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

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

void FD_ZERO(fd_set *fds);       清(qing)零(ling)

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

服務器端(duan)可以采用多路(lu)IO復用實(shi)現一對多處理,代(dai)碼如(ru)下:

……

#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);// 創建(jian)TCP通信(xin)的套接字(zi)--流式(shi)套接字(zi)

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

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)); // 綁定(ding)

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

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

FD_ZERO(&fds);      // 清空表

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

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

while(1){

   // 阻(zu)塞 等(deng)待(dai)是(shi)否 有訪問到來

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

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

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

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

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

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

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

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

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

else{       // 客戶端 接收/發送(song) 信息&nbsp;

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

if(r <= 0){

   close(i);

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

}else {

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

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

bzero(buf, strlen(buf));

}

}}}}}

客(ke)戶端(duan)代碼如下:

int main()

{

int sockfd,r;

char buf[100] = {0};

struct sockaddr_in toaddr;

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

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

toaddr.sin_family = AF_INET;         // 地(di)址(zhi)信(xin)息填(tian)寫  

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

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

    // 發送(song)連接(jie)(jie)請求(qiu) 與對方建立連接(jie)(jie)

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

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

printf("connect OK\n"); &nbsp;        // 連(lian)接成(cheng)功

    scanf("%s", buf);

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

bzero(buf, strlen(buf));

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

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

close(sockfd);

}

然(ran)后(hou),gcc編譯服務器(qi)端(duan)(duan)和(he)客戶(hu)端(duan)(duan),分別(bie)生(sheng)成可(ke)執行(xing)文件,在(zai)不同終端(duan)(duan)執行(xing)(左邊第(di)一(yi)個(ge)為服務器(qi)端(duan)(duan),之后(hou)的是客戶(hu)端(duan)(duan)),執行(xing)后(hou)結果(guo)如圖5所示:

圖5 執行結果(guo)圖

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

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

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

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

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

回到頂部