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

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

linux網(wang)絡(luo)編程中的(de)并(bing)發控制(zhi) 時間:2018-09-25 ;     來源(yuan):未知(zhi)

在Linux網絡編程(cheng)中,一(yi)般建(jian)立在兩端(duan)(duan)(duan)之間,服(fu)(fu)務器(qi)端(duan)(duan)(duan)和客(ke)(ke)戶(hu)(hu)端(duan)(duan)(duan)。客(ke)(ke)戶(hu)(hu)端(duan)(duan)(duan)是面向(xiang)用戶(hu)(hu)的應用,而服(fu)(fu)務器(qi)端(duan)(duan)(duan)要(yao)處理(li)客(ke)(ke)戶(hu)(hu)端(duan)(duan)(duan)所(suo)提出的請(qing)求。通常一(yi)個服(fu)(fu)務器(qi)要(yao)面向(xiang)多個客(ke)(ke)戶(hu)(hu)端(duan)(duan)(duan),保證對每個客(ke)(ke)戶(hu)(hu)端(duan)(duan)(duan)都(dou)能(neng)高效的處理(li),這(zhe)(zhe)時候需要(yao)并(bing)發操作。實(shi)現并(bing)發控制的方(fang)(fang)法有兩個,一(yi)個是并(bing)發服(fu)(fu)務器(qi),另一(yi)個是多路復用I/O,現在就(jiu)給大家(jia)介紹一(yi)下這(zhe)(zhe)兩種方(fang)(fang)法。

方法一:并發服務器

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

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

首(shou)先,服務器(qi)端代碼如下:

#include

#include

#include

#include

#include

#include

#include

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

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

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

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

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

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

perror("listen ");return -1;

}

while(1) { // 循(xun)環

newfd = accept(sockfd, &fromaddr, &len); // 阻(zu)塞接(jie)收 客(ke)戶端的連(lian)接(jie)請(qing)求(qiu)

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

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

while(1){

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

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

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

bzero(buf, strlen(buf));

}

close(newfd); // 關閉 連(lian)接

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

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

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

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

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

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

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

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

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

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

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

scanf("%s", buf);

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

}

close(sockfd);

}

然后,編譯服務(wu)器(qi)端(duan) 和(he) 客戶端(duan),終(zhong)端(duan)執行(xing)如圖(tu)1命(ming)令:

圖1 編譯(yi)文(wen)件

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

左邊第一個(ge)是服務器端(duan)(duan),先開啟(qi);右(you)邊2個(ge)是客(ke)(ke)戶端(duan)(duan),同時訪問服務器;服務器能同時處(chu)理這(zhe)些客(ke)(ke)戶端(duan)(duan)。

圖2 執(zhi)行(xing)結果(guo)

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

首先,服務器(qi)端(duan)代碼(ma)如下(xia):

……

#include

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

{

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

char buf[100] = {0};

int r;

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

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

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

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

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

bzero(buf,strlen(buf));

}

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

}

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

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

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

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

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

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

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

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

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

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

}

close(sockfd);

}

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

編(bian)譯服務器和(he)客戶端,終(zhong)端執行(xing)命(ming)令,如圖3:注意線程編(bian)譯時加載(zai)庫。

圖3 gcc編譯

用一個終端(duan)(duan)執行服(fu)(fu)務器(qi),多個終端(duan)(duan)執行客(ke)(ke)戶(hu)端(duan)(duan),結果如圖4。左(zuo)邊第一個是(shi)服(fu)(fu)務器(qi)端(duan)(duan),先開啟;右邊2個是(shi)客(ke)(ke)戶(hu)端(duan)(duan),同(tong)時訪問服(fu)(fu)務器(qi);服(fu)(fu)務器(qi)為客(ke)(ke)戶(hu)端(duan)(duan)創建(jian)線程,同(tong)時處理這(zhe)些客(ke)(ke)戶(hu)端(duan)(duan)。

圖(tu)4 執行結(jie)果(guo)

方法二:多路復用I/O

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

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

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

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

函數原型 : #include #include #include

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

n : 文件描述符大值+1

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

write_fds : 寫(xie) 文件描述符集合

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

timeout : 阻塞等(deng)待的時間(jian) 毫(hao)秒

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

NULL/0 無限等待

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

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

超時返回 0

失敗(bai)返(fan)回 -1

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

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

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

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

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

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

……

#include

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

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

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); // 清(qing)空(kong)表

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

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, &amp;fds)) { //找出(chu) I/O操作的(de)套接(jie)字描述(shu)符(fu)

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

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

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

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

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

}

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

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

if(r <= 0){

close(i);

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

}else {

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

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

bzero(buf, strlen(buf));

}

}}}}}

客戶(hu)端代碼如下:

int main()

{

int sockfd,r;

char buf[100] = {0};

struct sockaddr_in toaddr;

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

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

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

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

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

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

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

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

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

scanf("%s", buf);

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

bzero(buf, strlen(buf));

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

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

close(sockfd);

}

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

圖5 執行結果圖

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

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

上一篇:10進制轉16進制(采用移位實現)

下一篇:進程間通信方式簡述及區別

熱(re)點文章推(tui)薦
華清學員就業榜(bang)單
高(gao)薪(xin)學員經驗分(fen)享
熱(re)點(dian)新(xin)聞推(tui)薦
前(qian)臺專(zhuan)線(xian):010-82525158 企業培訓(xun)洽(qia)談專線:010-82525379 院校合(he)作洽談(tan)專線:010-82525379 Copyright © 2004-2022 北京華清遠見科技集團有限公司 版權所有 ,,京公海網安備11010802025203號

回到頂部