# 網路程式設計 HW08 傳送接收檔案作業
學生: 1084837廖經翰
## 練習1. 測試TCP (1)若兩方都先送後收,可以運作嗎? (2)若兩方都先收後送,可以運作嗎?
### (1)若兩方都先送後收,可以運作嗎?
這次的作業練習1的第一個測試是當client端與server端都先傳送資訊給雙方,接著在接收雙方給的資訊,這樣是否可以正常運作? 答案是可以,因為TCP的資料是類似stream(串流),當send()出去後資訊會放入待送buffer裡面,而當recv()的時候則是讀取buffer裡面的資訊,所以當雙方都先傳送的時候,雙方的資訊都會保留在buffer裡面,接著雙方在接收的時候就會從buffer裡面讀取資訊,這樣就可以正常的傳送接收資訊
<center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/11hrZ9V.png" /></center>
<center>TCP的stream(串流)</center>
<br>
執行畫面:
<center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/4AW42Sz.png" /></center>
<center>server</center>
<br>
<center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/a6Im2Qb.png" /></center>
<center>client</center>
<br>
server程式碼:
```C=
#include<stdio.h>
#include<winsock.h>
#define MAXSIZE 1024
int main()
{
//set WSA and initial WSAStartup
SOCKET sd , clnt_sd;
WSADATA wsadata;
struct sockaddr_in serv , clnt;
int i , n;
char str[1024];
char str1[100]="I love Algorithm!";
//設定server的結構
serv.sin_addr.s_addr = inet_addr( "127.0.0.1" );
serv.sin_family = AF_INET;
serv.sin_port = htons( 1234 );
//呼叫WSAStartup()註冊WinSock DLL的使用
WSAStartup( 0x101 , (LPWSADATA) &wsadata );
//使用TCP協定
sd = socket( AF_INET , SOCK_STREAM , 0 );
printf( "sd = %d\n" , sd );
int len_serv = sizeof( serv );
//綁定server結構裡設定的資訊
n = bind( sd , (struct sockaddr*) &serv , len_serv );
printf( "bind(): %d\n" , n );
n = listen( sd , 5 );
printf( "listen(): %d\n" , n );
int len_clnt = sizeof( clnt );
clnt_sd = accept( sd , (struct sockaddr*) &clnt , &len_clnt );
send( clnt_sd , str1 , strlen(str1) , 0 );
printf( "send: %s (%d)\n" , str1 , n );
n = recv( clnt_sd , str , MAXSIZE , 0 );
str[n] = '\0';
printf( "recv: %s (%d)\n" , str , n );
//關閉TCP socket
closesocket(sd);
closesocket(clnt_sd);
//結束WinSock DLL的使用
WSACleanup();
system("pause");
}
```
client程式碼:
```C=
#include<stdio.h>
#include<winsock.h>
int main()
{
//set WSA and initial WSAStartup
SOCKET sd;
WSADATA wsadata;
struct sockaddr_in serv;
int i , n;
char str[1024];
char str1[1024]="I love NP!";
//設定Server的結構
serv.sin_addr.s_addr = inet_addr( "127.0.0.1" );
serv.sin_family = AF_INET;
serv.sin_port = htons( 1234 );
//呼叫WSAStartup()註冊WinSock DLL的使用
WSAStartup( 0x101 , (LPWSADATA) &wsadata );
//使用TCP協定
sd = socket( AF_INET , SOCK_STREAM , 0 );
printf( "sd = %d\n" , sd );
int len_serv = sizeof( serv );
//因使用send(),所以在傳送之前要先連接至server,連接之後才能傳送資訊
n = connect( sd , (struct sockaddr*) &serv , len_serv );
printf( "connect(): %d\n" , n );
n = send( sd , str1 , strlen(str1) , 0 );
printf( "send: %s (%d)\n" , str1 , n );
n = recv( sd , str , 1024 , 0 );
str[n] = '\0';
printf( "recv: %s (%d)\n" , str , n );
//關閉TCP socket
closesocket(sd);
//結束WinSock DLL的使用
WSACleanup();
system("pause");
}
```
### (2)若兩方都先收後送,可以運作嗎?
作業練習1的第二個測試是當client端與server端都先接收雙方的資訊,接著在傳送資訊給雙方,這樣是否可以正常運作? 答案是不行,因為當雙方一開始都先接收資訊的時候,雙方就去讀取buffer裡面是否有資訊,如果有就取出,如果沒有就會一直等待直到資訊進入buffer,但是雙方都沒有傳送資訊,表示雙方的buffer都是空的,這樣雙方就會一直在等待資訊的狀態,導致程式不能正常的傳送接收資訊
執行畫面:
<center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/bowVpKr.png" /></center>
<center>server</center>
<br>
<center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/boGnmw1.png" /></center>
<center>client</center>
<br>
server程式碼:
```C=
#include<stdio.h>
#include<winsock.h>
#define MAXSIZE 1024
int main()
{
//set WSA and initial WSAStartup
SOCKET sd , clnt_sd;
WSADATA wsadata;
struct sockaddr_in serv , clnt;
int i , n;
char str[1024];
char str1[100]="I love Algorithm!";
//設定server的結構
serv.sin_addr.s_addr = inet_addr( "127.0.0.1" );
serv.sin_family = AF_INET;
serv.sin_port = htons( 1234 );
//呼叫WSAStartup()註冊WinSock DLL的使用
WSAStartup( 0x101 , (LPWSADATA) &wsadata );
//使用TCP協定
sd = socket( AF_INET , SOCK_STREAM , 0 );
printf( "sd = %d\n" , sd );
int len_serv = sizeof( serv );
//綁定server結構裡設定的資訊
n = bind( sd , (struct sockaddr*) &serv , len_serv );
printf( "bind(): %d\n" , n );
n = listen( sd , 5 );
printf( "listen(): %d\n" , n );
int len_clnt = sizeof( clnt );
clnt_sd = accept( sd , (struct sockaddr*) &clnt , &len_clnt );
n = recv( clnt_sd , str , MAXSIZE , 0 );
str[n] = '\0';
printf( "recv: %s (%d)\n" , str , n );
send( clnt_sd , str1 , strlen(str1) , 0 );
printf( "send: %s (%d)\n" , str1 , n );
//關閉TCP socket
closesocket(sd);
closesocket(clnt_sd);
//結束WinSock DLL的使用
WSACleanup();
system("pause");
}
```
client程式碼:
```C=
#include<stdio.h>
#include<winsock.h>
int main()
{
//set WSA and initial WSAStartup
SOCKET sd;
WSADATA wsadata;
struct sockaddr_in serv;
int i , n;
char str[1024];
char str1[1024]="I love NP!";
//設定Server的結構
serv.sin_addr.s_addr = inet_addr( "127.0.0.1" );
serv.sin_family = AF_INET;
serv.sin_port = htons( 1234 );
//呼叫WSAStartup()註冊WinSock DLL的使用
WSAStartup( 0x101 , (LPWSADATA) &wsadata );
//使用TCP協定
sd = socket( AF_INET , SOCK_STREAM , 0 );
printf( "sd = %d\n" , sd );
int len_serv = sizeof( serv );
//因使用send(),所以在傳送之前要先連接至server,連接之後才能傳送資訊
n = connect( sd , (struct sockaddr*) &serv , len_serv );
printf( "connect(): %d\n" , n );
n = recv( sd , str , 1024 , 0 );
str[n] = '\0';
printf( "recv: %s (%d)\n" , str , n );
n = send( sd , str1 , strlen(str1) , 0 );
printf( "send: %s (%d)\n" , str1 , n );
//關閉TCP socket
closesocket(sd);
//結束WinSock DLL的使用
WSACleanup();
system("pause");
}
```
## 練習2. 完成TCP (1)送一次,收多次 (2)送多次,收一次
### (1)送一次,收多次
作業練習2的第一個練習是當client端傳送一次資訊給server端,然後server端接收很多次資訊,client端一次要傳送 [NCYU][NCYU][NCYU][NCYU][NCYU] 給server端,然後server端一次只接收6個Byte,所以server端要接收五次才能順利接收到完整的資料
執行畫面:
<center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/22JuRrc.png" /></center>
<center>server</center>
<br>
<center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/iU97qgs.png" /></center>
<center>client</center>
<br>
server程式碼:
```C=
#include<stdio.h>
#include<winsock.h>
int main()
{
//set WSA and initial WSAStartup
SOCKET sd , clnt_sd;
WSADATA wsadata;
struct sockaddr_in serv , clnt;
int i , n;
char str[100];
//呼叫WSAStartup()註冊WinSock DLL的使用
WSAStartup( 0x101 , &wsadata );
//使用TCP協定
sd = socket( AF_INET , SOCK_STREAM , 0 );
//設定server的結構
serv.sin_addr.s_addr = inet_addr( "127.0.0.1" );
serv.sin_family = AF_INET;
serv.sin_port = htons( 1234 );
//綁定server結構裡設定的資訊
bind( sd , (struct sockaddr*) &serv , sizeof(serv) );
listen( sd , 5 );
int len_clnt = sizeof( clnt );
printf( "Server waits...\n");
clnt_sd = accept( sd , (struct sockaddr*) &clnt , &len_clnt );
while(1)
{
n = recv( clnt_sd , str , 6 , 0 );
if( n == 0 )
{
break;
}
printf( "Server receive: %s , byte: %d\n" , str , n );
}
//關閉TCP socket
closesocket(sd);
closesocket(clnt_sd);
//結束WinSock DLL的使用
WSACleanup();
system("pause");
}
```
client程式碼:
```C=
#include<stdio.h>
#include<winsock.h>
int main()
{
//set WSA and initial WSAStartup
SOCKET sd;
WSADATA wsadata;
struct sockaddr_in serv;
int i , n;
char str[100] = "[NCYU][NCYU][NCYU][NCYU][NCYU]";
//呼叫WSAStartup()註冊WinSock DLL的使用
WSAStartup( 0x101 , &wsadata );
//使用TCP協定
sd = socket( AF_INET , SOCK_STREAM , 0 );
//設定Server的結構
serv.sin_addr.s_addr = inet_addr( "127.0.0.1" );
serv.sin_family = AF_INET;
serv.sin_port = htons( 1234 );
//因使用send(),所以在傳送之前要先連接至server,連接之後才能傳送資訊
connect( sd , (struct sockaddr*) &serv , sizeof(serv) );
send( sd , str , strlen(str) , 0 );
//關閉TCP socket
closesocket(sd);
//結束WinSock DLL的使用
WSACleanup();
system("pause");
}
```
### (2)送多次,收一次
作業練習2的第二個練習是當client端傳送多次資訊給server端,然後server端接收一次資訊,client端要傳送五次 [NCYU] 給server端,然後server端要一次接收全部的資訊
執行畫面:
<center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/84CQX2I.png" /></center>
<center>server</center>
<br>
<center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/JNDGndz.png" /></center>
<center>client</center>
<br>
server程式碼:
```C=
#include<stdio.h>
#include<winsock.h>
int main()
{
//set WSA and initial WSAStartup
SOCKET sd , clnt_sd;
WSADATA wsadata;
struct sockaddr_in serv , clnt;
int i , n;
char str[100];
//呼叫WSAStartup()註冊WinSock DLL的使用
WSAStartup( 0x101 , &wsadata );
//使用TCP協定
sd = socket( AF_INET , SOCK_STREAM , 0 );
//設定server的結構
serv.sin_addr.s_addr = inet_addr( "127.0.0.1" );
serv.sin_family = AF_INET;
serv.sin_port = htons( 1234 );
//綁定server結構裡設定的資訊
bind( sd , (struct sockaddr*) &serv , sizeof(serv) );
listen( sd , 5 );
int len_clnt = sizeof( clnt );
printf( "Server waits...\n");
clnt_sd = accept( sd , (struct sockaddr*) &clnt , &len_clnt );
//等待一段時間,讓封包都到齊之後再接收
Sleep(1000);
n = recv( clnt_sd , str , 100 , 0 );
printf( "Server receive: %s , byte: %d\n" , str , n );
//關閉TCP socket
closesocket(sd);
closesocket(clnt_sd);
//結束WinSock DLL的使用
WSACleanup();
system("pause");
}
```
client程式碼:
```C=
#include<stdio.h>
#include<winsock.h>
int main()
{
//set WSA and initial WSAStartup
SOCKET sd;
WSADATA wsadata;
struct sockaddr_in serv;
int i , n;
char str[100] = "[NCYU]";
//呼叫WSAStartup()註冊WinSock DLL的使用
WSAStartup( 0x101 , &wsadata );
//使用TCP協定
sd = socket( AF_INET , SOCK_STREAM , 0 );
//設定Server的結構
serv.sin_addr.s_addr = inet_addr( "127.0.0.1" );
serv.sin_family = AF_INET;
serv.sin_port = htons( 1234 );
//因使用send(),所以在傳送之前要先連接至server,連接之後才能傳送資訊
connect( sd , (struct sockaddr*) &serv , sizeof(serv) );
for( int i = 0 ; i < 5 ; i++ )
{
send( sd , str , strlen(str) , 0 );
}
//關閉TCP socket
closesocket(sd);
//結束WinSock DLL的使用
WSACleanup();
system("pause");
}
```
## 練習3. 完成UDP成對收送的實驗
作業練習3是利用UDP當client端傳送多次資訊給server端,然後server端接收多次資訊,client端要傳送十次 [NCYU] 給server端,然後server端要多次接收,要接收10次才能接收全部的資訊
執行畫面:
<center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/ueCN3Gz.png" /></center>
<center>server</center>
<br>
<center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/daODFru.png" /></center>
<center>client</center>
<br>
server程式碼:
```C=
#include<stdio.h>
#include<winsock.h>
int main()
{
//set WSA and initial WSAStartup
SOCKET sd , clnt_sd;
WSADATA wsadata;
struct sockaddr_in serv , clnt;
int i , n;
char str[100];
//呼叫WSAStartup()註冊WinSock DLL的使用
WSAStartup( 0x101 , &wsadata );
//使用UDP協定
sd = socket( AF_INET , SOCK_DGRAM , 0 );
//設定server的結構
serv.sin_addr.s_addr = inet_addr( "127.0.0.1" );
serv.sin_family = AF_INET;
serv.sin_port = htons( 1234 );
//綁定server結構裡設定的資訊
bind( sd , (struct sockaddr*) &serv , sizeof(serv) );
int len_clnt = sizeof( clnt );
printf( "Server waits...\n");
Sleep(1000);
while(1)
{
n = recv( sd , str , 100 , 0 );
printf( "Server receive: %s , byte: %d\n" , str , n );
Sleep(1000);
}
//關閉TCP socket
closesocket(sd);
//結束WinSock DLL的使用
WSACleanup();
system("pause");
}
```
client程式碼:
```C=
#include<stdio.h>
#include<winsock.h>
int main()
{
//set WSA and initial WSAStartup
SOCKET sd;
WSADATA wsadata;
struct sockaddr_in serv;
int i , n;
char str[100] = "[NCYU]";
//呼叫WSAStartup()註冊WinSock DLL的使用
WSAStartup( 0x101 , &wsadata );
//使用UDP協定
sd = socket( AF_INET , SOCK_DGRAM , 0 );
//設定Server的結構
serv.sin_addr.s_addr = inet_addr( "127.0.0.1" );
serv.sin_family = AF_INET;
serv.sin_port = htons( 1234 );
//因使用send(),所以在傳送之前要先連接至server,連接之後才能傳送資訊
connect( sd , (struct sockaddr*) &serv , sizeof(serv) );
for( int i = 0 ; i < 10 ; i++ )
{
send( sd , str , strlen(str) , 0 );
}
//關閉TCP socket
closesocket(sd);
//結束WinSock DLL的使用
WSACleanup();
system("pause");
}
```
## 練習4. 完成單次傳送文字檔的實驗
作業練習4是利用TCP來讓client端單次傳送文字檔 in.txt 給server端,然後server端接收到 in.txt 之後再去讀取 in.txt 並把裡面的內容寫入 out.txt 裡面
執行畫面:
<center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/UE9dnxy.png" /></center>
<center>server</center>
<br>
<center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/PpiNH7A.png" /></center>
<center>client</center>
<br>
<center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/iXLICk4.png" /></center>
<center>in.txt</center>
<br>
<center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/rNsPHRQ.png" /></center>
<center>out.txt</center>
<br>
server程式碼:
```C=
#include<stdio.h>
#include<winsock.h>
#define MAXSIZE 1024
int main()
{
//set WSA and initial WSAStartup
SOCKET sd , clnt_sd;
WSADATA wsadata;
struct sockaddr_in serv , clnt;
int i , n;
char str[1024];
FILE *fout;
//讀取檔案
fout = fopen( "out.txt" , "w+t" );
//設定server的結構
serv.sin_addr.s_addr = inet_addr( "127.0.0.1" );
serv.sin_family = AF_INET;
serv.sin_port = htons( 1234 );
//呼叫WSAStartup()註冊WinSock DLL的使用
WSAStartup( 0x101 , (LPWSADATA) &wsadata );
//使用TCP協定
sd = socket( AF_INET , SOCK_STREAM , 0 );
printf( "sd = %d\n" , sd );
int len_serv = sizeof( serv );
//綁定server結構裡設定的資訊
n = bind( sd , (struct sockaddr*) &serv , len_serv );
printf( "bind(): %d\n" , n );
n = listen( sd , 5 );
printf( "listen(): %d\n" , n );
int len_clnt = sizeof( clnt );
clnt_sd = accept( sd , (struct sockaddr*) &clnt , &len_clnt );
n = recv( clnt_sd , str , MAXSIZE , 0 );
str[n] = '\0';
printf( "recv: %s (%d)\n" , str , n );
fputs( str , fout );
fclose( fout );
//關閉TCP socket
closesocket(sd);
closesocket(clnt_sd);
//結束WinSock DLL的使用
WSACleanup();
system("pause");
}
```
client程式碼:
```C=
#include<stdio.h>
#include<winsock.h>
#define MAXSIZE 1024
int main()
{
//set WSA and initial WSAStartup
SOCKET sd;
WSADATA wsadata;
struct sockaddr_in serv;
int i , n;
char str[MAXSIZE];
FILE *fin;
//讀取檔案
fin = fopen( "in.txt" , "rt" );
fgets( str , MAXSIZE , fin );
printf( "len: %d\n" , strlen(str) );
puts(str);
//設定Server的結構
serv.sin_addr.s_addr = inet_addr( "127.0.0.1" );
serv.sin_family = AF_INET;
serv.sin_port = htons( 1234 );
//呼叫WSAStartup()註冊WinSock DLL的使用
WSAStartup( 0x101 , (LPWSADATA) &wsadata );
//使用TCP協定
sd = socket( AF_INET , SOCK_STREAM , 0 );
printf( "sd = %d\n" , sd );
int len_serv = sizeof( serv );
//因使用send(),所以在傳送之前要先連接至server,連接之後才能傳送資訊
n = connect( sd , (struct sockaddr*) &serv , len_serv );
printf( "connect(): %d\n" , n );
n = send( sd , str , strlen(str) , 0 );
printf( "send: %s (%d)\n" , str , n );
//關閉TCP socket
closesocket(sd);
//結束WinSock DLL的使用
WSACleanup();
fclose(fin);
system("pause");
}
```
in.txt:
```txt=
123456789
```
out.txt:
```txt=
123456789
```
## 練習5. 完成連續傳送大量文字檔的實驗
作業練習5是利用TCP來讓client端連續傳送大量文字檔 in.txt 給server端,然後server端接收到 in.txt 之後再去讀取 in.txt 並把裡面的內容寫入 out.txt 裡面
執行畫面:
<center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/ZEOwnOy.png" /></center>
<center>server</center>
<br>
<center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/7MvooMG.png" /></center>
<center>client</center>
<br>
<center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/XXPcLVr.png" /></center>
<center>in.txt</center>
<br>
<center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/QbGJKT1.png" /></center>
<center>out.txt</center>
<br>
server程式碼:
```C=
#include<stdio.h>
#include<winsock.h>
#define MAXSIZE 1024
int main()
{
//set WSA and initial WSAStartup
SOCKET sd , clnt_sd;
WSADATA wsadata;
struct sockaddr_in serv , clnt;
int i , n;
char str[1024];
FILE *fout;
//讀取檔案
fout = fopen( "out.txt" , "w+t" );
//設定server的結構
serv.sin_addr.s_addr = inet_addr( "127.0.0.1" );
serv.sin_family = AF_INET;
serv.sin_port = htons( 1234 );
//呼叫WSAStartup()註冊WinSock DLL的使用
WSAStartup( 0x101 , (LPWSADATA) &wsadata );
//使用TCP協定
sd = socket( AF_INET , SOCK_STREAM , 0 );
printf( "sd = %d\n" , sd );
int len_serv = sizeof( serv );
//綁定server結構裡設定的資訊
n = bind( sd , (struct sockaddr*) &serv , len_serv );
printf( "bind(): %d\n" , n );
n = listen( sd , 5 );
printf( "listen(): %d\n" , n );
int len_clnt = sizeof( clnt );
clnt_sd = accept( sd , (struct sockaddr*) &clnt , &len_clnt );
while( ( n = recv( clnt_sd , str , MAXSIZE , 0 ) ) != 0 )
{
str[n] = '\0';
fputs( str , fout );
}
fclose( fout );
//關閉TCP socket
closesocket(sd);
closesocket(clnt_sd);
//結束WinSock DLL的使用
WSACleanup();
system("pause");
}
```
client程式碼:
```C=
#include<stdio.h>
#include<winsock.h>
int main()
{
//set WSA and initial WSAStartup
SOCKET sd;
WSADATA wsadata;
struct sockaddr_in serv;
int i , n;
char str[1024]="I love Life";
FILE *fin;
//讀取檔案
fin = fopen( "in.txt" , "rt" );
//設定Server的結構
serv.sin_addr.s_addr = inet_addr( "127.0.0.1" );
serv.sin_family = AF_INET;
serv.sin_port = htons( 1234 );
//呼叫WSAStartup()註冊WinSock DLL的使用
WSAStartup( 0x101 , (LPWSADATA) &wsadata );
//使用TCP協定
sd = socket( AF_INET , SOCK_STREAM , 0 );
printf( "sd = %d\n" , sd );
int len_serv = sizeof( serv );
//因使用send(),所以在傳送之前要先連接至server,連接之後才能傳送資訊
n = connect( sd , (struct sockaddr*) &serv , len_serv );
printf( "connect(): %d\n" , n );
while( fgets( str , 100 , fin ) != NULL )
{
n = send( sd , str , strlen(str) , 0 );
printf( "send: %s (%d)\n" , str , n );
}
fclose(fin);
//關閉TCP socket
closesocket(sd);
//結束WinSock DLL的使用
WSACleanup();
system("pause");
}
```
in.txt:
```txt=
123456
abcde
xyz
qwertyuiop
asdfghjkl
zxcvbnm
```
out.txt:
```txt=
123456
abcde
xyz
qwertyuiop
asdfghjkl
zxcvbnm
```
## 練習6. 完成連續傳送大量二位元檔的實驗
作業練習6是利用TCP來讓client端連續傳送大量二位元檔 in.txt 給server端,然後server端接收到 in.txt 之後再去讀取 in.txt 並把裡面的內容寫入 out.txt 裡面
執行畫面:
<center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/dfGTvFQ.png" /></center>
<center>server</center>
<br>
<center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/AkLsycT.png" /></center>
<center>client</center>
<br>
<center><img style="margin-top:auto;border:1px #eee;width:100%;" src="https://i.imgur.com/ztM1gsS.png" /></center>
<center>in.txt</center>
<br>
<center><img style="margin-top:auto;border:1px #eee;width:100%;" src="https://i.imgur.com/tmC5R9L.png" /></center>
<center>out.txt</center>
<br>
server程式碼:
```C=
#include<stdio.h>
#include<winsock.h>
#define MAXSIZE 1024
int main()
{
//set WSA and initial WSAStartup
SOCKET sd , clnt_sd;
WSADATA wsadata;
struct sockaddr_in serv , clnt;
int i , n;
char buf[MAXSIZE];
FILE *fout;
//讀取檔案
fout = fopen( "out.txt" , "w+b" );
//設定server的結構
serv.sin_addr.s_addr = inet_addr( "127.0.0.1" );
serv.sin_family = AF_INET;
serv.sin_port = htons( 1234 );
//呼叫WSAStartup()註冊WinSock DLL的使用
WSAStartup( 0x101 , (LPWSADATA) &wsadata );
//使用TCP協定
sd = socket( AF_INET , SOCK_STREAM , 0 );
printf( "sd = %d\n" , sd );
int len_serv = sizeof( serv );
//綁定server結構裡設定的資訊
n = bind( sd , (struct sockaddr*) &serv , len_serv );
printf( "bind(): %d\n" , n );
n = listen( sd , 5 );
printf( "listen(): %d\n" , n );
int len_clnt = sizeof( clnt );
clnt_sd = accept( sd , (struct sockaddr*) &clnt , &len_clnt );
//recv and save
n = recv( clnt_sd , buf , MAXSIZE , 0 );
printf( "recv and save: %d Byte\n" , n );
while( n > 0 )
{
fwrite( buf , 1 , n , fout );
n = recv( clnt_sd , buf , MAXSIZE , 0 );
if( n == 0 )
{
break;
}
printf( "recv and save: %d Byte\n" , n );
}
fclose( fout );
//關閉TCP socket
closesocket(sd);
closesocket(clnt_sd);
//結束WinSock DLL的使用
WSACleanup();
system("pause");
}
```
client程式碼:
```C=
#include<stdio.h>
#include<winsock.h>
#define MAXSIZE 1024
int main()
{
//set WSA and initial WSAStartup
SOCKET sd;
WSADATA wsadata;
struct sockaddr_in serv;
int i , n;
char buf[MAXSIZE];
FILE *fin;
//讀取檔案
fin = fopen( "in.txt" , "rb" );
//設定Server的結構
serv.sin_addr.s_addr = inet_addr( "127.0.0.1" );
serv.sin_family = AF_INET;
serv.sin_port = htons( 1234 );
//呼叫WSAStartup()註冊WinSock DLL的使用
WSAStartup( 0x101 , (LPWSADATA) &wsadata );
//使用TCP協定
sd = socket( AF_INET , SOCK_STREAM , 0 );
printf( "sd = %d\n" , sd );
int len_serv = sizeof( serv );
//因使用send(),所以在傳送之前要先連接至server,連接之後才能傳送資訊
n = connect( sd , (struct sockaddr*) &serv , len_serv );
printf( "connect(): %d\n" , n );
//read and send
n = fread( buf , 1 , MAXSIZE , fin );
while( n > 0 )
{
send( sd , buf , n , 0 );
printf( "read and send: %d Byte\n" , n );
n = fread( buf , 1 , MAXSIZE , fin );
}
fclose(fin);
//關閉TCP socket
closesocket(sd);
//結束WinSock DLL的使用
WSACleanup();
system("pause");
}
```
in.txt:
```txt=
123456789
123456789123456789
123456789123456789
123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789123456789
123456789
123456789
123456789
123456789
123456789
123456789
```
out.txt:
```txt=
123456789
123456789123456789
123456789123456789
123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789
123456789123456789
123456789
123456789
123456789
123456789
123456789
123456789
```
## 練習7. 完成UDP傳檔的實驗
作業練習7是利用UDP來讓client端傳送檔案 in.txt 給server端,然後server端接收到 in.txt 之後再去讀取 in.txt 並把裡面的內容寫入 out.txt 裡面,但是如果用UDP傳檔會發生一些問題,就是要手動去把server結束之後,程式才會把 in.txt 裡面的內容寫入 out.txt 裡面,因為UDP不知道傳送結束,所以程式會一直在待機的狀態,這樣導致要手動關閉程式, in.txt 的內容才會順利的寫入 out.txt 裡面
執行畫面:
<center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/qOCFU5g.png" /></center>
<center>server</center>
<br>
<center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/iTwrVCv.png" /></center>
<center>client</center>
<br>
<center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/94v4dYh.png" /></center>
<center>in.txt</center>
<br>
<center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/jbC7Aue.png" /></center>
<center>out.txt</center>
<br>
server程式碼:
```C=
#include<stdio.h>
#include<winsock.h>
#define MAXSIZE 300
int main()
{
//set WSA and initial WSAStartup
SOCKET sd , clnt_sd;
WSADATA wsadata;
struct sockaddr_in serv , clnt;
int i , n;
char buf[MAXSIZE];
FILE *fout;
//讀取檔案
fout = fopen( "out.txt" , "w+b" );
//設定server的結構
serv.sin_addr.s_addr = inet_addr( "127.0.0.1" );
serv.sin_family = AF_INET;
serv.sin_port = htons( 1234 );
//呼叫WSAStartup()註冊WinSock DLL的使用
WSAStartup( 0x101 , (LPWSADATA) &wsadata );
//使用UDP協定
sd = socket( AF_INET , SOCK_DGRAM , 0 );
printf( "sd = %d\n" , sd );
int len_serv = sizeof( serv );
//綁定server結構裡設定的資訊
n = bind( sd , (struct sockaddr*) &serv , len_serv );
printf( "bind(): %d\n" , n );
int len_clnt = sizeof( clnt );
//recv and save
n = recv( sd , buf , MAXSIZE , 0 );
printf( "recv and save: %d Byte\n" , n );
while( n > 0 )
{
fwrite( buf , 1 , n , fout );
n = recv( sd , buf , MAXSIZE , 0 );
printf( "recv and save: %d Byte\n" , n );
}
fclose( fout );
//關閉TCP socket
closesocket(sd);
closesocket(clnt_sd);
//結束WinSock DLL的使用
WSACleanup();
system("pause");
}
```
client程式碼:
```C=
#include<stdio.h>
#include<winsock.h>
#define MAXSIZE 300
int main()
{
//set WSA and initial WSAStartup
SOCKET sd;
WSADATA wsadata;
struct sockaddr_in serv;
int i , n;
char buf[MAXSIZE];
FILE *fin;
//讀取檔案
fin = fopen( "in.txt" , "rb" );
//設定Server的結構
serv.sin_addr.s_addr = inet_addr( "127.0.0.1" );
serv.sin_family = AF_INET;
serv.sin_port = htons( 1234 );
//呼叫WSAStartup()註冊WinSock DLL的使用
WSAStartup( 0x101 , (LPWSADATA) &wsadata );
//使用UDP協定
sd = socket( AF_INET , SOCK_DGRAM , 0 );
printf( "sd = %d\n" , sd );
int len_serv = sizeof( serv );
//因使用send(),所以在傳送之前要先連接至server,連接之後才能傳送資訊
n = connect( sd , (struct sockaddr*) &serv , len_serv );
printf( "connect(): %d\n" , n );
//read and send
n = fread( buf , 1 , MAXSIZE , fin );
while( n > 0 )
{
send( sd , buf , n , 0 );
printf( "read and send: %d Byte\n" , n );
n = fread( buf , 1 , MAXSIZE , fin );
}
fclose(fin);
//關閉TCP socket
closesocket(sd);
//結束WinSock DLL的使用
WSACleanup();
system("pause");
}
```
in.txt:
```txt=
123456789
123456789123456789
123456789123456789
123456789
123456789123456789
123456789123456789
123456789
123456789123456789
123456789123456789
```
out.txt:
```txt=
123456789
123456789123456789
123456789123456789
123456789
123456789123456789
123456789123456789
123456789
123456789123456789
123456789123456789
```
## 練習8. 完成UDP傳檔改良的實驗
作業練習8是利用UDP來讓client端 ( ip: 127.0.0.1 , port: 22222 ) 傳送檔案 in.jpg 給server端,然後server端接收到 in.jpg 之後再去讀取 in.jpg 並把裡面的內容寫入 out.jpg 裡面,並且要改良因為使用UDP傳檔的一些問題
問題(1) 不知道傳送結束: 可以用暗號或是一些文字告知
問題(2) 不知道有無遺失: 當傳送完畢時,可以附上送出總量,來對比傳輸檔案是否有完整的傳送過去
問題(3) 不知道是否有人亂入: 只收特別IP與port,在傳送的時候可以查看來源的IP與port是否正確
執行畫面:
<center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/uq43GQR.png" /></center>
<center>server</center>
<br>
<center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/Ay0YMPM.png" /></center>
<center>client</center>
<br>
<center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/EtOwGj7.jpg" /></center>
<center>in.jpg</center>
<br>
<center><img style="margin-top:auto;border:1px #eee;width:70%;" src="https://i.imgur.com/UwLUI1e.jpg" /></center>
<center>out.jpg</center>
<br>
server程式碼:
```C=
#include<stdio.h>
#include<winsock.h>
#define MAXSIZE 3000
int main()
{
//set WSA and initial WSAStartup
SOCKET sd , clnt_sd;
WSADATA wsadata;
struct sockaddr_in serv , clnt;
int i , n , sum=0 , counter=0;
char *ip;
int port;
char buf[MAXSIZE];
FILE *fout;
//讀取檔案
fout = fopen( "out.jpg" , "w+b" );
//設定server的結構
serv.sin_addr.s_addr = inet_addr( "127.0.0.1" );
serv.sin_family = AF_INET;
serv.sin_port = htons( 1234 );
//呼叫WSAStartup()註冊WinSock DLL的使用
WSAStartup( 0x101 , (LPWSADATA) &wsadata );
//使用UDP協定
sd = socket( AF_INET , SOCK_DGRAM , 0 );
printf( "sd = %d\n" , sd );
int len_serv = sizeof( serv );
//綁定server結構裡設定的資訊
n = bind( sd , (struct sockaddr*) &serv , len_serv );
printf( "bind(): %d\n" , n );
int len_clnt = sizeof( clnt );
//recv and save
n = recvfrom( sd , buf , MAXSIZE , 0 , (struct sockaddr*) &clnt , &len_clnt );
//將32位元的IPv4地址轉換為十進制字串,並且知道是從哪一個ip傳送過來
ip = inet_ntoa( clnt.sin_addr );
//將一個16位元的整數轉換為host位元組順序,並且知道是從哪一個port傳送過來
port = ntohs( clnt.sin_port );
while( n > 0 )
{
counter++;
sum = sum + n ;
fwrite( buf , 1 , n , fout );
printf( "(%d) recv and save: %d (%d) Byte --> from: %s (%d)\n" , counter , n , sum , ip , port );
n = recvfrom( sd , buf , MAXSIZE , 0 , (struct sockaddr*) &clnt , &len_clnt );
//將32位元的IPv4地址轉換為十進制字串,並且知道是從哪一個ip傳送過來
ip = inet_ntoa( clnt.sin_addr );
//將一個16位元的整數轉換為host位元組順序,並且知道是從哪一個port傳送過來
port = ntohs( clnt.sin_port );
if( strcmp( buf , "END" ) == 0 )
{
printf( "File transmission complete. (Sum: %d)\n" , sum );
n = recv( sd , buf , MAXSIZE , 0 );
if( sum != atoi(buf) )
{
printf( "oh...Pkt Number mismatched!!\n" );
}
else
{
printf( "Correct , Pkt Number matched!!\n" );
}
break;
}
}
fclose( fout );
//關閉TCP socket
closesocket(sd);
closesocket(clnt_sd);
//結束WinSock DLL的使用
WSACleanup();
system("pause");
}
```
client程式碼:
```C=
#include<stdio.h>
#include<winsock.h>
#define MAXSIZE 3000
int main()
{
//set WSA and initial WSAStartup
SOCKET sd;
WSADATA wsadata;
struct sockaddr_in serv , clnt;
int i , n , sum=0 , counter=0;
char buf[MAXSIZE];
FILE *fin;
//讀取檔案
fin = fopen( "in.jpg" , "rb" );
//呼叫WSAStartup()註冊WinSock DLL的使用
WSAStartup( 0x101 , (LPWSADATA) &wsadata );
//使用UDP協定
sd = socket( AF_INET , SOCK_DGRAM , 0 );
printf( "sd = %d\n" , sd );
int len_clnt = sizeof( clnt );
//設定client的結構
clnt.sin_addr.s_addr = inet_addr( "127.0.0.1" );
clnt.sin_family = AF_INET;
clnt.sin_port = htons( 22222 ); //set 0 is random port
//綁定client結構裡設定的資訊
bind( sd , (struct sockaddr*) &clnt , &len_clnt );
//設定Server的結構
serv.sin_addr.s_addr = inet_addr( "127.0.0.1" );
serv.sin_family = AF_INET;
serv.sin_port = htons( 1234 );
int len_serv = sizeof( serv );
//因使用send(),所以在傳送之前要先連接至server,連接之後才能傳送資訊
n = connect( sd , (struct sockaddr*) &serv , len_serv );
printf( "connect(): %d\n" , n );
//read and send
n = fread( buf , 1 , MAXSIZE , fin );
while( n > 0 )
{
send( sd , buf , n , 0 );
sum = sum + n;
counter++;
printf( "(%d) read and send: %d (%d) Byte\n" , counter , n , sum );
n = fread( buf , 1 , MAXSIZE , fin );
}
send( sd , "END" , 4 , 0 );
printf( "File transmission complete. (Sum: %d)\n" , sum );
itoa( sum , buf , 10 );
send( sd , buf , sizeof( buf ) , 0 );
fclose(fin);
//關閉TCP socket
closesocket(sd);
//結束WinSock DLL的使用
WSACleanup();
system("pause");
}
```
## 本週心得
這次老師教的課程為TCP與UDP更進階的應用,之前都沒有想過TCP雙方都先送在收的問題,經過這次老師的課程,讓我了解TCP的資料是類似stream(串流)方式與buffer的架構,這樣之後再使用TCP就可以更了解send()與recv()的運作,也讓我知道UDP在傳輸檔案會有一些問題發生與如何解決UDP的傳檔問題,讓我最驚訝的是之前幾乎都是 .txt 的文字檔再傳送接收,但是老師竟然有教如何傳送二位元的檔案,這樣不僅可以傳送 .jpg or .png 的圖檔,也可以傳送 .exe 的執行檔,讓我非常的驚訝與開心原來還可以這樣子傳送接收,所以之後如果需要應用到傳送不同類型的檔案時,也許可以派上用場,所以非常謝謝老師有教一些日常上的程式應用,也希望老師之後的課程可以多教與多使用現在外面公司在用的技術或是現實很常會應用到的功能,謝謝老師的教學影片,我看完老師的課程非常有收穫,謝謝老師,期待您下一次的課程與講解,感謝助教大哥改那麼多人的作業,辛苦了!
### 作者: 廖經翰