-
另一個實現多工技術-select()。
-
有較低CPU使用率。
-
不用一直查看,有資料時會「被告知」。
-
設定保全:(要先檢查清單 -> fd_set readfds/fd_set writefds(宣告))
- 清空檢查表 -> FD_ZERO(&readfds)/FD_ZERO(&writefds)
- 填入要監看的socket -> FD_SET(socket1,&readfds)/FD_SET(socket2,&writefds)
-
啟動保全:
- 進行監看(偵測) -> select()
- 當發出警報,核對是哪個socket,作出合適處置 -> FD_ISSET( socket1 , &readfds)/FD_ISSET( socket2 , &writefds
-
select()可監視socket三種狀態
- 是否有資料抵達,可以讀取
- 是否有buffer空間,可以寫入buffer
- 是否有例外發生
-
select()的回傳值
- 正常情況下返回偵測到的事件總數量
- 經過了timeout時間,仍無偵測到,返回值為0
- 如果select被中斷,它將返回-1
#include <stdio.h>
#include <winsock.h>
#define MAXRECV 1024
int main(){
SOCKET sd,cli_sd;
WSADATA wsadata;
struct sockaddr_in serv,cli;
int i,n,cnt=0;
char str[1024];
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 cli_len=sizeof(cli);
printf("Server waits.\n");
cli_sd = accept(sd, (struct sockaddr *) &cli,&cli_len );
printf("Client is connected: socked fd is %d, ip is : %s, port : %d \n",cli_sd, inet_ntoa(cli.sin_addr),ntohs(cli.sin_port));
fd_set readfds;
int activity;
while(1){//檢查
printf("[1]clear the socket fd set. \n");
FD_ZERO(&readfds);//清空檢查表
printf("[2]add client socket to fd set. \n");
FD_SET(cli_sd,&readfds);//填入要監看的socket
printf("[3]call select() and waiting. \n");
activity = select(0,&readfds, NULL , NULL , NULL);
printf("[4]wake up from select():%d \n",activity);
if(activity == SOCKET_ERROR)
{
printf("select call failed with error code: %d",WSAGetLastError());
exit(EXIT_FAILURE);
}
if(FD_ISSET(cli_sd,&readfds))//核對是哪個socket
{
n = recv(cli_sd,str, MAXRECV, 0);
printf("recv from client: %s\n",str);
if (n <= 0 )
{
printf("connection closed.\n");
break;
}
}
}
closesocket(sd);
closesocket(cli_sd);
WSACleanup();
}
client
#include <stdio.h>
#include <winsock.h>
int main(){
SOCKET sd;
WSADATA wsadata;
struct sockaddr_in serv;
int i,n;
char str[100] = "How are you.";
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");
for(;;){
Sleep(10000);
send(sd,str, strlen(str)+1,0);
printf("Send : %s(in 10 secs)\n",str);
}
closesocket(sd);
WSACleanup();
}
clint 和第一個只差在多了RECV,因為要有echo的效果。
#include <stdio.h>
#include <winsock.h>
#define MAXRECV 1024
int main(){
SOCKET sd,cli_sd1,cli_sd2;
WSADATA wsadata;
struct sockaddr_in serv,cli;
int i,n;
char str[1024];
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 cli_len=sizeof(cli);
printf("Server waits.\n");
cli_sd1 = accept(sd, (struct sockaddr *) &cli,&cli_len );
printf("Client is connected: socked fd is %d, ip is : %s, port : %d \n",cli_sd1, inet_ntoa(cli.sin_addr),ntohs(cli.sin_port));
cli_sd2 = accept(sd, (struct sockaddr *) &cli,&cli_len );
printf("Client is connected: socked fd is %d, ip is : %s, port : %d \n",cli_sd2, inet_ntoa(cli.sin_addr),ntohs(cli.sin_port));
fd_set readfds;
int activity;
while(1){//檢查
printf("[1]clear the socket fd set. \n");
FD_ZERO(&readfds);//清空檢查表
printf("[2]add client socket to fd set. \n");
FD_SET(cli_sd1,&readfds);//填入要監看的socket
FD_SET(cli_sd2,&readfds);
printf("[3]call select() and waiting. \n");
activity = select(0,&readfds, NULL , NULL , NULL);
printf("[4]wake up from select():%d \n",activity);
if(activity == SOCKET_ERROR)
{
printf("select call failed with error code: %d",WSAGetLastError());
exit(EXIT_FAILURE);
}
//client1
if(FD_ISSET(cli_sd1,&readfds))//核對是哪個socket
{
n = recv(cli_sd1,str, MAXRECV, 0);
printf("recv from client1: %s\n",str);
send(cli_sd1,str,strlen(str)+1,0);
}
//client2
if(FD_ISSET(cli_sd2,&readfds))//核對是哪個socket
{
n = recv(cli_sd2,str, MAXRECV, 0);
printf("recv from client2: %s\n",str);
send(cli_sd2,str,strlen(str)+1,0);
}
}
closesocket(sd);
closesocket(cli_sd1);
closesocket(cli_sd2);
WSACleanup();
}
client
#include <stdio.h>
#include <winsock.h>
#define MAXLINE 1024
int main(){
SOCKET sd;
WSADATA wsadata;
struct sockaddr_in serv;
int i,n;
char str[100] = "How are you.";
char str_r[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");
for(;;){
Sleep(10000);
send(sd,str, strlen(str)+1,0);
printf("Send : %s(in 10 secs)\n",str);
recv(sd,str_r,MAXLINE,0);
printf("Recv:%s\n",str);
}
closesocket(sd);
WSACleanup();
}
#include <stdio.h>
#include <winsock.h>
#define MAXRECV 1024
#define MAXCLI 10
int main(){
SOCKET sd,cli_sd[MAXCLI],new_socket;//支援多個client,所以設陣列
WSADATA wsadata;
struct sockaddr_in serv,cli;
int i,n,client_num=0;
char str[MAXRECV];
for(int i=0;i<MAXCLI;i++) cli_sd[i] = 0;//初始化
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);
fd_set readfds;
int activity;
while(1){//檢查
printf("[1]clear the socket fd set. \n");
FD_ZERO(&readfds);//清空檢查表
printf("[2]add client socket to fd set. \n");
FD_SET(sd,&readfds);//server也要加入!!!!!
printf("[2]add client socket to fd set. \n");
//填入要監看的socket
for(int i=0;i<MAXCLI;i++)//陣列 所以用迴圈
{
if(cli_sd[i] > 0)
{
FD_SET(cli_sd[i],&readfds);//填入要監看的socket
}
}
printf("[3]call select() and waiting. \n");
activity = select(0,&readfds, NULL , NULL , NULL);
printf("[4]wake up from select():%d \n",activity);
if(activity == SOCKET_ERROR)
{
printf("select call failed with error code: %d",WSAGetLastError());
exit(EXIT_FAILURE);
}
//新連線
if(FD_ISSET(sd,&readfds))
{
int cli_len=sizeof(cli);
printf("Server waits.\n");
new_socket = accept(sd, (struct sockaddr *) &cli,&cli_len );
printf("New connection: socket socked fd is %d, ip is : %s, port : %d \n",new_socket, inet_ntoa(cli.sin_addr),ntohs(cli.sin_port));
for(int i=0;i<MAXCLI;i++)
{
if(cli_sd[i] == 0){
cli_sd[i] = new_socket;
client_num++;
printf("The %d client socket is in cli_sd[%d]\n",client_num,i);
break;
}
}
}
//check
for(int i=0;i<MAXCLI;i++)
{
if(FD_ISSET(cli_sd[i],&readfds))
{
n = recv(cli_sd[i],str, MAXRECV, 0);
if(n == SOCKET_ERROR)//發現已經結束連線
{
int error__code = WSAGetLastError();
if(error__code == WSAECONNRESET)
{
printf("Host disconnected unexpectedly.\n");
closesocket(cli_sd[i]);
cli_sd[i] = 0;
client_num--;
}
else{printf("recv failed with error code : %d"),error__code;}
}
if(n == 0)//沒連到
{
printf("Host disconnected. \n");
closesocket(cli_sd[i]);
cli_sd[i] = 0;
client_num--;
}
//有連到
if(n > 0)
{
printf("recv from and echo to cli[%d]: %s \n",i,str);
send(cli_sd[i],str,strlen(str)+1,0);
}
}
}
}
closesocket(sd);
closesocket(cli_sd[MAXCLI]);
WSACleanup();
}
client 和 練習二相同。
server和上一題的差異:recv後,send透過另一種方法傳送。 每次echo的狀況會觸發兩次select,分別是recv和write的動作。
#include <stdio.h>
#include <winsock.h>
#define MAXRECV 1024
#define MAXCLI 10
int main(){
SOCKET sd,cli_sd[MAXCLI],new_socket;//支援多個client,所以設陣列
WSADATA wsadata;
struct sockaddr_in serv,cli;
int i,n,client_num=0,ready_to_send[MAXCLI];
char str[MAXRECV];
for(int i=0;i<MAXCLI;i++)
{
cli_sd[i] = 0;//初始化
ready_to_send[i] = 0;//初始化
}
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);
fd_set readfds;
fd_set writefds;
int activity;
while(1){//檢查
printf("[1]clear the socket fd set. \n");
FD_ZERO(&readfds);//清空檢查表
FD_ZERO(&writefds);
printf("[2]add client socket to fd set. \n");
FD_SET(sd,&readfds);//server也要加入!!!!!
printf("[2]add client socket to fd set. \n");
//填入要監看的socket
for(int i=0;i<MAXCLI;i++)
{
if(cli_sd[i] > 0)//判斷要傳送還是要接收~~~~
{
if(ready_to_send[i]==0)
{
FD_SET(cli_sd[i],&readfds);//讀入
}
else
{
FD_SET(cli_sd[i],&writefds);//寫入
}
}
}
printf("[3]call select() and waiting. \n");
activity = select(0,&readfds, &writefds , NULL , NULL);//多了一個writefds
printf("[4]wake up from select():%d \n",activity);
if(activity == SOCKET_ERROR)
{
printf("select call failed with error code: %d",WSAGetLastError());
exit(EXIT_FAILURE);
}
//新連線
if(FD_ISSET(sd,&readfds))
{
int cli_len=sizeof(cli);
printf("Server waits.\n");
new_socket = accept(sd, (struct sockaddr *) &cli,&cli_len );
printf("New connection: socket socked fd is %d, ip is : %s, port : %d \n",new_socket, inet_ntoa(cli.sin_addr),ntohs(cli.sin_port));
for(int i=0;i<MAXCLI;i++)
{
if(cli_sd[i] == 0){
cli_sd[i] = new_socket;
client_num++;
printf("The %d client socket is in cli_sd[%d]\n",client_num,i);
break;
}
}
}
//check
for(int i=0;i<MAXCLI;i++)
{
if(FD_ISSET(cli_sd[i],&readfds))
{
n = recv(cli_sd[i],str, MAXRECV, 0);
if(n == SOCKET_ERROR)//發現已經結束連線
{
int error__code = WSAGetLastError();
if(error__code == WSAECONNRESET)
{
printf("Host disconnected unexpectedly.\n");
closesocket(cli_sd[i]);
cli_sd[i] = 0;
client_num--;
}
else{printf("recv failed with error code : %d"),error__code;}
}
if(n == 0)//沒連到
{
printf("Host disconnected. \n");
closesocket(cli_sd[i]);
cli_sd[i] = 0;
client_num--;
}
//有連到
if(n > 0)
{
printf("recv from and echo to cli[%d]: %s \n",i,str);
//send(cli_sd[i],str,strlen(str)+1,0);
ready_to_send[i]=1;//設1,表示有資料準備要傳送
}
}
//判斷有資料要傳送
if(FD_ISSET(cli_sd[i],&writefds))
{
send(cli_sd[i],str,strlen(str)+1,0);
ready_to_send[i]=0;//設為0
}
}
}
closesocket(sd);
closesocket(cli_sd[MAXCLI]);
WSACleanup();
}
client 和 練習二相同。
#include <stdio.h>
#include <winsock.h>
#define MAXRECV 1024
#define MAXCLI 10
int main(){
SOCKET sd,cli_sd[MAXCLI],new_socket;//支援多個client,所以設陣列
WSADATA wsadata;
struct sockaddr_in serv,cli;
int i,n,client_num=0,ready_to_send[MAXCLI];
char str[MAXRECV];
TIMEVAL timeout;//新增的(計算時間)
for(int i=0;i<MAXCLI;i++)
{
cli_sd[i] = 0;//初始化
ready_to_send[i] = 0;//初始化
}
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);
fd_set readfds;
fd_set writefds;
int activity;
while(1){//檢查
printf("[1]clear the socket fd set. \n");
FD_ZERO(&readfds);//清空檢查表
FD_ZERO(&writefds);
printf("[2]add client socket to fd set. \n");
FD_SET(sd,&readfds);//server也要加入!!!!!
printf("[2]add client socket to fd set. \n");
//填入要監看的socket
for(int i=0;i<MAXCLI;i++)
{
if(cli_sd[i] > 0)//判斷要傳送還是要接收~~~~
{
if(ready_to_send[i]==0)
{
FD_SET(cli_sd[i],&readfds);//讀入
}
else
{
FD_SET(cli_sd[i],&writefds);//寫入
}
}
}
printf("[3]call select() and waiting. \n");
//set timeout
timeout.tv_sec = 5;
timeout.tv_usec = 0;
activity = select(0,&readfds, &writefds , NULL , &timeout);//多了一個timeout~~
printf("[4]wake up from select():%d \n",activity);
if(activity == SOCKET_ERROR)
{
printf("select call failed with error code: %d",WSAGetLastError());
exit(EXIT_FAILURE);
}
//沒有事發生印出~~~
if(activity == 0)
{
printf("NNo message: select() has waited for 5 sec. \n");
}
//新連線
if(FD_ISSET(sd,&readfds))
{
int cli_len=sizeof(cli);
printf("Server waits.\n");
new_socket = accept(sd, (struct sockaddr *) &cli,&cli_len );
printf("New connection: socket socked fd is %d, ip is : %s, port : %d \n",new_socket, inet_ntoa(cli.sin_addr),ntohs(cli.sin_port));
for(int i=0;i<MAXCLI;i++)
{
if(cli_sd[i] == 0){
cli_sd[i] = new_socket;
client_num++;
printf("The %d client socket is in cli_sd[%d]\n",client_num,i);
break;
}
}
}
//check
for(int i=0;i<MAXCLI;i++)
{
if(FD_ISSET(cli_sd[i],&readfds))
{
n = recv(cli_sd[i],str, MAXRECV, 0);
if(n == SOCKET_ERROR)//發現已經結束連線
{
int error__code = WSAGetLastError();
if(error__code == WSAECONNRESET)
{
printf("Host disconnected unexpectedly.\n");
closesocket(cli_sd[i]);
cli_sd[i] = 0;
client_num--;
}
else{printf("recv failed with error code : %d"),error__code;}
}
if(n == 0)//沒連到
{
printf("Host disconnected. \n");
closesocket(cli_sd[i]);
cli_sd[i] = 0;
client_num--;
}
//有連到
if(n > 0)
{
printf("recv from and echo to cli[%d]: %s \n",i,str);
//send(cli_sd[i],str,strlen(str)+1,0);
ready_to_send[i]=1;//設1,表示有資料準備要傳送
}
}
//判斷有資料要傳送
if(FD_ISSET(cli_sd[i],&writefds))
{
send(cli_sd[i],str,strlen(str)+1,0);
ready_to_send[i]=0;//設為0
}
}
}
closesocket(sd);
closesocket(cli_sd[MAXCLI]);
WSACleanup();
}
client 和 練習二相同。
這星期的程式相對複雜了許多,有很多需要去透過if判斷,裡面我覺得比較難的是第四題,因為他不是直接用send傳回,而是改用writefds的方式另外寫,因此要先透過ready_to_send去設置有沒有東西要被傳送,再接續後面的動作,整個看完就覺得很酷!