# 網路程式設計 HW8 * 攔阻模式 函示呼叫後,需要完成才回覆。(API等完成後才回覆) * 非攔阻模式 * API立即回覆,不需要等到完成後才回覆。 * 代價: * 回覆!=完成 * 須不斷檢查才能確認完成。 * 報酬: 不會被卡住,等待期間可以做其他事情。 * 攔阻模式優缺點 | 優點 | 缺點 | |:---------:|:--------------------------:| | 流程簡單 | 函式攔阻後無法進行其他動作 | | 容易debug | 功能受限 | | 流程簡單 | -- | * NB程式 * 使用API:ioctlsocket() * 改變socket模式 * 放置需要改變模式之前(recv()、accept()) * 攔阻模式 : iMode=0 * 非攔阻模式 : iMode=1 ## 應用1 聊天室第1版解除一人一句的限制 * 非攔阻模式 * server 一直接收,每隔5秒才回傳I know * client 用不同間隔,server總是5秒才回應 * 畫面 ![](https://i.imgur.com/vtd68Na.png) :::spoiler * Client ```cpp== // simple TCP chat client #include <stdio.h> #include <winsock.h> int main(){ SOCKET sd; WSADATA wsadata; struct sockaddr_in serv; int i,n; char str[100]="I love NP!"; WSAStartup(0x101,&wsadata); sd = socket(AF_INET, SOCK_STREAM, 0); serv.sin_family = AF_INET; serv.sin_port = htons(1234); serv.sin_addr.s_addr = inet_addr("127.0.0.1"); connect(sd, (struct sockaddr *) &serv,sizeof(serv) ); printf("Connect to server.\n"); u_long iMode=1; // set NB mode ioctlsocket(sd,FIONBIO,&iMode); while(1){ strcpy(str,"I miss you!"); printf("(clietn): %s\n",str); send(sd,str, strlen(str)+1,0); Sleep(500); memset(str,0,100); n = recv(sd,str, 100, 0); if(n>0) { str[n]='\0'; printf("(server): %s\n",str); } } closesocket(sd); WSACleanup(); } ``` * Server ```cpp== // simple TCP chatroom server #include <stdio.h> #include <winsock.h> #include <time.h> int main(){ SOCKET sd,clnt_sd; WSADATA wsadata; struct sockaddr_in serv,clnt; int i,n; char str[100]; WSAStartup(0x101,&wsadata); sd = socket(AF_INET, SOCK_STREAM, 0); serv.sin_family = AF_INET; serv.sin_port = htons(1234); serv.sin_addr.s_addr = inet_addr("127.0.0.1"); bind(sd, (struct sockaddr *) &serv, sizeof(serv)); listen(sd,5); int clnt_len=sizeof(clnt); printf("Server waits.\n"); clnt_sd = accept(sd, (struct sockaddr *) &clnt,&clnt_len ); printf("Client is connected.\n"); u_long iMode=1; ioctlsocket(clnt_sd,FIONBIO,&iMode); clock_t t2=0; clock_t t1=0; float time; while(1){ n = recv(clnt_sd,str, 100, 0); if(n>0) { str[n]='\0'; printf("(client): %s\n",str); } t1=clock(); time=(t1-t2)/CLOCKS_PER_SEC; if(time>5) //5秒 { strcpy(str,"I know."); printf("(server): %s\n",str); send(clnt_sd,str, strlen(str)+1,0); t2=clock(); Sleep(800); } } closesocket(sd); closesocket(clnt_sd); WSACleanup(); } ``` ::: ## 應用2 聊天室第2版解除一人一句限制 * lient執行後,先輸入名稱與時間間隔 * 之後便固定週期發送出其名稱。 * server先等5秒待雙方都連入後 * 接著不管順序,收到誰的都轉送給另一方 * client程式只有一個 * 不分client1,client2。 * 由於client/server雙方都有接收, * 都須將socket改為非攔阻模式。 * 畫面 ![](https://i.imgur.com/5GaifSo.png) :::spoiler * Client ```cpp== // simple TCP chat client(1) // v2: two client chat through server #include <stdio.h> #include <winsock.h> #include <time.h> int main(){ SOCKET sd; WSADATA wsadata; struct sockaddr_in serv; int i,n; char str[100]; char str1[100]; WSAStartup(0x101,&wsadata); sd = socket(AF_INET, SOCK_STREAM, 0); serv.sin_family = AF_INET; serv.sin_port = htons(1234); serv.sin_addr.s_addr = inet_addr("127.0.0.1"); connect(sd, (struct sockaddr *) &serv,sizeof(serv) ); printf("Connect to server.\n"); u_long iMode=1; // set NB mode ioctlsocket(sd,FIONBIO,&iMode); printf("Client Name : "); //名稱 gets(str1); printf("Time Interval(sec) : "); //時間間隔 int ti; scanf("%d",&ti); Sleep(5000); clock_t t1,t2=0; float time; while(1){ t1=clock(); time=(t1-t2)/CLOCKS_PER_SEC; if(time>ti) { strcpy(str,str1); printf("[Client]: %s\n",str); send(sd,str,strlen(str),0); t2=clock(); } memset(str,0,100); n=recv(sd,str, 100, 0); if(n>0) { str[n]='\0'; printf("[Other]:%s\n",str); } Sleep(100); } closesocket(sd); WSACleanup(); } ``` * Server ```cpp== #include <stdio.h> #include <winsock.h> int main(){ SOCKET sd,clnt_sd1,clnt_sd2; WSADATA wsadata; struct sockaddr_in serv,clnt1,clnt2; int i,n; char str[100]; WSAStartup(0x101,&wsadata); sd = socket(AF_INET, SOCK_STREAM, 0); serv.sin_family = AF_INET; serv.sin_port = htons(1234); serv.sin_addr.s_addr = inet_addr("127.0.0.1"); bind(sd, (struct sockaddr *) &serv, sizeof(serv)); listen(sd,5); int clnt_len1=sizeof(clnt1); int clnt_len2=sizeof(clnt2); printf("Server waits.\n"); clnt_sd1 = accept(sd, (struct sockaddr *) &clnt1,&clnt_len1 ); printf("One client connects.\n"); clnt_sd2 = accept(sd, (struct sockaddr *) &clnt2,&clnt_len2 ); printf("Two clients connect.\n"); u_long iMode=1; ioctlsocket(clnt_sd1,FIONBIO,&iMode); ioctlsocket(clnt_sd2,FIONBIO,&iMode); while(1){ //收到(1) n = recv(clnt_sd1,str, 100, 0); if(n>0) { str[n]='\0'; printf("(1): %s\n",str); send(clnt_sd2,str,strlen(str)+1,0); } //收到(2) n = recv(clnt_sd2,str, 100, 0); if (n > 0 ) { str[n]='\0'; printf("(2): %s\n",str); send(clnt_sd1,str, strlen(str)+1,0); } Sleep(100); } closesocket(sd); closesocket(clnt_sd1); closesocket(clnt_sd2); WSACleanup(); } ``` ::: ## 應用3 server可服務多個client 且 client可陸續連入 * client可隨時連入,並開始接收文字 * server可最多服務5個client > 專注於server的非攔阻部分,client可維持攔阻模式 * 畫面 * 一個登入 ![](https://i.imgur.com/7Yo92Qi.png) * 兩個登入 ![](https://i.imgur.com/wr5uRBx.png) * 三個登入 ![](https://i.imgur.com/prTC3ul.png) * 四個登入 ![](https://i.imgur.com/zkFUcZv.png) * 五個登入 ![](https://i.imgur.com/tDpPo7h.png) :::spoiler * Client ```cpp== // simple TCP chat client #include <stdio.h> #include <winsock.h> int main(){ SOCKET sd; WSADATA wsadata; struct sockaddr_in serv; int i,n; char str[100]="I love NP!"; WSAStartup(0x101,&wsadata); sd = socket(AF_INET, SOCK_STREAM, 0); serv.sin_family = AF_INET; serv.sin_port = htons(1234); serv.sin_addr.s_addr = inet_addr("127.0.0.1"); connect(sd, (struct sockaddr *) &serv,sizeof(serv) ); printf("Connect to server.\n"); while(1){ n = recv(sd,str, 100, 0); if(n <= 0) break; printf("Recv: %s\n",str); } closesocket(sd); WSACleanup(); } ``` * Server ```cpp== // simple TCP chatroom server // v1:one clint chat to server #include <stdio.h> #include <winsock.h> int main(){ SOCKET sd,clnt_sd[5]; WSADATA wsadata; struct sockaddr_in serv,clnt; int i,n; char str[100]="I love NP!"; WSAStartup(0x101,&wsadata); sd = socket(AF_INET, SOCK_STREAM, 0); serv.sin_family = AF_INET; serv.sin_port = htons(1234); serv.sin_addr.s_addr = inet_addr("127.0.0.1"); bind(sd, (struct sockaddr *) &serv, sizeof(serv)); listen(sd,5); u_long iMode=1; ioctlsocket(sd,FIONBIO,&iMode); int clnt_len=sizeof(clnt); printf("Server waits.\n"); for(int i=0;i<5;i++) { clnt_sd[i]=-1; } int curr_client_num=0; while(1){ n=accept(sd,(struct sockaddr*) &clnt,&clnt_len); if(n!=-1) { clnt_sd[curr_client_num]=n; printf("Client[%d] is connected.\n",curr_client_num); curr_client_num++; } for(int i=0;i<5;i++) { if(clnt_sd[i]!=-1) { send(clnt_sd[i],str,strlen(str)+1,0); printf("Send to clnt_sd[%d]: %s\n",i,str); } } Sleep(1000); } closesocket(sd); for(int i=0;i<5;i++) { closesocket(clnt_sd[i]); } WSACleanup(); system("pause"); } ``` ::: ## 心得 覺得這次作業很酷,有些剛好是我之前有想過是不是都可以做的,但都還沒實際操作過,這次剛好有老師解惑且有機會可以實作看看。 ###### tags: `網路程式設計`