# 網路程式設計 hw4
## 1. 使用wireshark抓封包
- 請開兩個idle分別執行python echo client與server,client會連至server並傳送封包後結束,請抓下將此過程中所有的封包,請將封包分類(建立連線/傳送資料/結束連線),並統計總共的封包數。
- 建立連線
> 製造了3個封包

- 傳送資料
> 製造了4(一次共四個)*5(5次hello) = 20

- 結束連線
> 製造了四個封包

- 因此一共製造了27個封包
### 步驟
- STEP1:打開wireShark,並使用Adapter for loopback traffic capture(只要抓locahost就好)

- STEP2:開始監聽

- STEP3:打開你的terminal(要兩個喔)
- 一個執行`python server.py`
- 一個執行`python client.py`
:::info
###### 溫腥小提醒
記得先開server.py,不然client ping不到會直接跳出
:::

## 2. 實作Simple TCP/IP Server
- 影片中的server範例為echo server
- 請將範例的Echo Server修改成為另外四種server
- 丟棄服務 (Discard)
- 日期時間服務 (Daytime)
- 每日一句 (QUOTE)
- 字元產生器 (CHARGEN)
- 答案有四個server程式
:::warning
###### 溫腥小提醒
Server不要用記得關掉!
Server不要用記得關掉!
Server不要用記得關掉!
Server不要用記得關掉!
Server不要用記得關掉!
Server不要用記得關掉!
Server不要用記得關掉!
Server不要用記得關掉!
我試過了,port會互相占用,你甚麼都測試不了,別怪我沒有警告
:::
### Discard Server
:::info
###### 作業說明
- 當client連入後,server一直不斷接收,並印出接收的內容,但不會回傳
- 當client輸入.(英文句點)時,server予以斷線
- 斷線後,會重新等待下一個client連入
:::
- 程式碼
:::spoiler C
```C++=
#include <stdio.h>
#include <winsock.h>
int main(){
// Initial winsock
WSADATA wsadata;
WSAStartup(0x101,&wsadata);
char str[100];
//Create Socket for Server and Client
SOCKET servSock,clntSock;
struct sockaddr_in serv,clnt;
//Initial Server Socket
servSock = socket(AF_INET,SOCK_STREAM,0);
//Initial Server Information
serv.sin_family = AF_INET;
serv.sin_port = htons(1234);
serv.sin_addr.s_addr = inet_addr("127.0.0.1");
//Get Socket from my host
bind(servSock, (struct sockaddr *) &serv, sizeof(serv));
//Start Service
int clnt_len=sizeof(clnt);
//Waiting for connection
listen(servSock,5);//Allow 5 people in this queue
while(1){
printf("Server waits.\n");
//Get Connect from client
clntSock = accept(servSock, (struct sockaddr *) &clnt,&clnt_len );
//Receive data from client
while(1){
int size = recv(clntSock,str, 100, 0);
str[size] = '\0';
if(str[0] == '.'){
break;
}
printf("Server recveive:%s\n",str);
};
closesocket(clntSock);
}
closesocket(servSock);
WSACleanup();
return 0;
}
```
:::
:::spoiler Python
```python=
# -*- coding: utf-8 -*-
import socket
HOST = '127.0.0.1'
PORT = 1234
#socket set up
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((HOST, PORT))
s.listen(5)
print('server start at: %s:%s' % (HOST, PORT))
while True:
print('wait for connection...')
#connection
conn, addr = s.accept()
print('connected by ' + str(addr))
#recrive data from client
while True:
indata = conn.recv(1024)
if indata.decode() == ".": # connection closed
conn.close()
break
#discard data
print(f"Server recveive: {indata.decode()}")
s.close()
```
:::
- 執行結果

### Time Server
:::info
###### 作業說明
- 當client連入後,server立即印出並回傳server系統時間,之後就斷線
- 斷線後,會重新等待下一個client連入
:::
- 程式碼
:::spoiler C
```C++=
#include <stdio.h>
#include <winsock.h>
#include "time.h"
int main(){
// Initial winsock
WSADATA wsadata;
WSAStartup(0x101,&wsadata);
//Create Socket for Server and Client
SOCKET servSock,clntSock;
struct sockaddr_in serv,clnt;
//Initial Server Socket
servSock = socket(AF_INET,SOCK_STREAM,0);
//Initial Server Information
serv.sin_family = AF_INET;
serv.sin_port = htons(1234);
serv.sin_addr.s_addr = inet_addr("127.0.0.1");
//Get Socket from my host
bind(servSock, (struct sockaddr *) &serv, sizeof(serv));
//Start Service
int clnt_len=sizeof(clnt);
//Waiting for connection
listen(servSock,5);//Allow 5 people in this queue
while(1){
printf("Server waits.\n");
//Get time
time_t rawtime;
struct tm * timeinfo;
time ( &rawtime );
timeinfo = localtime ( &rawtime );
//Get Connect from client
clntSock = accept(servSock, (struct sockaddr *) &clnt,&clnt_len );
printf("Server Send:%s\n",asctime(timeinfo));
send(clntSock,asctime(timeinfo), strlen(asctime(timeinfo)) + 1 ,0);
closesocket(clntSock);
}
closesocket(servSock);
WSACleanup();
return 0;
}
```
:::
:::spoiler Python
```python=
# -*- coding: utf-8 -*-
import socket
import time
HOST = '127.0.0.1'
PORT = 1234
#socket set up
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((HOST, PORT))
s.listen(5)
print('server start at: %s:%s' % (HOST, PORT))
while True:
print('wait for connection...')
#client connection
conn, addr = s.accept()
print('connected by ' + str(addr))
outdata = time.ctime()
#send data
conn.send(outdata.encode())
conn.close()
print(f"Server send: {outdata}")
s.close()
```
:::
- 筆記
1. C中有time.h函數庫可以做到回傳目前時間,方式如下
```C++=
time_t rawtime;
struct tm * timeinfo;
time ( &rawtime );
timeinfo = localtime ( &rawtime );
```
2. 拿到`rawtime`後可以使用`asctime()`回傳字元陣列
- 執行結果

### Quote Server
:::info
###### 作業說明
- 當client連入後,server立即印出並回傳佳句名言,之後就斷線
- 斷線後,會重新等待下一個client連入
:::
- 程式碼
:::spoiler C
```C++=
#include <stdio.h>
#include <random>
#include <winsock.h>
char Quote[10][1024]={
"Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live. (Martin Golding)\n",
"One man's crappy software is another man's full time job. (Jessica Gaston)\n",
"If at first you don't succeed, call it version 1.0 (unknown)\n",
"All programmers are playwrights and all computers are lousy actors. (Anonymous Hack Actor)\n",
"Any code of your own that you haven't looked at for six or more months might as well have been written by someone else. (Eagleson's law)\n",
"Commenting your code is like cleaning your bathroom — you never want to do it, but it really does create a more pleasant experience for you and your guests. (Ryan Campbell)\n",
"In order to understand recursion, one must first understand recursion. (Anonymous)\n",
"The best thing about a boolean is even if you are wrong, you are only off by a bit.\n",
"Should array indices start at 0 or 1? My compromise of 0.5 was rejected without, I thought, proper consideration. (Stan Kelly-Bootle)\n",
"Computers are good at following instructions, but not at reading your mind. (Donald Knuth)\n"
};
int main(){
// Initial winsock
WSADATA wsadata;
WSAStartup(0x101,&wsadata);
char str[100];
//Create Socket for Server and Client
SOCKET servSock,clntSock;
struct sockaddr_in serv,clnt;
//Initial Server Socket
servSock = socket(AF_INET,SOCK_STREAM,0);
//Initial Server Information
serv.sin_family = AF_INET;
serv.sin_port = htons(1234);
serv.sin_addr.s_addr = inet_addr("127.0.0.1");
//Get Socket from my host
bind(servSock, (struct sockaddr *) &serv, sizeof(serv));
//Start Service
int clnt_len=sizeof(clnt);
//Waiting for connection
listen(servSock,5);//Allow 5 people in this queue
while(1){
printf("Server waits.\n");
//Get Connect from client
clntSock = accept(servSock, (struct sockaddr *) &clnt,&clnt_len );
int random = rand()%10;
printf("Server Send: %s\n",Quote[random]);
send(clntSock,Quote[random], strlen(Quote[random]) ,0);
closesocket(clntSock);
}
closesocket(servSock);
WSACleanup();
return 0;
}
```
:::
:::spoiler Python
```python=
# -*- coding: utf-8 -*-
import socket
import random
QUOTE = [
"Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live. (Martin Golding)",
"One man's crappy software is another man's full time job. (Jessica Gaston)",
"If at first you don't succeed, call it version 1.0 (unknown)",
"All programmers are playwrights and all computers are lousy actors. (Anonymous Hack Actor)",
"Any code of your own that you haven't looked at for six or more months might as well have been written by someone else. (Eagleson's law)",
"Commenting your code is like cleaning your bathroom — you never want to do it, but it really does create a more pleasant experience for you and your guests. (Ryan Campbell)",
"In order to understand recursion, one must first understand recursion. (Anonymous)",
"The best thing about a boolean is even if you are wrong, you are only off by a bit.",
"Should array indices start at 0 or 1? My compromise of 0.5 was rejected without, I thought, proper consideration. (Stan Kelly-Bootle)",
"Computers are good at following instructions, but not at reading your mind. (Donald Knuth)"
]
HOST = '127.0.0.1'
PORT = 1234
#sockect set up
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((HOST, PORT))
s.listen(5)
print('server start at: %s:%s' % (HOST, PORT))
while True:
print('wait for connection...')
#connection from client
conn, addr = s.accept()
print('connected by ' + str(addr))
outdata = QUOTE[random.randrange(10)]
#send data
conn.send(outdata.encode())
conn.close()
print(f"Server send: {outdata}")
s.close()
```
:::
- 執行結果

### CharGen Server
:::info
###### 作業說明
- 當client連入後,server立即印出並一直重複回傳
- 斷線後,會重新等待下一個client連入
:::
- 程式碼
:::spoiler C
```C++=
#include <stdio.h>
#include <winsock.h>
int main(){
// Initial winsock
WSADATA wsadata;
WSAStartup(0x101,&wsadata);
//Create Socket for Server and Client
SOCKET servSock,clntSock;
struct sockaddr_in serv,clnt;
//Initial Server Socket
servSock = socket(AF_INET,SOCK_STREAM,0);
//Initial Server Information
serv.sin_family = AF_INET;
serv.sin_port = htons(1234);
serv.sin_addr.s_addr = inet_addr("127.0.0.1");
//Get Socket from my host
bind(servSock, (struct sockaddr *) &serv, sizeof(serv));
//Start Service
int clnt_len=sizeof(clnt);
//Waiting for connection
listen(servSock,5);//Allow 5 people in this queue
while(1){
printf("Server waits.\n");
//Get Connect from client
clntSock = accept(servSock, (struct sockaddr *) &clnt,&clnt_len );
char str[1024] = "!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefg";
while (1){
printf("Server Send:%s\n",str);
int result = send(clntSock,str, strlen(str) + 1 ,0);
if (result == SOCKET_ERROR ){
break;
}
}
closesocket(clntSock);
}
closesocket(servSock);
WSACleanup();
return 0;
}
```
:::
:::spoiler Python
```python=
# -*- coding: utf-8 -*-
import socket
HOST = '127.0.0.1'
PORT = 1234
CHAR = "!#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefg"
#sockect set up
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((HOST, PORT))
s.listen(5)
print('server start at: %s:%s' % (HOST, PORT))
while True:
print('wait for connection...')
#connect
conn, addr = s.accept()
print('connected by ' + str(addr))
while True:
#send data, until client connection close
try:
conn.send(CHAR.encode())
print(f"Server send: {CHAR}")
except:
#client close its connection
break
conn.close()
s.close()
```
:::
- 筆記
1. 其實send的時候也會有回傳值回傳連線狀態(正常時事回傳字串長度,不正常時回傳SOCKET_ERROR)
2. 所以透過回傳值我們可以判斷與連線狀態,當error,直接break迴圈即可
- 執行結果

## 心得
今天學習了Server怎麼建立,好好玩,之前網際網路服務只有學過怎麼用XAMPP,建立網頁Server,這次學到如何建立一個Server流程,感覺更加了解Server的運作。
作業的部分,這次寫了四種伺服器,我覺得比較有趣的是CharGen Server,我那時思考如何斷線後Server還可以繼續服務下一位Client,結果就發現原來send有回傳值可以使用,真棒!
## 附錄
[全部的程式碼檔案](https://github.com/kevin0920911/NetworkGIF/tree/main/w4)