实验2 基于数据报套接字的网络程序设计(计算机网络Ⅱ)

 

一、实验环境

操作系统: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)基于数据报套接字的循环服务器回射程序设计

①服务器

  1. #include <stdio.h>  
  2. #include "winsock2.h"  
  3. #pragma comment(lib, "ws2_32.lib")  
  4. #define SERVER_PORT 4501  
  5. #define MAXLINE 1024    //缓冲区长度  
  6. int main()  
  7. {  
  8. WSADATA wsd; //WSADATA变量  
  9. SOCKET sConnfd;//连接套接字  
  10. sockaddr_in Cliaddr, Servaddr;//通信地址  
  11. char sendBuf[MAXLINE]; //发送缓冲区  
  12. char recvBuf[MAXLINE];//接收缓冲区  
  13. int len = sizeof(SOCKADDR);  
  14. int dwError;  
  15. //初始化套结字动态库  
  16. if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)  
  17. {  
  18. printf("WSAStartup failed!\n");  
  19. return -1;  
  20. }  
  21. if (LOBYTE(wsd.wVersion) != 2 ||HIBYTE(wsd.wVersion) != 2)   
  22. {  
  23. WSACleanup();  
  24. return -1;  
  25. }  
  26. //创建监听套接字  
  27. sConnfd = socket(AF_INET, SOCK_DGRAM, 0);  
  28. if (INVALID_SOCKET == sConnfd)  
  29. {  
  30. printf("socket failed!\n");  
  31. WSACleanup();//释放套接字资源;  
  32. return  -1;  
  33. }  
  34. memset(&Servaddr, 0, sizeof(Servaddr));  
  35. //服务器监听套接字地址   
  36. Servaddr.sin_family = AF_INET;  
  37. Servaddr.sin_port = htons(SERVER_PORT);  
  38. Servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//将IP地址指定为INADDR_ANY,允许套接字向任何分配给本地机器的IP地址发送或接收数据。  
  39. //绑定监听套接字  
  40. if (SOCKET_ERROR == bind(sConnfd, (SOCKADDR *)&Servaddr, sizeof(Servaddr)))  
  41. {  
  42. dwError = WSAGetLastError();  
  43. printf("bind函数调用错误,错误号: %d\n", dwError);  
  44. closesocket(sConnfd);   //关闭套接字  
  45. WSACleanup();           //释放套接字资源  
  46. return -1;  
  47. }  
  48. for(;;)  
  49. {  
  50. //接收数据,连接套接字,接收缓冲区,缓冲区长度,正常发送方式,源地址指针,地址大小  
  51. if(SOCKET_ERROR==(recvfrom(sConnfd, recvBuf, MAXLINE, 0, (SOCKADDR*)&Cliaddr, &len)))  
  52. {  
  53. printf("recvfrom函数调用错误,错误号: %d\n", WSAGetLastError());  
  54. closesocket(sConnfd);   //关闭套接字  
  55. WSACleanup();           //释放套接字资源  
  56. return -1;  
  57. }  
  58. sprintf(sendBuf, "echo: %s",recvBuf);  
  59. printf("%s\n", sendBuf);  
  60. //回射消息  
  61. if(SOCKET_ERROR==(sendto(sConnfd, recvBuf, strlen(recvBuf) + 1, 0, (SOCKADDR*)&Cliaddr, len)))  
  62. {  
  63. printf("sendto函数调用错误,错误号: %d\n", WSAGetLastError());  
  64. closesocket(sConnfd);   //关闭套接字  
  65. WSACleanup();           //释放套接字资源  
  66. return -1;  
  67. }  
  68. }  
  69. closesocket(sConnfd);  
  70. WSACleanup();  
  71. return 0;  
  72. }  

②客户端

  1. #include <stdio.h>  
  2. #include "winsock2.h"  
  3. #pragma comment(lib, "ws2_32.lib")  
  4. #define SERVER_PORT 4501  
  5. #define MAXLINE 1024    //接收缓冲区长度  
  6. int main()  
  7. {  
  8. WSADATA wsd; //WSADATA变量  
  9. SOCKET cConnfd;//连接套接字  
  10. sockaddr_in Servaddr;//通信地址  
  11. char sendBuf[MAXLINE]; //发送缓冲区  
  12. char recvBuf[MAXLINE];//接收缓冲区  
  13. int len = sizeof(SOCKADDR);  
  14. //初始化套结字动态库  
  15. if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)  
  16. {  
  17. printf("WSAStartup failed!\n");  
  18. return -1;  
  19. }  
  20. if (LOBYTE(wsd.wVersion) != 2 || HIBYTE(wsd.wVersion) != 2)  
  21. {  
  22. WSACleanup();  
  23. return -1;  
  24. }  
  25. //创建监听套接字  
  26. cConnfd = socket(AF_INET, SOCK_DGRAM, 0);  
  27. if (INVALID_SOCKET == cConnfd)  
  28. {  
  29. printf("socket failed!\n");  
  30. WSACleanup();//释放套接字资源;  
  31. return  -1;  
  32. }  
  33. memset(&Servaddr, 0, sizeof(Servaddr));  
  34. //设置服务器地址  
  35. Servaddr.sin_family = AF_INET;  
  36. Servaddr.sin_port = htons(SERVER_PORT);  
  37. Servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");  
  38. for(;;)  
  39. {  
  40. gets_s(sendBuf);  
  41. if (sendBuf[0] == 'q')  
  42. {  
  43. printf("程序退出\n");  
  44. return -1;  
  45. }  
  46. sendto(cConnfd, sendBuf, strlen(sendBuf) + 1, 0,(SOCKADDR*)&Servaddr, len);//发送数据  
  47. recvfrom(cConnfd, recvBuf, MAXLINE, 0, (SOCKADDR*)&Servaddr, &len);//接收数据  
  48. printf("echo: %s\n", recvBuf);  
  49. }  
  50. closesocket(cConnfd);  
  51. WSACleanup();  
  52. return 0;  
  53. }  

(2)基于数据报套接字的并发服务器回射程序设计

①服务器

  1. #include <stdio.h>  
  2. #include <winsock2.h>  
  3. #pragma comment(lib, "WS2_32")  // 链接到WS2_32.lib  
  4. #define SERVER_PORT 4502  
  5. #define MAXLINE 1024  
  6. int tempPort = SERVER_PORT;//临时端口号  
  7. DWORD WINAPI str_echo(LPVOID sd)  
  8. {     
  9. char recvBuf[MAXLINE];        
  10. char szBuf[MAXLINE];          
  11. SOCKADDR_IN Cliaddr;  
  12. int iClilen = sizeof(SOCKADDR);  
  13. int tmp = tempPort;  
  14. int retVal = 0;  
  15. //在新的套接口上绑定临时端口  
  16. SOCKET tempSocket = socket(AF_INET, SOCK_DGRAM, 0);  
  17. SOCKADDR_IN Servaddr;  
  18. Servaddr.sin_addr.s_addr = htonl(INADDR_ANY);  
  19. Servaddr.sin_family = AF_INET;  
  20. Servaddr.sin_port = htons(tmp);  
  21. //绑定  
  22. if (SOCKET_ERROR == bind(tempSocket, (SOCKADDR *)&Servaddr, sizeof(Servaddr)))  
  23. {  
  24. printf("bind函数调用错误,错误号: %d\n", WSAGetLastError());  
  25. closesocket(tempSocket);    //关闭套接字  
  26. WSACleanup();           //释放套接字资源  
  27. return -1;  
  28. }  
  29. for(;;)  
  30. {  
  31. retVal = recvfrom(tempSocket, recvBuf, MAXLINE, 0, (SOCKADDR*)&Cliaddr, &iClilen);  
  32. if (retVal == SOCKET_ERROR)  
  33. {  
  34. printf("recvfrom函数调用错误,错误号: %d\n", WSAGetLastError());  
  35. closesocket(tempSocket);  
  36. WSACleanup();  
  37. return -1;  
  38. }             
  39. sprintf_s(szBuf, "%d echo:%s", ntohs(Servaddr.sin_port),recvBuf);  
  40. printf("%s\n", szBuf);  
  41. retVal = sendto(tempSocket, szBuf, strlen(szBuf) + 1, 0, (SOCKADDR*)&Cliaddr, iClilen);  
  42. if (SOCKET_ERROR == retVal)  
  43. {  
  44. printf("sendto函数调用错误,错误号: %d\n", WSAGetLastError());  
  45. closesocket(tempSocket);  
  46. WSACleanup();  
  47. return -1;  
  48. }  
  49. }  
  50. closesocket(tempSocket);  
  51. return 0;  
  52. }  
  53. int main()  
  54. {  
  55. WSADATA wsd; //WSADATA变量  
  56. SOCKET sSocket;//监听套接字\连接套接字  
  57. sockaddr_in Cliaddr, Servaddr;//通信地址  
  58. DWORD dwError = 0;  
  59. int iClilen = sizeof(SOCKADDR);//客户端地址长度  
  60. char sendBuf[MAXLINE];//发送缓冲区  
  61. char recvBuf[MAXLINE];//接收缓冲区  
  62. HANDLE  hThread = NULL; //新线程  
  63. //初始化套结字动态库  
  64. if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)  
  65. {  
  66. printf("WSAStartup failed!\n");  
  67. return -1;  
  68. }  
  69. //创建监听套接字  
  70. sSocket = socket(AF_INET, SOCK_DGRAM, 0);  
  71. if (INVALID_SOCKET == sSocket)  
  72. {  
  73. printf("socket failed!\n");  
  74. WSACleanup();//释放套接字资源;  
  75. return  -1;  
  76. }  
  77. memset(&Servaddr, 0, sizeof(Servaddr));  
  78. memset(&Cliaddr, 0, sizeof(Cliaddr));  
  79. //建立套接字  
  80. Servaddr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);  
  81. Servaddr.sin_family = AF_INET;  
  82. Servaddr.sin_port = htons(SERVER_PORT);  
  83. //绑定监听套接字  
  84. if (SOCKET_ERROR == bind(sSocket, (SOCKADDR *)&Servaddr, sizeof(Servaddr)))  
  85. {  
  86. dwError = WSAGetLastError();  
  87. printf("bind函数调用错误,错误号: %d\n", dwError);  
  88. closesocket(sSocket);   //关闭套接字  
  89. WSACleanup();           //释放套接字资源  
  90. return -1;  
  91. }  
  92. for(;;)  
  93. {  
  94. recvfrom(sSocket, recvBuf, MAXLINE, 0, (SOCKADDR*)&Cliaddr, &iClilen);  
  95. ++tempPort;//给新客户的临时端口号  
  96. sprintf_s(sendBuf, "%d", tempPort);  
  97. //将临时端口号发给客户端  
  98. sendto(sSocket, sendBuf, sizeof(sendBuf), 0, (SOCKADDR*)&Cliaddr, iClilen);  
  99. sprintf_s(sendBuf, "%d echo:%s",tempPort, recvBuf);  
  100. printf("%s\n", sendBuf);  
  101. sendto(sSocket, sendBuf, sizeof(sendBuf), 0, (SOCKADDR*)&Cliaddr, iClilen);  
  102. //利用新的端口号建立新线程  
  103. hThread = CreateThread(NULL, 0, str_echo, (LPVOID)sSocket, 0, NULL);  
  104. if (hThread == NULL)  
  105. {  
  106. printf("创建线程失败!\n");  
  107. return -1;  
  108. }  
  109. }  
  110. closesocket(sSocket);  
  111. WSACleanup();  
  112. return 0;  
  113. }  

②客户端

  1. #include <Winsock2.h>  
  2. #include <stdio.h>  
  3. #pragma comment(lib,"wsock32.lib")  
  4. #define MAXLINE 1024  
  5. #define SERVER_PORT 4502  
  6. int main()  
  7. {  
  8. WSADATA wsd; //WSADATA变量  
  9. SOCKET cSocket;//连接套接字  
  10. sockaddr_in Servaddr;//通信地址  
  11. DWORD dwError = 0;  
  12. int retVal = 0;  
  13. int srvLen = sizeof(SOCKADDR);  
  14. char sendBuf[MAXLINE];//发送缓冲区  
  15. char recvBuf[MAXLINE];//接收缓冲区  
  16. int tempPort = SERVER_PORT;  
  17. //初始化套结字动态库  
  18. if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)  
  19. {  
  20. printf("WSAStartup failed!\n");  
  21. return -1;  
  22. }  
  23. //创建监听套接字  
  24. cSocket = socket(AF_INET, SOCK_DGRAM, 0);  
  25. if (INVALID_SOCKET == cSocket)  
  26. {  
  27. printf("socket failed!\n");  
  28. WSACleanup();//释放套接字资源;  
  29. return  -1;  
  30. }  
  31. memset(&Servaddr, 0, sizeof(Servaddr));  
  32. //设置服务器地址  
  33. Servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");  
  34. Servaddr.sin_family = AF_INET;  
  35. Servaddr.sin_port = htons(SERVER_PORT);  
  36. gets_s(sendBuf);  
  37. //  printf("%s",sendBuf);  
  38. if (SOCKET_ERROR == sendto(cSocket, sendBuf, strlen(sendBuf) + 1, 0, (SOCKADDR*)&Servaddr, srvLen))  
  39. {  
  40. printf("sendto函数调用错误,错误号: %d\n", WSAGetLastError());  
  41. closesocket(cSocket);  
  42. WSACleanup();  
  43. return -1;  
  44. }  
  45. recvfrom(cSocket, recvBuf, MAXLINE, 0, (SOCKADDR*)&Servaddr, &srvLen);  
  46. tempPort = atoi(recvBuf);  
  47. //回射  
  48. recvfrom(cSocket, recvBuf, MAXLINE, 0, (SOCKADDR*)&Servaddr, &srvLen);  
  49. printf("%s\n", recvBuf);  
  50. memset(&Servaddr, 0, sizeof(Servaddr));  
  51. Servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");  
  52. Servaddr.sin_family = AF_INET;  
  53. Servaddr.sin_port = htons(tempPort);  
  54. for(;;)//循环接收数据  
  55. {  
  56. gets_s(sendBuf);  
  57. if (sendBuf[0] == 'q')  
  58. {  
  59. printf("程序退出\n");  
  60. return -1;  
  61. }  
  62. //发送响应数据  
  63. retVal = sendto(cSocket, sendBuf, strlen(sendBuf) + 1, 0,(SOCKADDR*)&Servaddr, srvLen);  
  64. if (SOCKET_ERROR == retVal)  
  65. {  
  66. printf("sendto函数调用错误,错误号: %d\n", WSAGetLastError());  
  67. closesocket(cSocket);  
  68. WSACleanup();  
  69. return -1;  
  70. }  
  71. //接收请求数据  
  72. retVal = recvfrom(cSocket, recvBuf, 100, 0, (SOCKADDR*)&Servaddr, &srvLen);  
  73. if (retVal == SOCKET_ERROR)  
  74. {  
  75. printf("recvfrom函数调用错误,错误号: %d\n", WSAGetLastError());  
  76. closesocket(cSocket);  
  77. WSACleanup();  
  78. return -1;  
  79. }  
  80. //打印收到的回显字符串   
  81. printf("%s\n", recvBuf);  
  82. }  
  83. closesocket(cSocket);  
  84. WSACleanup();  
  85. return 0;  
  86. }  

 

(3)无连接应用程序丢包率测试

①服务器

  1. #include <stdio.h>  
  2. #include "winsock2.h"  
  3. #pragma comment(lib, "ws2_32.lib")  
  4. #define SERVER_PORT 6888  
  5. int main()  
  6. {  
  7. WSADATA wsd; //WSADATA变量  
  8. SOCKET sSocket;//连接套接字  
  9. sockaddr_in Servaddr;//通信地址  
  10. DWORD dwError = 0;  
  11. int retVal = 0;  
  12. int srvLen = sizeof(SOCKADDR);  
  13. //初始化套结字动态库  
  14. if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)  
  15. {  
  16. printf("WSAStartup failed!\n");  
  17. return -1;  
  18. }  
  19. //创建监听套接字  
  20. sSocket = socket(AF_INET, SOCK_DGRAM, 0);  
  21. if (INVALID_SOCKET == sSocket)  
  22. {  
  23. printf("socket failed!\n");  
  24. WSACleanup();//释放套接字资源;  
  25. return  -1;  
  26. }  
  27. memset(&Servaddr, 0, sizeof(Servaddr));  
  28. //服务器监听套接字地址   
  29. Servaddr.sin_family = AF_INET;  
  30. Servaddr.sin_port = htons(SERVER_PORT);  
  31. Servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//将IP地址指定为INADDR_ANY,允许套接字向任何分配给本地机器的IP地址发送或接收数据  
  32. //绑定监听套接字                                                     
  33. if (SOCKET_ERROR == bind(sSocket, (SOCKADDR *)&Servaddr, sizeof(Servaddr)))  
  34. {  
  35. dwError = WSAGetLastError();  
  36. printf("bind 函数调用错误,错误号: %d\n", dwError);  
  37. closesocket(sSocket);   //关闭套接字  
  38. WSACleanup();           //释放套接字资源  
  39. return -1;  
  40. }  
  41. //设置缓冲大小及超时时间  
  42. int recvBufLen;  
  43. int len = sizeof(recvBufLen);  
  44. printf("请设置接收缓冲区大小:");  
  45. scanf("%d", &recvBufLen);  
  46. if (setsockopt(sSocket, SOL_SOCKET, SO_RCVBUF, (const char *)&recvBufLen, len) < 0)  
  47. {  
  48. printf("setsockopt error\n");  
  49. return -1;  
  50. }  
  51. if (getsockopt(sSocket, SOL_SOCKET, SO_RCVBUF, (char *)&recvBufLen, &len) < 0)  
  52. {  
  53. printf("getsockopt error\n");  
  54. return -1;  
  55. }  
  56. //设置套接字的接收超时时间  
  57. int nTimeOver;  
  58. printf("请设置接收超时时间:");  
  59. scanf("%d", &nTimeOver);  
  60. if (setsockopt(sSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&nTimeOver, sizeof(nTimeOver)) < 0)  
  61. {  
  62. printf("setsockopt error\n");  
  63. return -1;  
  64. }  
  65. printf("\n系统接收缓存大小被设置为: %d bytes\n", recvBufLen);  
  66. printf("系统接收超时时间被设置为: %d ms\n", nTimeOver);  
  67. SOCKADDR_IN addrClient;  
  68. int addrlen = sizeof(SOCKADDR);  
  69. char recvBuf[10];  
  70. memset(recvBuf, 0, 10);  
  71. double dropRate;//丢包率  
  72. int count = 0;  
  73. int udpClientCount;  
  74. char cCount[10];  
  75. recvfrom(sSocket, cCount, 10, 0, (SOCKADDR *)&addrClient, &addrlen);  
  76. udpClientCount = atoi(cCount);  
  77. printf("客户端一共发送了%d个数据包\n", udpClientCount);  
  78. do {  
  79. memset(recvBuf, 0, 10);  
  80. Sleep(1000);  
  81. //接收数据  
  82. retVal = recvfrom(sSocket, recvBuf, 10, 0, (SOCKADDR *)&addrClient, &addrlen);  
  83. if (retVal > 0) {  
  84. count++;  
  85. }  
  86. else if (retVal == 0)  
  87. {  
  88. break;  
  89. }  
  90. else  
  91. {  
  92. int err = WSAGetLastError();  
  93. printf("recvfrom 函数调用错误,错误号: %d\n", err);  
  94. }  
  95. while (retVal > 0);  
  96. printf("服务器端总共收到%d个数据报\n", count);  
  97. dropRate = 1 - ((double)count) /udpClientCount;  
  98. dropRate = dropRate * 100;  
  99. printf("丢包率 = %lf\n", dropRate);  
  100. closesocket(sSocket);  
  101. WSACleanup();  
  102. return 0;  
  103. }  

②客户端

  1. #include <Winsock2.h>  
  2. #include <stdio.h>  
  3. #pragma comment(lib,"wsock32.lib")  
  4. #define SERVER_PORT 6888  
  5. int main()  
  6. {  
  7. WSADATA wsd; //WSADATA变量  
  8. SOCKET cSocket;//连接套接字  
  9. sockaddr_in Servaddr;//通信地址  
  10. DWORD dwError = 0;  
  11. int retVal = 0;  
  12. int srvLen = sizeof(SOCKADDR);  
  13. //初始化套结字动态库  
  14. if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)  
  15. {  
  16. printf("WSAStartup failed!\n");  
  17. return -1;  
  18. }  
  19. //创建监听套接字  
  20. cSocket = socket(AF_INET, SOCK_DGRAM, 0);  
  21. if (INVALID_SOCKET == cSocket)  
  22. {  
  23. printf("socket failed!\n");  
  24. WSACleanup();//释放套接字资源;  
  25. return  -1;  
  26. }  
  27. memset(&Servaddr, 0, sizeof(Servaddr));  
  28. //设置服务器地址  
  29. Servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");  
  30. Servaddr.sin_family = AF_INET;  
  31. Servaddr.sin_port = htons(SERVER_PORT);  
  32. char sendBuf[10];  
  33. memset(sendBuf, 'h', 9);  
  34. sendBuf[9] = 0;  
  35. int sCount;  
  36. int i = 0;  
  37. char sendCount[10];  
  38. printf("请设置客户端发送数据包次数:");  
  39. scanf("%d", &sCount);  
  40. sprintf_s(sendCount, "%d", sCount);  
  41. sendto(cSocket, sendCount, sizeof(sendCount), 0, (SOCKADDR*)&Servaddr, sizeof(SOCKADDR));  
  42. while (i<sCount)  
  43. {  
  44. retVal = sendto(cSocket, sendBuf, sizeof(sendBuf), 0, (SOCKADDR*)&Servaddr, sizeof(SOCKADDR));  
  45. if (SOCKET_ERROR == retVal)  
  46. {  
  47. printf("sendto 函数调用错误,错误号: %d\n", WSAGetLastError());  
  48. return -1;  
  49. }  
  50. i++;  
  51. }  
  52. closesocket(cSocket);  
  53. WSACleanup();  
  54. return 0;  
  55. }  

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%。

//多有不妥,欢迎指正

上一篇:php-fpm 启动参数及重要配置详解


下一篇:如何编写 Typescript 声明文件