# 網路程式設計 hw8 ## 觀念題 ### 第一題 - 請說明,當socket設定blocking mode與non-blocking mode之後,會有何差異? - 在攔阻模式下 - recv()函數會等待資料收到才會進到下一行 - accept()函數會等待客戶連線才會進到下一行 - 在非攔阻模式下 - recv()函數不會等待收到資料,當沒收到,會回傳-1 - accept()函數不會等待客戶連線,當沒連線,會回傳-1 - ~~感覺上非攔阻模式就像急性子~~ - 請說明,影片裏第一個程式的範例裏,由blocking mode改為non-blocking mode,請問結果有何不同? - 以下為執行結果 - blocking mode ![image](https://github.com/kevin0920911/NetworkGIF/blob/main/w10/GIF/1.2.gif?raw=true) - non-blocking mode ![image](https://github.com/kevin0920911/NetworkGIF/blob/main/w10/GIF/1.1.gif?raw=true) - 差別 - blocking mode - 在blocking mode下,recv會一直等待,直到接收到資料 - non-blocking mode - 在non-blocking mode,recv不會等待,沒收到就直接回傳-1 ## 實作題 ### 第二題 - TCP或UDP都可改為非攔阻,請將上例中non-blocking mode的client與server,由TCP改為UDP。 - 程式碼 - Sever :::spoiler C ```C= #include <stdio.h> #include <string.h> #include <winsock.h> #define MAXLINE 1024 /* 字串緩衝區長度 */ int main() { SOCKET serv_sd, cli_sd; /* socket 描述子 */ int cli_len, n,i; char str[MAXLINE]; struct sockaddr_in serv, cli; WSADATA wsadata; WSAStartup(0x101, &wsadata); //呼叫 WSAStartup() 註冊 WinSock DLL 的使用 serv_sd=socket(AF_INET, SOCK_DGRAM, 0);// 開啟 UDP socket //指定 socket 的 IP 位址和 port number serv.sin_family = AF_INET; serv.sin_addr.s_addr = 0; serv.sin_port = htons(5678); // 指定 IPPORT_ECHO 為 echo port bind(serv_sd, (LPSOCKADDR) &serv, sizeof(serv)); cli_len = sizeof(cli); u_long iMode=1; ioctlsocket(serv_sd,FIONBIO,&iMode); while (1) { n=recvfrom(serv_sd, str, MAXLINE, 0, (LPSOCKADDR) &cli, &cli_len); str[n]='\0'; if (n > 0 ) printf("Recv: %s\n",str); // 顯示從 client 傳來的字串 Sleep(1000); int nError=WSAGetLastError(); if(nError!=WSAEWOULDBLOCK && nError!=0){ printf("Disconnected! error code:%d\n",nError); closesocket(cli_sd); break; } } //結束 WinSock DLL 的使用 closesocket(serv_sd); closesocket(cli_sd); WSACleanup(); } ``` ::: :::spoiler Python ```Python= import socket import time """ Server Information IP: 127.0.0.1 PORT: 1234 """ IP, PORT = "127.0.0.1", 1234 # create UDP socket server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) server.bind((IP, PORT)) server.setblocking(False) while True: try: data, addr = server.recvfrom(1024) print(f"Received: {data.decode()} from {addr}") except: pass ``` ::: - Client :::spoiler C ```C= #include <stdio.h> #include <string.h> #include <winsock.h> #define MAXLINE 1024 int main(int argc, char** argv) { SOCKET sd; struct sockaddr_in serv; char str[1024]="How are you?",str1[1024]; WSADATA wsadata; int n,i; WSAStartup(0x101,(LPWSADATA) &wsadata); // 呼叫 WSAStartup() 註冊 WinSock DLL 的使用 sd=socket(AF_INET, SOCK_DGRAM, 0); //開啟一個 UDP socket. serv.sin_family = AF_INET; serv.sin_addr.s_addr = inet_addr("127.0.0.1"); serv.sin_port = htons(5678); connect(sd, (LPSOCKADDR) &serv, sizeof(serv)); // 連接至 echo server printf("Client has connectted to Server.\n"); printf("Waiting 10 secs on purpose...\n"); Sleep(10000); // 建立連線後,刻意停頓, 觀察 recv()是否block for (;;){ //每隔3秒,週期送出 Sleep(3000); send(sd, str, strlen(str)+1, 0); int nError=WSAGetLastError(); if(nError!=WSAEWOULDBLOCK && nError!=0) { printf("Winsock error code:%d\n",nError); printf("Disconnected!"); // Close our socket entirely closesocket(sd); break; } printf("Send every 3 secs: %s\n" ,str); } closesocket(sd); //關閉TCP socket WSACleanup(); // 結束 WinSock DLL 的使用 return 0; } ``` ::: :::spoiler Python ```Python= import socket import time """ Server Information IP: 127.0.0.1 PORT: 1234 """ IP, PORT = "127.0.0.1", 1234 # create UDP socket sd = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sd.connect((IP, PORT)) sd.setblocking(False) print("Wait for 10 secs...") time.sleep(10) while True: try: sd.send(b"Hello World!") print("Send every 3 secs: Hello World!") time.sleep(3) except: pass ``` ::: - 執行結果 ![image](https://github.com/kevin0920911/NetworkGIF/blob/main/w10/GIF/0.gif?raw=true) - 備註 - 在Python中非阻攔模式需要用try/except ```Python try: sd.resv() except: pass ``` ### 第三題 - 程式碼 - Sever :::spoiler C ```C= #include <stdio.h> #include <winsock.h> int main(){ FILE *fin, *fout; fin = fopen("def.txt", "r"); fout = fopen("server.txt","w"); SOCKET sd,clnt_sd; WSADATA wsadata; struct sockaddr_in serv,clnt; int i,n; char str[2048]=""; 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 mode = 1; ioctlsocket(clnt_sd,FIONBIO,&mode); while(1){ char* c_line = NULL; size_t len = 0; int status = getline(&c_line,&len,fin); if (status != EOF) { send(clnt_sd,c_line, strlen(c_line)+1,0); printf("Send: %s\n",c_line); } else{ send(clnt_sd,"END", 4,0); printf("Send: END\n"); } free(c_line); memset(str,0,sizeof(str)); n = recv(clnt_sd,str, sizeof(str), 0); if (n > 0){ str[n] = '\0'; printf("Receive: %s\n",str); if (strcmp(str, "END\0") == 0 && status == EOF) { break; } else if (strcmp(str, "END\0") == 0) { continue; } fprintf(fout,"%s",str); } else if (n == 0){ break; } Sleep(100); } closesocket(sd); closesocket(clnt_sd); fclose(fin); fclose(fout); WSACleanup(); } ``` ::: :::spoiler Python ```Python= import socket import time """ File Input: def.txt Output: server.txt """ fin = open("def.txt", "r") fout = open("server.txt", "w") """ Server Information IP: 127.0.0.1 PORT: 1234 """ IP,PORT = "127.0.0.1", 1234 # crete a server socket serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) serverSocket.bind((IP,PORT)) serverSocket.listen(5) # Accept the client connection clientSocket, address = serverSocket.accept() # Set Non Blocking clientSocket.setblocking(False) serverSocket.setblocking(False) EOF = False while True: try: data = clientSocket.recv(1024).decode() print(f"Client: {data}") if EOF and data == "END": break elif EOF: continue fout.write(data) except : pass try: data = fin.readline() if not data: raise EOFError clientSocket.send(data.encode()) print(f"Server: {data}") except EOFError: clientSocket.send(b'END') print(f"Server: END") EOF = True time.sleep(1) # Close the connection clientSocket.close() serverSocket.close() ``` ::: - Client :::spoiler C ```C= #include <stdio.h> #include <winsock.h> int main(){ FILE *fin, *fout; fin = fopen("abc.txt", "r"); fout = fopen("client.txt","w"); SOCKET sd; WSADATA wsadata; struct sockaddr_in serv; int i,n; char str[2048]=""; 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 mode = 1; ioctlsocket(sd,FIONBIO,&mode); while(1){ char* c_line = NULL; size_t len = 0; int status = getline(&c_line,&len,fin); if (status != EOF) { send(sd,c_line, strlen(c_line)+1,0); printf("Send: %s\n",c_line); } else{ send(sd,"END", 4,0); printf("Send: END\n"); } memset(str,0,sizeof(str)); n = recv(sd,str, sizeof(str), 0); if (n > 0) { str[n] = '\0'; printf("Receive: %s\n",str); if (strcmp(str, "END\0") == 0 && status == EOF) { break; } else if (strcmp(str, "END\0") == 0) { continue; } fprintf(fout,"%s",str); } else if (n == 0){ break; } Sleep(100); } closesocket(sd); fclose(fin); fclose(fout); WSACleanup(); } ``` ::: :::spoiler Python ```Python= import socket import time """ File Input: abc.txt Output: client.txt """ fin = open("abc.txt", "r") fout = open("client.txt", "w") """ Client Information IP: 127.0.0.1 PORT: 1234 """ IP,PORT = "127.0.0.1", 1234 # Create a client socket clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) clientSocket.connect((IP,PORT)) # Set Non Blocking clientSocket.setblocking(False) EOF = False while True: try: data = clientSocket.recv(1024).decode() print(f"Server: {data}") if EOF and data == "END": break elif EOF: continue fout.write(data) except: pass try: data = fin.readline() if not data: raise EOFError clientSocket.send(data.encode()) print(f"Client: {data}") except EOFError: EOF = True print(f"Client: END") clientSocket.send(b'END') time.sleep(1) fin.close() fout.close() # Close the connection clientSocket.close() fout.close() fin.close() ``` ::: - 執行結果 ![image](https://github.com/kevin0920911/NetworkGIF/blob/main/w10/GIF/1.gif?raw=true) - 備註 - 為了知道雙方都已經傳送完畢,因此作了以下動作 - 自己輸入完成,EOF的指標會傳出True,並傳END給Server - 當收到END時要判斷自己是否也EOF ### 第四題 - 程式碼 - Sever :::spoiler C++ ```C++= #include <iostream> #include <string> #include <winsock.h> #include <vector> #include <time.h> using namespace std; int main(){ SOCKET sd; vector<SOCKET> clnt_sd; u_long mode = 1; WSADATA wsadata; struct sockaddr_in serv,clnt1,clnt2; int i,n; char str[2048]; 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"); time_t start, end; end = time(NULL); start = time(NULL); SOCKET clnt = accept(sd, (struct sockaddr *) &clnt1,&clnt_len1 ); printf("Client connected.\n"); ioctlsocket(clnt,FIONBIO,&mode); clnt_sd.push_back(clnt); clnt = accept(sd, (struct sockaddr *) &clnt1,&clnt_len1 ); printf("Client connected.\n"); ioctlsocket(clnt,FIONBIO,&mode); clnt_sd.push_back(clnt); while(1){ for (int i = 0; i<clnt_sd.size(); i++){ // Receive from client n = recv(clnt_sd[i],str, 2048, 0); if (n>0){ printf("(%i) %s\n",i+1,str); for (int j=0; j<clnt_sd.size(); j++){ if (i==j) continue; // Send to other clients send(clnt_sd[j],str, strlen(str)+1,0); } } else if (n == 0){ // Client disconnected goto end; } } } end: closesocket(sd); for (int i=0; i<clnt_sd.size(); i++) closesocket(clnt_sd[i]); WSACleanup(); } ``` ::: :::spoiler Python ```Python= import socket import time """ Server Information IP: 127.0.0.1 PORT: 1234 """ IP,PORT = "127.0.0.1", 1234 # crete a server socket serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) serverSocket.bind((IP,PORT)) serverSocket.listen(5) serverSocket.setblocking(False) # crete client socket clientSocket = [] # Waiting for connteing (5 seconds) start,end = time.time(),time.time() while end-start < 5: try: conn, addr = serverSocket.accept() print(f"Connect to {addr}") conn.setblocking(False) clientSocket.append(conn) except: pass end = time.time() time.sleep(5) breaked = False while True: for i, client in enumerate(clientSocket): try: data = client.recv(1024).decode() print(f"({i}) {data}") for j,sock in enumerate(clientSocket): if j == i: continue n = sock.send(data.encode()) if n == 0: breaked = True break if breaked: break except: pass if breaked: break for i in clientSocket: i.close() ``` ::: - Client :::spoiler C ```C= #include <stdio.h> #include <winsock.h> #include <time.h> int main(){ FILE *fin, *fout; SOCKET sd; WSADATA wsadata; struct sockaddr_in serv; int i,n; 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"); char fileName[1024]; printf("Enter file name: "); scanf("%s", fileName, sizeof(fileName)); fin = fopen(fileName, "r"); if (fin == NULL){ printf("File not found\n"); return 1; } memset(fileName,0,sizeof(fileName)); printf("Enter exports file name: "); scanf("%s", fileName, sizeof(fileName)); fout = fopen(fileName, "w"); if (fin == NULL){ printf("File not found\n"); return 1; } u_long mode = 1; ioctlsocket(sd,FIONBIO,&mode); Sleep(5000); while(1){ char* c_line = NULL; size_t len = 0; int status = getline(&c_line,&len,fin); // Read line from file char buffer[2048] = ""; if (status == EOF){ // Send END to server int n = send(sd,"END", 3+1,0); if (n == SOCKET_ERROR){ break; } printf("Send: %s\n","END"); } else{ // Send line to server send(sd,c_line, strlen(c_line)+1,0); printf("Send: %s\n",c_line); } free(c_line); n = recv(sd,buffer, 2048, 0); if (n>0){ // Receive line from server buffer[n] = '\0'; if (strcmp(buffer,"END") == 0 && status == EOF){ printf("Received: %s\n",buffer); break; } else if (strcmp(buffer,"END") == 0){ printf("Received: %s\n",buffer); continue; } else{ printf("Received: %s\n",buffer); fprintf(fout,"%s",buffer); } } else if (n == 0){ break; } Sleep(1000); } closesocket(sd); fclose(fin); fclose(fout); WSACleanup(); } ``` ::: :::spoiler Python ```Python= import socket import time """ Server Information IP: 127.0.0.1 PORT: 1234 """ IP,PORT = "127.0.0.1", 1234 # crete a server socket serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) serverSocket.bind((IP,PORT)) serverSocket.listen(5) serverSocket.setblocking(False) # crete client socket clientSocket = [] # Waiting for connteing (5 seconds) start,end = time.time(),time.time() while end-start < 5: try: conn, addr = serverSocket.accept() print(f"Connect to {addr}") conn.setblocking(False) clientSocket.append(conn) except: pass end = time.time() time.sleep(5) breaked = False while True: for i, client in enumerate(clientSocket): try: data = client.recv(1024).decode() print(f"({i}) {data}") for j,sock in enumerate(clientSocket): if j == i: continue n = sock.send(data.encode()) if n == 0: breaked = True break if breaked: break except: pass if breaked: break for i in clientSocket: i.close() ``` ::: - 執行結果 ![image](https://github.com/kevin0920911/NetworkGIF/blob/main/w10/GIF/1.gif?raw=true) ### 第五題 - 程式碼 - Sever :::spoiler C++ ```C++= #include <stdio.h> #include <winsock.h> #include <vector> using namespace std; int main(){ SOCKET sd; vector<SOCKET> clientsd; 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); int clnt_len=sizeof(clnt); printf("Server waits.\n"); SOCKET clnt_sd = accept(sd, (struct sockaddr *) &clnt,&clnt_len ); printf("Client is connected.\n"); clientsd.push_back(clnt_sd); u_long mode = 1; ioctlsocket(sd,FIONBIO,&mode); while(1){ clnt_sd = accept(sd, (struct sockaddr *) &clnt,&clnt_len ); if (clnt_sd != INVALID_SOCKET){ clientsd.push_back(clnt_sd); } vector<int> disconnect; for (int i = 0; i<clientsd.size();i++){ int n = send(clientsd[i],str, strlen(str)+1,0); if (n <= 0){ disconnect.push_back(i); printf("Client %d is disconnected.\n",i); } } for (auto i:disconnect){ closesocket(clientsd[i]); clientsd.erase(clientsd.begin()+i); } printf("(server send): %s\n",str); Sleep(1000); } closesocket(sd); for (int i = 0; i<clientsd.size();i++){ closesocket(clientsd[i]); } WSACleanup(); system("pause"); } ``` ::: :::spoiler Python ```Python= import socket import time """ The Data I want to send to the clinet """ DATA = "Something Smell" FREQ = 1 """ Server Information IP: 127.0.0.1 PORT: 1234 """ IP,PORT = "127.0.0.1", 1234 # crete server socket serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) serverSocket.bind((IP,PORT)) serverSocket.listen(5) # A list for client socket clientSocket = [] sd,addr = serverSocket.accept() clientSocket.append(sd) print(f"Client {addr} connected") serverSocket.setblocking(False) while True: try: sd, addr = serverSocket.accept() clientSocket.append(sd) print(f"Client {addr} connected") except: pass disconnect = [] for i,conn in enumerate(clientSocket): try: conn.send(DATA.encode()) except ConnectionResetError: disconnect.append(i) for i in disconnect: clientSocket[i].close() clientSocket.pop(i) print(f"Client {i} disconnected") time.sleep(FREQ) for i in clientSocket: i.close() ``` ::: - Client :::spoiler C++ ```C++= #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("(from server): %s\n",str); } closesocket(sd); WSACleanup(); } ``` ::: :::spoiler Python ```Python= import socket """ Server Information IP: 127.0.0.1 PORT: 1234 """ IP,PORT = "127.0.0.1", 1234 # crete a socket connet to server sd = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sd.connect((IP,PORT)) print(f"Connect to server {IP}:{PORT}") while True: data = sd.recv(1024).decode() print(f"Server: {data}") sd.close() ``` ::: - 執行結果 ![image](https://github.com/kevin0920911/NetworkGIF/blob/main/w10/GIF/3.gif?raw=true) - 備註 - 當Server因為對方關閉連線,並將該連線踢出list中 ## 心得 這周作業交了non blocking mode,感覺他像是一個有不耐煩的人,沒收到就直接回傳-1,雖然更有彈性了,但是要找霸哥 aka bug,就很困難。 這周作業感覺變難做了,感覺要考慮很多情況,而且加上非阻攔模式下有夠難debug,我發現錯誤時一直拿wireshark,發明這個的人真電。 在寫作業其實有遇到一個小問題,我以為Python也要像C那樣去看recv回傳的結果,結果會報錯,原來要改成try/except,雖然小麻煩,但我覺得比C易讀很多。 ## 附錄 [檔案的連結](https://github.com/kevin0920911/NetworkGIF/tree/main/w10)