# 網路程式設計 HW8
* 攔阻模式
函示呼叫後,需要完成才回覆。(API等完成後才回覆)
* 非攔阻模式
* API立即回覆,不需要等到完成後才回覆。
* 代價:
* 回覆!=完成
* 須不斷檢查才能確認完成。
* 報酬:
不會被卡住,等待期間可以做其他事情。
* 攔阻模式優缺點
| 優點 | 缺點 |
|:---------:|:--------------------------:|
| 流程簡單 | 函式攔阻後無法進行其他動作 |
| 容易debug | 功能受限 |
| 流程簡單 | -- |
* NB程式
* 使用API:ioctlsocket()
* 改變socket模式
* 放置需要改變模式之前(recv()、accept())
* 攔阻模式 : iMode=0
* 非攔阻模式 : iMode=1
## 應用1 聊天室第1版解除一人一句的限制
* 非攔阻模式
* server
一直接收,每隔5秒才回傳I know
* client
用不同間隔,server總是5秒才回應
* 畫面

:::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改為非攔阻模式。
* 畫面

:::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可維持攔阻模式
* 畫面
* 一個登入

* 兩個登入

* 三個登入

* 四個登入

* 五個登入

:::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: `網路程式設計`