一、實驗環境
操作系統:Win10
開發工具:VS2017
使用語言:C
二、實驗內容
1.設計思路
(1)基於數據報套接字的循環服務器回射程序設計
實現基於數據報套接字的循環服務器回射程序編程模型如(1)數據報套接字編程模型。對於數據報循環服務器,服務器每次接收到一個客戶的請求並處理后,繼續接收進入套接字接收緩沖區的其他請求,因此調用recvfrom(),而后調用sendto()回射數據是循環進行的。這是最常見的無連接服務器的形式,適用於要求對每個請求進行少量處理的服務器設計。
(2)基於數據報套接字的並發服務器回射程序設計
對於並發服務器,則要求當服務器與一個客戶進行通信的過程中,可以同時接收其他客戶的服務請求,並且服務器要為每一個客戶創建一個單獨的子線程。
為了實現客戶與服務器多次交互數據的並發服務器,本實驗采取讓服務器在新線程中為每個客戶創建一個新的套接字,在其上綁定一個臨時端口,然后使用這個套接字處理該客戶的后續所有數據交互。而新客戶的請求還是用服務器最初創建的套接字以及綁定的端口號進行接收。交互過程如圖2-2所示。
圖2-2 多次交互的客戶請求過程
首先客戶端向服務器綁定源端口4502發送請求報文,服務器接收后為其創建子線程,並在線程中創建新的套接口,綁定臨時端口號4503(代碼具體實現中,每個臨時端口依次遞增1),之后便可以進行正確的並發服務和通信,即recvfrom()接收數據,sendto()回射消息。
(3)無連接應用程序丟包率測試
用戶可以在客戶端設置好發包次數sCount,調用sCount次sendto()函數將緩沖區數據發送給服務器。
服務器通過套接字選項SO_RCVBUF選項設置緩沖區大小,通過SO_RCVTIMEO選項設置超時時間,然后開始每隔1000ms接收一次數據。統計接收次數,即收到的數據報個數,最后在服務器計算出丟包率。
2.程序清單(要求有詳細的注釋)
(1)基於數據報套接字的循環服務器回射程序設計
①服務器
- #include <stdio.h>
- #include "winsock2.h"
- #pragma comment(lib, "ws2_32.lib")
- #define SERVER_PORT 4501
- #define MAXLINE 1024 //緩沖區長度
- int main()
- {
- WSADATA wsd; //WSADATA變量
- SOCKET sConnfd;//連接套接字
- sockaddr_in Cliaddr, Servaddr;//通信地址
- char sendBuf[MAXLINE]; //發送緩沖區
- char recvBuf[MAXLINE];//接收緩沖區
- int len = sizeof(SOCKADDR);
- int dwError;
- //初始化套結字動態庫
- if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
- {
- printf("WSAStartup failed!\n");
- return -1;
- }
- if (LOBYTE(wsd.wVersion) != 2 ||HIBYTE(wsd.wVersion) != 2)
- {
- WSACleanup();
- return -1;
- }
- //創建監聽套接字
- sConnfd = socket(AF_INET, SOCK_DGRAM, 0);
- if (INVALID_SOCKET == sConnfd)
- {
- printf("socket failed!\n");
- WSACleanup();//釋放套接字資源;
- return -1;
- }
- memset(&Servaddr, 0, sizeof(Servaddr));
- //服務器監聽套接字地址
- Servaddr.sin_family = AF_INET;
- Servaddr.sin_port = htons(SERVER_PORT);
- Servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//將IP地址指定為INADDR_ANY,允許套接字向任何分配給本地機器的IP地址發送或接收數據。
- //綁定監聽套接字
- if (SOCKET_ERROR == bind(sConnfd, (SOCKADDR *)&Servaddr, sizeof(Servaddr)))
- {
- dwError = WSAGetLastError();
- printf("bind函數調用錯誤,錯誤號: %d\n", dwError);
- closesocket(sConnfd); //關閉套接字
- WSACleanup(); //釋放套接字資源
- return -1;
- }
- for(;;)
- {
- //接收數據,連接套接字,接收緩沖區,緩沖區長度,正常發送方式,源地址指針,地址大小
- if(SOCKET_ERROR==(recvfrom(sConnfd, recvBuf, MAXLINE, 0, (SOCKADDR*)&Cliaddr, &len)))
- {
- printf("recvfrom函數調用錯誤,錯誤號: %d\n", WSAGetLastError());
- closesocket(sConnfd); //關閉套接字
- WSACleanup(); //釋放套接字資源
- return -1;
- }
- sprintf(sendBuf, "echo: %s",recvBuf);
- printf("%s\n", sendBuf);
- //回射消息
- if(SOCKET_ERROR==(sendto(sConnfd, recvBuf, strlen(recvBuf) + 1, 0, (SOCKADDR*)&Cliaddr, len)))
- {
- printf("sendto函數調用錯誤,錯誤號: %d\n", WSAGetLastError());
- closesocket(sConnfd); //關閉套接字
- WSACleanup(); //釋放套接字資源
- return -1;
- }
- }
- closesocket(sConnfd);
- WSACleanup();
- return 0;
- }
②客戶端
- #include <stdio.h>
- #include "winsock2.h"
- #pragma comment(lib, "ws2_32.lib")
- #define SERVER_PORT 4501
- #define MAXLINE 1024 //接收緩沖區長度
- int main()
- {
- WSADATA wsd; //WSADATA變量
- SOCKET cConnfd;//連接套接字
- sockaddr_in Servaddr;//通信地址
- char sendBuf[MAXLINE]; //發送緩沖區
- char recvBuf[MAXLINE];//接收緩沖區
- int len = sizeof(SOCKADDR);
- //初始化套結字動態庫
- if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
- {
- printf("WSAStartup failed!\n");
- return -1;
- }
- if (LOBYTE(wsd.wVersion) != 2 || HIBYTE(wsd.wVersion) != 2)
- {
- WSACleanup();
- return -1;
- }
- //創建監聽套接字
- cConnfd = socket(AF_INET, SOCK_DGRAM, 0);
- if (INVALID_SOCKET == cConnfd)
- {
- printf("socket failed!\n");
- WSACleanup();//釋放套接字資源;
- return -1;
- }
- memset(&Servaddr, 0, sizeof(Servaddr));
- //設置服務器地址
- Servaddr.sin_family = AF_INET;
- Servaddr.sin_port = htons(SERVER_PORT);
- Servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
- for(;;)
- {
- gets_s(sendBuf);
- if (sendBuf[0] == 'q')
- {
- printf("程序退出\n");
- return -1;
- }
- sendto(cConnfd, sendBuf, strlen(sendBuf) + 1, 0,(SOCKADDR*)&Servaddr, len);//發送數據
- recvfrom(cConnfd, recvBuf, MAXLINE, 0, (SOCKADDR*)&Servaddr, &len);//接收數據
- printf("echo: %s\n", recvBuf);
- }
- closesocket(cConnfd);
- WSACleanup();
- return 0;
- }
(2)基於數據報套接字的並發服務器回射程序設計
①服務器
- #include <stdio.h>
- #include <winsock2.h>
- #pragma comment(lib, "WS2_32") // 鏈接到WS2_32.lib
- #define SERVER_PORT 4502
- #define MAXLINE 1024
- int tempPort = SERVER_PORT;//臨時端口號
- DWORD WINAPI str_echo(LPVOID sd)
- {
- char recvBuf[MAXLINE];
- char szBuf[MAXLINE];
- SOCKADDR_IN Cliaddr;
- int iClilen = sizeof(SOCKADDR);
- int tmp = tempPort;
- int retVal = 0;
- //在新的套接口上綁定臨時端口
- SOCKET tempSocket = socket(AF_INET, SOCK_DGRAM, 0);
- SOCKADDR_IN Servaddr;
- Servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
- Servaddr.sin_family = AF_INET;
- Servaddr.sin_port = htons(tmp);
- //綁定
- if (SOCKET_ERROR == bind(tempSocket, (SOCKADDR *)&Servaddr, sizeof(Servaddr)))
- {
- printf("bind函數調用錯誤,錯誤號: %d\n", WSAGetLastError());
- closesocket(tempSocket); //關閉套接字
- WSACleanup(); //釋放套接字資源
- return -1;
- }
- for(;;)
- {
- retVal = recvfrom(tempSocket, recvBuf, MAXLINE, 0, (SOCKADDR*)&Cliaddr, &iClilen);
- if (retVal == SOCKET_ERROR)
- {
- printf("recvfrom函數調用錯誤,錯誤號: %d\n", WSAGetLastError());
- closesocket(tempSocket);
- WSACleanup();
- return -1;
- }
- sprintf_s(szBuf, "%d echo:%s", ntohs(Servaddr.sin_port),recvBuf);
- printf("%s\n", szBuf);
- retVal = sendto(tempSocket, szBuf, strlen(szBuf) + 1, 0, (SOCKADDR*)&Cliaddr, iClilen);
- if (SOCKET_ERROR == retVal)
- {
- printf("sendto函數調用錯誤,錯誤號: %d\n", WSAGetLastError());
- closesocket(tempSocket);
- WSACleanup();
- return -1;
- }
- }
- closesocket(tempSocket);
- return 0;
- }
- int main()
- {
- WSADATA wsd; //WSADATA變量
- SOCKET sSocket;//監聽套接字\連接套接字
- sockaddr_in Cliaddr, Servaddr;//通信地址
- DWORD dwError = 0;
- int iClilen = sizeof(SOCKADDR);//客戶端地址長度
- char sendBuf[MAXLINE];//發送緩沖區
- char recvBuf[MAXLINE];//接收緩沖區
- HANDLE hThread = NULL; //新線程
- //初始化套結字動態庫
- if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
- {
- printf("WSAStartup failed!\n");
- return -1;
- }
- //創建監聽套接字
- sSocket = socket(AF_INET, SOCK_DGRAM, 0);
- if (INVALID_SOCKET == sSocket)
- {
- printf("socket failed!\n");
- WSACleanup();//釋放套接字資源;
- return -1;
- }
- memset(&Servaddr, 0, sizeof(Servaddr));
- memset(&Cliaddr, 0, sizeof(Cliaddr));
- //建立套接字
- Servaddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
- Servaddr.sin_family = AF_INET;
- Servaddr.sin_port = htons(SERVER_PORT);
- //綁定監聽套接字
- if (SOCKET_ERROR == bind(sSocket, (SOCKADDR *)&Servaddr, sizeof(Servaddr)))
- {
- dwError = WSAGetLastError();
- printf("bind函數調用錯誤,錯誤號: %d\n", dwError);
- closesocket(sSocket); //關閉套接字
- WSACleanup(); //釋放套接字資源
- return -1;
- }
- for(;;)
- {
- recvfrom(sSocket, recvBuf, MAXLINE, 0, (SOCKADDR*)&Cliaddr, &iClilen);
- ++tempPort;//給新客戶的臨時端口號
- sprintf_s(sendBuf, "%d", tempPort);
- //將臨時端口號發給客戶端
- sendto(sSocket, sendBuf, sizeof(sendBuf), 0, (SOCKADDR*)&Cliaddr, iClilen);
- sprintf_s(sendBuf, "%d echo:%s",tempPort, recvBuf);
- printf("%s\n", sendBuf);
- sendto(sSocket, sendBuf, sizeof(sendBuf), 0, (SOCKADDR*)&Cliaddr, iClilen);
- //利用新的端口號建立新線程
- hThread = CreateThread(NULL, 0, str_echo, (LPVOID)sSocket, 0, NULL);
- if (hThread == NULL)
- {
- printf("創建線程失敗!\n");
- return -1;
- }
- }
- closesocket(sSocket);
- WSACleanup();
- return 0;
- }
②客戶端
- #include <Winsock2.h>
- #include <stdio.h>
- #pragma comment(lib,"wsock32.lib")
- #define MAXLINE 1024
- #define SERVER_PORT 4502
- int main()
- {
- WSADATA wsd; //WSADATA變量
- SOCKET cSocket;//連接套接字
- sockaddr_in Servaddr;//通信地址
- DWORD dwError = 0;
- int retVal = 0;
- int srvLen = sizeof(SOCKADDR);
- char sendBuf[MAXLINE];//發送緩沖區
- char recvBuf[MAXLINE];//接收緩沖區
- int tempPort = SERVER_PORT;
- //初始化套結字動態庫
- if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
- {
- printf("WSAStartup failed!\n");
- return -1;
- }
- //創建監聽套接字
- cSocket = socket(AF_INET, SOCK_DGRAM, 0);
- if (INVALID_SOCKET == cSocket)
- {
- printf("socket failed!\n");
- WSACleanup();//釋放套接字資源;
- return -1;
- }
- memset(&Servaddr, 0, sizeof(Servaddr));
- //設置服務器地址
- Servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
- Servaddr.sin_family = AF_INET;
- Servaddr.sin_port = htons(SERVER_PORT);
- gets_s(sendBuf);
- // printf("%s",sendBuf);
- if (SOCKET_ERROR == sendto(cSocket, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR*)&Servaddr, srvLen))
- {
- printf("sendto函數調用錯誤,錯誤號: %d\n", WSAGetLastError());
- closesocket(cSocket);
- WSACleanup();
- return -1;
- }
- recvfrom(cSocket, recvBuf, MAXLINE, 0, (SOCKADDR*)&Servaddr, &srvLen);
- tempPort = atoi(recvBuf);
- //回射
- recvfrom(cSocket, recvBuf, MAXLINE, 0, (SOCKADDR*)&Servaddr, &srvLen);
- printf("%s\n", recvBuf);
- memset(&Servaddr, 0, sizeof(Servaddr));
- Servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
- Servaddr.sin_family = AF_INET;
- Servaddr.sin_port = htons(tempPort);
- for(;;)//循環接收數據
- {
- gets_s(sendBuf);
- if (sendBuf[0] == 'q')
- {
- printf("程序退出\n");
- return -1;
- }
- //發送響應數據
- retVal = sendto(cSocket, sendBuf, strlen(sendBuf) + 1, 0,(SOCKADDR*)&Servaddr, srvLen);
- if (SOCKET_ERROR == retVal)
- {
- printf("sendto函數調用錯誤,錯誤號: %d\n", WSAGetLastError());
- closesocket(cSocket);
- WSACleanup();
- return -1;
- }
- //接收請求數據
- retVal = recvfrom(cSocket, recvBuf, 100, 0, (SOCKADDR*)&Servaddr, &srvLen);
- if (retVal == SOCKET_ERROR)
- {
- printf("recvfrom函數調用錯誤,錯誤號: %d\n", WSAGetLastError());
- closesocket(cSocket);
- WSACleanup();
- return -1;
- }
- //打印收到的回顯字符串
- printf("%s\n", recvBuf);
- }
- closesocket(cSocket);
- WSACleanup();
- return 0;
- }
(3)無連接應用程序丟包率測試
①服務器
- #include <stdio.h>
- #include "winsock2.h"
- #pragma comment(lib, "ws2_32.lib")
- #define SERVER_PORT 6888
- int main()
- {
- WSADATA wsd; //WSADATA變量
- SOCKET sSocket;//連接套接字
- sockaddr_in Servaddr;//通信地址
- DWORD dwError = 0;
- int retVal = 0;
- int srvLen = sizeof(SOCKADDR);
- //初始化套結字動態庫
- if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
- {
- printf("WSAStartup failed!\n");
- return -1;
- }
- //創建監聽套接字
- sSocket = socket(AF_INET, SOCK_DGRAM, 0);
- if (INVALID_SOCKET == sSocket)
- {
- printf("socket failed!\n");
- WSACleanup();//釋放套接字資源;
- return -1;
- }
- memset(&Servaddr, 0, sizeof(Servaddr));
- //服務器監聽套接字地址
- Servaddr.sin_family = AF_INET;
- Servaddr.sin_port = htons(SERVER_PORT);
- Servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//將IP地址指定為INADDR_ANY,允許套接字向任何分配給本地機器的IP地址發送或接收數據
- //綁定監聽套接字
- if (SOCKET_ERROR == bind(sSocket, (SOCKADDR *)&Servaddr, sizeof(Servaddr)))
- {
- dwError = WSAGetLastError();
- printf("bind 函數調用錯誤,錯誤號: %d\n", dwError);
- closesocket(sSocket); //關閉套接字
- WSACleanup(); //釋放套接字資源
- return -1;
- }
- //設置緩沖大小及超時時間
- int recvBufLen;
- int len = sizeof(recvBufLen);
- printf("請設置接收緩沖區大小:");
- scanf("%d", &recvBufLen);
- if (setsockopt(sSocket, SOL_SOCKET, SO_RCVBUF, (const char *)&recvBufLen, len) < 0)
- {
- printf("setsockopt error\n");
- return -1;
- }
- if (getsockopt(sSocket, SOL_SOCKET, SO_RCVBUF, (char *)&recvBufLen, &len) < 0)
- {
- printf("getsockopt error\n");
- return -1;
- }
- //設置套接字的接收超時時間
- int nTimeOver;
- printf("請設置接收超時時間:");
- scanf("%d", &nTimeOver);
- if (setsockopt(sSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&nTimeOver, sizeof(nTimeOver)) < 0)
- {
- printf("setsockopt error\n");
- return -1;
- }
- printf("\n系統接收緩存大小被設置為: %d bytes\n", recvBufLen);
- printf("系統接收超時時間被設置為: %d ms\n", nTimeOver);
- SOCKADDR_IN addrClient;
- int addrlen = sizeof(SOCKADDR);
- char recvBuf[10];
- memset(recvBuf, 0, 10);
- double dropRate;//丟包率
- int count = 0;
- int udpClientCount;
- char cCount[10];
- recvfrom(sSocket, cCount, 10, 0, (SOCKADDR *)&addrClient, &addrlen);
- udpClientCount = atoi(cCount);
- printf("客戶端一共發送了%d個數據包\n", udpClientCount);
- do {
- memset(recvBuf, 0, 10);
- Sleep(1000);
- //接收數據
- retVal = recvfrom(sSocket, recvBuf, 10, 0, (SOCKADDR *)&addrClient, &addrlen);
- if (retVal > 0) {
- count++;
- }
- else if (retVal == 0)
- {
- break;
- }
- else
- {
- int err = WSAGetLastError();
- printf("recvfrom 函數調用錯誤,錯誤號: %d\n", err);
- }
- } while (retVal > 0);
- printf("服務器端總共收到%d個數據報\n", count);
- dropRate = 1 - ((double)count) /udpClientCount;
- dropRate = dropRate * 100;
- printf("丟包率 = %lf\n", dropRate);
- closesocket(sSocket);
- WSACleanup();
- return 0;
- }
②客戶端
- #include <Winsock2.h>
- #include <stdio.h>
- #pragma comment(lib,"wsock32.lib")
- #define SERVER_PORT 6888
- int main()
- {
- WSADATA wsd; //WSADATA變量
- SOCKET cSocket;//連接套接字
- sockaddr_in Servaddr;//通信地址
- DWORD dwError = 0;
- int retVal = 0;
- int srvLen = sizeof(SOCKADDR);
- //初始化套結字動態庫
- if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
- {
- printf("WSAStartup failed!\n");
- return -1;
- }
- //創建監聽套接字
- cSocket = socket(AF_INET, SOCK_DGRAM, 0);
- if (INVALID_SOCKET == cSocket)
- {
- printf("socket failed!\n");
- WSACleanup();//釋放套接字資源;
- return -1;
- }
- memset(&Servaddr, 0, sizeof(Servaddr));
- //設置服務器地址
- Servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
- Servaddr.sin_family = AF_INET;
- Servaddr.sin_port = htons(SERVER_PORT);
- char sendBuf[10];
- memset(sendBuf, 'h', 9);
- sendBuf[9] = 0;
- int sCount;
- int i = 0;
- char sendCount[10];
- printf("請設置客戶端發送數據包次數:");
- scanf("%d", &sCount);
- sprintf_s(sendCount, "%d", sCount);
- sendto(cSocket, sendCount, sizeof(sendCount), 0, (SOCKADDR*)&Servaddr, sizeof(SOCKADDR));
- while (i<sCount)
- {
- retVal = sendto(cSocket, sendBuf, sizeof(sendBuf), 0, (SOCKADDR*)&Servaddr, sizeof(SOCKADDR));
- if (SOCKET_ERROR == retVal)
- {
- printf("sendto 函數調用錯誤,錯誤號: %d\n", WSAGetLastError());
- return -1;
- }
- i++;
- }
- closesocket(cSocket);
- WSACleanup();
- return 0;
- }
3.用戶使用說明(輸入 / 輸出規定)
(1)基於數據報套接字的循環服務器回射程序設計
輸入:在客戶端輸入要發送的消息或字符’q’,按“回車”鍵;
輸出:客戶端顯示“echo:”並連接剛剛發送的消息。若輸入為“q”,則顯示“程序退出!”。
(2)基於數據報套接字的並發服務器回射程序設計
輸入:在客戶端輸入要發送的消息或字符’q’,按“回車”鍵;
輸出:客戶端顯示“端口號 echo:”並連接剛剛發送的消息。若輸入為“q”,則顯示“程序退出!”。
(3)無連接應用程序丟包率測試
輸入:在客戶端輸入要發送數據包的數量,按“回車”鍵;在服務器根據提示依次輸入設置的緩沖區大小以及超時時間,按“回車”鍵。
輸出:客戶端顯示“端口號 echo:”並連接剛剛發送的消息。若輸入為“q”,則顯示“程序退出!”。
4.運行結果(要求截圖)
(1)基於數據報套接字的循環服務器回射程序設計
基於數據報套接字的循環服務器回射程序如圖2-3所示。
圖2-3 數據庫套接字的循環服務器-客戶端運行結果截圖
a.在客戶端輸入消息“你好呀”,按“回車”鍵,服務器顯示“echo:你好呀”,同時客戶端回射消息“你好呀”;
b.如同a,用戶依次發送消息 “hiahia”、“helloWorld”,並回射相應消息;
c.用戶最后輸入“q”,則顯示“程序退出!”,服務器端繼續等待新的請求。
(2)基於數據報套接字的並發服務器回射程序設計
基於數據報套接字的並發服務器-客戶端運行結果如圖2-4所示。
圖2-4 並發服務器-客戶端運行結果截圖
a.首先打開服務器等待請求,接着打開三個客戶端,服務器為其創建三個線程;
b.端口號為4503的用戶首先與服務器進行通信,發送“你好呀”,按“回車”鍵,服務器顯示“4503 echo:你好呀”,同時客戶端回射消息“4503 echo:你好呀”;
c.如同b,不同用戶依次發送消息 “hiahia”、“helloWorld”、 “嘻嘻”,並在對應用戶窗口回射相應的端口號連接“echo:”連接剛剛發送的消息;
d.其中端口號為4504用戶最后輸入“q”,則顯示“程序退出!”,服務器端則斷開與端口號為4504的用戶的連接;
e.端口號為4505的用戶繼續與服務器進行通信,發送“哈哈”,按“回車”鍵,服務器顯示“4505 echo:哈哈”,同時客戶端回射消息“4505 echo:哈哈”,服務器與其他用戶通信正常。
(3)無連接應用程序丟包率測試
無連接應用程序丟包率測試結果如圖2-5所示。
圖2-5 無連接應用程序丟包率測試結果截圖
a.客戶端設置好要發送的數據包個數“10”,按“回車”鍵,此時數據包放入套接字緩沖區;
b.服務器設置好緩沖區大小,以及系統接收超時時間,按“回車”鍵;
c.服務器返回客戶端發包數,和服務器收包數,並計算出丟包率。
超時時間一定,nTimeOver=1000 ms,服務器接收數據包的速度是1000ms/次,改變緩沖區大小,測得數據如表2-1所示。
表2-1 服務器在接收緩存取不同值時的丟包率
緩沖區大小/bytes |
超時時間/ms |
客戶端發包/個 |
服務器收包/個 |
丟包率/% |
9 |
1000 |
10 |
1 |
90 |
10 |
1000 |
10 |
1 |
90 |
20 |
1000 |
10 |
2 |
80 |
30 |
1000 |
10 |
3 |
70 |
40 |
1000 |
10 |
4 |
60 |
50 |
1000 |
10 |
5 |
50 |
60 |
1000 |
10 |
6 |
40 |
70 |
1000 |
10 |
7 |
30 |
80 |
1000 |
10 |
8 |
20 |
90 |
1000 |
10 |
9 |
10 |
100 |
1000 |
10 |
10 |
0 |
110 |
1000 |
10 |
10 |
0 |
120 |
1000 |
10 |
10 |
0 |
在該實驗代碼中,客戶端一次發送10個10 bytes的包,由於UDP是面向報文,一次交付的,會將一次發送的全部報文放入緩沖區,超出緩沖區的部分報文丟掉,因此在設置10、20、…、100 bytes的套接字緩沖區后,服務器收包個數是1、2、…、10個,根據丟包率計算公式丟包率=1-(服務器收到的報文個數/客戶端發送的報文個數)×100%,丟包率依次是90%、80%、…、0%。
對於小於一個數據包大小的緩沖區,比如上表中的9 bytes,同樣由於UDP數據處理特點,使服務器可以收到1個數據包的9bytes;對於超過發送報文的全部大小的緩沖區,在服務器接收速度為1000ms/次的條件下,服務器可以把全部包都接收到,丟包率為0%。
//多有不妥,歡迎指正