首页 > 其他 > 详细

实验1 基于流式套接字的网络程序设计(计算机网络Ⅱ)

时间:2020-11-12 21:28:43      阅读:36      评论:0      收藏:0      [点我收藏+]

实验1  基于流式套接字的网络程序设计

一、实验环境

操作系统Win10

开发工具:VS2017

使用语言:C

二、实验内容

1.设计思路

1)流式套接字编程模型

①通信过程

服务器通信过程如下:

1.socket初始化

2.创建套接字,指定使用TCP进行通信;

3.指定本地地址和通信端口;

4.等待客户端的连接请求

5.进行数据传输;

6.关闭套接字;

7.结束对Windows socket dll的使用。

客户端通信过程如下:

1.socket初始化

2.创建套接字,指定使用TCP进行通信

3.指定服务器地址和通信端口

4.向服务器发送连接请求;

5.进行数据传输;

6.关闭套接字;

7.结束对Windows socket dll的使用。

②客户-服务器交互模型

在通常情况下,首先服务器处于监听状态,它随时等待客户连接请求的到来,而客户的连接请求则由客户根据需要随时发出;连接建立后,双方在连接通道上进行数据交互;在会话结束后,双方关闭连接。由于服务器端的服务对象通常不限于单个,因此在服务器的函数设置上考虑了多个客户同时连接服务器的情形,基于流式套接字的客户-服务器交互模型如图1-1所示。

 

1-1  基于流式套接字的客户-服务器交互模型

服务器进程要先于客户进程启动每个步骤中调用的套接字函数如下

1.调用WSAStartup()函数加载Windows Sockets DLL,然后调用socket()函数创建流式套接字,返回套接字s

2.调用bind()函数将套接字s绑定到一个本地的端点地址上;

3.调用listen()将套接字s设置为监听模式,准备好接受来自各个客户的连接请求;

4.调用accept()函数等待接受客户的连接请求;

5.如果接受到客户的连接请求,则accept()函数返回,得到新的套接字ns

6.调用recv()函数在套接字ns上接收来自客户的数据;

7.处理客户的服务器请求;

8.调用send()函数在套接字ns上向客户发送数据;

9.与客户结束通信后,由客户进程断开连接。断开连接后,服务器进程调用closesocket()函数关闭套接字ns。此后服务器进程继续等待客户进程的连接,回到d

10.如果要退出服务器进程,则调用closesocket()函数关闭最初的套接字。

客户进程中每一步骤中使用的套接字函数如下:

1.调用WSAStartup()函数加载Windows Sockets DLL,然后调用socket()函数创建流式套接字,返回套接字s

2.调用connect()函数将套接字s连接到服务器;

3.调用send()函数向服务器发送数据,调用recv()函数接收来自服务器的数据;

4.与服务器通信结束后,由服务器进程断开连接。断开连接后,客户进程调用closesocket()函数关闭套接字s

2基于流式套接字的时间同步服务器设计

实现时间同步服务器和客户端的编程模型如1)流式套接字编程模型。服务器在调用accept()函数接收客户端的请求后,利用time()ctime()函数获取当前时间放入缓冲区,再调用send()函数将当前时间发送给客户端;客户端recv()函数循环接收数据,直到收到服务器发来的时间,双方关闭连接,释放资源。

3基于流式套接字的服务器回射程序设计

实现基于流式套接字的服务器回射程序编程模型如(1流式套接字编程模型。为了提高流式套接字网络程序对流数据的接收能力,需合理处理流数据的接收,从而实现了服务器-客户端读写数据时的三种方式的优化。

① 客户端接收数据以recvline接收一行数据

客户端接收数据时使用recvline接收一行数据。判断的标准是接收到“\n”即为接收到一行使用循环,将数据先全部接收到缓存区中,再从缓存区中一个一个字符判断是否为“\n”。

② 服务器接收数据以recvn定长方式接收

对流数据进行固定长度的消息分割,其长度由服务器运行时指定。定长函数以循环方式接收数据,在循环调用recv()函数进行接收过程中,始终是在recvBuf缓冲区中存储接收到的数据。每次成功接收数据后,将指针后移本次接收到的字节数。变量cnt类似于接收方的接收窗口,标识当前接收方还能接收多大长度的消息,该值在调用recv()前等于定长值,之后随着接收的推进逐渐递减,直到最后一次数据接收后,cnt0时退出循环接收过程。

③ 服务器和客户端接收数据均以recvvl变长方式接收

该实验采用长度字段标记变长消息长度,这样就把变长数据传输问题转换为两次定长数据接收问题。可变长消息的格式如图1-2所示。

 

1-2  可变长度消息的格式

在数据发送时,首先发送定长的消息头声明本次传输的消息长度,再发送变长的消息体;在数据接收时,接收数据的应用程序把消息读取分成两个步骤:首先接收固定长度的消息头,从消息头中抽取出可变消息体的长度,然后再以定长数据接收的方式读取可变长度部分。

4)基于流式套接字的并发服务器设计

实现基于流式套接字的并发服务器编程模型如(1流式套接字编程模型。为了实现服务器同时为多个客户提供服务,服务器在调用accept()函数接收客户端的请求后,创建一个线程,每个客户端在各自的线程中与服务器进行读写数据。

2.程序清单(要求有详细的注释)

1)基于流式套接字的时间同步服务器设计

服务器

  1. #include <stdio.h>  
  2. #include <time.h>  
  3. #include "winsock2.h"  
  4. #pragma comment(lib, "ws2_32.lib")  
  5. #define BUF_SZIE 64 //接收缓冲区长度  
  6. #define LISTENQ 1024    //监听队列长度  
  7. int main()  
  8. {  
  9. WSADATA         wsd;            //WSADATA变量  
  10. SOCKET          sServer;        //服务器套接字  
  11. SOCKET          sClient;        //客户端套接字  
  12. SOCKADDR_IN     addrServ;;      //服务器地址  
  13. char            buf[BUF_SZIE];  //接收数据缓冲区  
  14. int             retVal;         //返回值  
  15. time_t          ticks;  
  16. //初始化套结字动态库  
  17. if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)  
  18. {  
  19. printf("WSAStartup failed!\n");  
  20. return -1;  
  21. }  
  22. //创建套接字  
  23. sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  
  24. if (INVALID_SOCKET == sServer)  
  25. {  
  26. printf("socket failed!\n");  
  27. WSACleanup();//释放套接字资源;  
  28. return  -1;  
  29. }  
  30. //服务器套接字地址   
  31. addrServ.sin_family = AF_INET;  
  32. addrServ.sin_port = htons(4567);  
  33. addrServ.sin_addr.s_addr = INADDR_ANY;//将IP地址指定为INADDR_ANY,允许套接字向任何分配给本地机器的IP地址发送或接收数据。  
  34. //绑定套接字  
  35. retVal = bind(sServer, (LPSOCKADDR)&addrServ, sizeof(SOCKADDR_IN));  
  36. if (SOCKET_ERROR == retVal)  
  37. {  
  38. printf("bind failed!\n");  
  39. closesocket(sServer);   //关闭套接字  
  40. WSACleanup();           //释放套接字资源;  
  41. return -1;  
  42. }  
  43. //开始监听   
  44. retVal = listen(sServer, LISTENQ);  
  45. if (SOCKET_ERROR == retVal)  
  46. {  
  47. printf("listen failed!\n");  
  48. closesocket(sServer);   //关闭套接字  
  49. WSACleanup();           //释放套接字资源;  
  50. return -1;  
  51. }  
  52. for (;;)  
  53. {  
  54. //接受客户端请求  
  55. sClient = accept(sServer, NULL, NULL);  
  56. if (INVALID_SOCKET == sClient)  
  57. {  
  58. printf("accept failed!\n");  
  59. closesocket(sServer);   //关闭套接字  
  60. WSACleanup();           //释放套接字资源;  
  61. return -1;  
  62. }  
  63. //获取当前时间    
  64. ticks = time(NULL);  
  65. memset(buf, 0, sizeof(buf));  
  66. sprintf(buf, "%.24s\r\n", ctime(&ticks));  
  67. printf("获取当前系统时间: %s\n", buf);  
  68. //发送时间     
  69. retVal = send(sClient, buf, strlen(buf), 0);  
  70. if (SOCKET_ERROR == retVal)  
  71. {  
  72. printf("send 函数调用错误,错误号: %d\n", WSAGetLastError());  
  73. closesocket(sClient);  
  74. WSACleanup();  
  75. return -1;  
  76. }  
  77. printf("向客户端发送时间成功\n");  
  78. // 停止连接,不再发送数据     
  79. retVal = shutdown(sClient, SD_SEND);  
  80. if (retVal == SOCKET_ERROR)   
  81. {  
  82. printf("shutdown 函数调用错误,错误号: %d\n", WSAGetLastError());  
  83. closesocket(sClient);  
  84. WSACleanup();  
  85. return -1;  
  86. }  
  87. // 关闭套接字     
  88. closesocket(sClient);  
  89. printf("主动关闭连接\n");  
  90. }  
  91. //退出  
  92. closesocket(sClient);   //关闭套接字*/  
  93. closesocket(sServer);   //关闭套接字  
  94. WSACleanup();           //释放套接字资源;  
  95. return 0;  
  96. }  

客户端

  1. #include "winsock2.h"  
  2. #include <stdio.h>  
  3. #pragma comment(lib, "ws2_32.lib")  
  4. #define BUF_SZIE 64  
  5. int main()  
  6. {  
  7. WSADATA         wsd;            //WSADATA变量  
  8. SOCKET          sHost;          //服务器套接字  
  9. SOCKADDR_IN     servAddr;       //服务器地址  
  10. char            buf[BUF_SZIE];  //接收数据缓冲区  
  11. int             retVal;         //返回值  
  12. //初始化套结字动态库  
  13. if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)  
  14. {  
  15. printf("WSAStartup failed!\n");  
  16. return -1;  
  17. }  
  18. //创建套接字  
  19. sHost = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  
  20. if (INVALID_SOCKET == sHost)  
  21. {  
  22. printf("socket failed!\n");  
  23. WSACleanup();//释放套接字资源  
  24. return  -1;  
  25. }  
  26. //设置服务器地址  
  27. servAddr.sin_family = AF_INET;  
  28. servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");  
  29. servAddr.sin_port = htons((short)4567);  
  30. int nServAddlen = sizeof(servAddr);  
  31. //连接服务器  
  32. retVal = connect(sHost, (LPSOCKADDR)&servAddr, sizeof(servAddr));  
  33. if (SOCKET_ERROR == retVal)  
  34. {  
  35. printf("connect failed!\n");  
  36. closesocket(sHost); //关闭套接字  
  37. WSACleanup();       //释放套接字资源  
  38. return -1;  
  39. }  
  40. // 持续接收数据,直到服务器方关闭连接    
  41. memset(&buf, 0, sizeof(buf));  
  42. printf("当前时间是:");  
  43. do {  
  44. Sleep(1000);  
  45. retVal = recv(sHost, buf, BUF_SZIE, 0);  
  46. if (retVal > 0)  
  47. printf("%s", buf);  
  48. else {  
  49. if (retVal == 0)  
  50. printf("对方连接关闭,退出\n");  
  51. else  
  52. printf("recv 函数调用错误,错误号: %d\n", WSAGetLastError());  
  53. }  
  54. memset(&buf, 0, sizeof(buf));  
  55. while (retVal > 0);  
  56. //退出  
  57. closesocket(sHost); //关闭套接字  
  58. WSACleanup();       //释放套接字资源  
  59. return 0;  
  60. }  

2)基于流式套接字的服务器回射程序设计——客户端接收数据以recvline接收一行数据

服务器

  1. #include <stdio.h>  
  2. #include "winsock2.h"  
  3. #pragma comment(lib, "ws2_32.lib")  
  4. #define SERVER_PORT 4567  
  5. #define MAXLINE 1024    //接收缓冲区长度  
  6. #define LISTENQ 1024    //监听队列长度  
  7. void str_echo(SOCKET s)  
  8. {  
  9. SOCKET sConnfd = s;  
  10. int n = 0;  
  11. char szBuf[MAXLINE];  
  12. int retVal = 0;  
  13. while ((n = recv(sConnfd, szBuf, sizeof(szBuf), 0)) >0)  
  14. {  
  15. printf("%d 接收到的内容:%s\n", sConnfd, szBuf);  
  16. retVal = send(sConnfd, szBuf, n, 0);  
  17. if (SOCKET_ERROR == retVal)  
  18. {  
  19. int dwError = WSAGetLastError();  
  20. printf("send函数调用错误,错误号: %d\n", dwError);  
  21. return;  
  22. }  
  23. }  
  24. closesocket(sConnfd);  
  25. }  
  26. int main()  
  27. {  
  28. WSADATA wsd; //WSADATA变量  
  29. SOCKET sListenfd, sConnectfd;//服务器监听套接字和连接套接字  
  30. int iClilen = 0;//连接客户端数  
  31. DWORD dwThreadId = 0;  
  32. sockaddr_in Cliaddr, Servaddr;//通信地址  
  33. DWORD dwError = 0;  
  34. HLOCAL hHlocal = NULL;  
  35. HANDLE hThread = NULL;  
  36. //初始化套结字动态库  
  37. if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)  
  38. {  
  39. printf("WSAStartup failed!\n");  
  40. return -1;  
  41. }  
  42. //创建监听套接字  
  43. sListenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  
  44. if (INVALID_SOCKET == sListenfd)  
  45. {  
  46. printf("socket failed!\n");  
  47. WSACleanup();//释放套接字资源;  
  48. return  -1;  
  49. }  
  50. memset(&Servaddr, 0, sizeof(Servaddr));  
  51. memset(&Cliaddr, 0, sizeof(Cliaddr));  
  52. //服务器监听套接字地址   
  53. Servaddr.sin_family = AF_INET;  
  54. Servaddr.sin_port = htons(SERVER_PORT);  
  55. Servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//将IP地址指定为INADDR_ANY,允许套接字向任何分配给本地机器的IP地址发送或接收数据。  
  56. //绑定监听套接字  
  57. if (SOCKET_ERROR == bind(sListenfd, (SOCKADDR *)&Servaddr, sizeof(Servaddr)))  
  58. {  
  59. dwError = WSAGetLastError();  
  60. printf("bind函数调用错误,错误号: %d\n", dwError);  
  61. closesocket(sListenfd); //关闭套接字  
  62. WSACleanup();           //释放套接字资源  
  63. return -1;  
  64. }  
  65. if (SOCKET_ERROR == listen(sListenfd, LISTENQ))  
  66. {  
  67. dwError = WSAGetLastError();  
  68. printf("listen函数调用错误,错误号: %d\n", dwError);  
  69. closesocket(sListenfd); //关闭套接字  
  70. WSACleanup();           //释放套接字资源  
  71. return -1;  
  72. }  
  73. for (;;)  
  74. {  
  75. iClilen = sizeof(Cliaddr);  
  76. printf("等待连接~n(*≧▽≦*)n\n");  
  77. sConnectfd = accept(sListenfd, (SOCKADDR *)&Cliaddr, &iClilen);  
  78. if (SOCKET_ERROR == sConnectfd)  
  79. {  
  80. dwError = WSAGetLastError();  
  81. printf("connect函数调用错误,错误号: %d\n", dwError);  
  82. continue;  
  83. }  
  84. else  
  85. {  
  86. printf("接收到新连接,连接套接口为:%d\n",sConnectfd);  
  87. str_echo(sConnectfd);  
  88. }  
  89. }  
  90. closesocket(sListenfd);  
  91. return 0;  
  92. }  

客户端

  1. #include <stdio.h>  
  2. #include <winsock2.h>  
  3. #pragma comment(lib, "WS2_32")  // 链接到WS2_32.lib  
  4. #define SERVER_PORT  4567    
  5. #define MAXLINE 1024   
  6. //接收一行数据  
  7. BOOL recvline(SOCKET S, char* buf)  
  8. {  
  9. BOOL retval = TRUE; //返回值    
  10. BOOL bLineEnd = FALSE;//一行读取结束    
  11. int nReadLen = 0; //读入字节数    
  12. int nDataLen = 0; //数据长度    
  13. while (!bLineEnd)  
  14. {  
  15. nReadLen = recv(S, buf, MAXLINE, 0);  
  16. if (nReadLen == SOCKET_ERROR)  
  17. {  
  18. int dwError = WSAGetLastError();   // 获取错误代码      
  19. printf("recv函数调用错误,错误号: %d\n", dwError);  
  20. retval = FALSE;   // 读取数据失败      
  21. break;              // 跳出循环      
  22. }  
  23. if (0 == nReadLen)  
  24. {  
  25. retval = FALSE; //读取数据失败    
  26. break;  
  27. }  
  28. for (int i = 0; i<nReadLen; i++)  
  29. {  
  30. if (‘\n‘ == *(buf + i))  
  31. bLineEnd = TRUE;  
  32. }  
  33. }  
  34. return retval;  
  35. }  
  36. void str_echo(FILE *fp, SOCKET sd)  
  37. {  
  38. char sendBuf[MAXLINE];  
  39. char recvBuf[MAXLINE];  
  40. memset(sendBuf, 0, MAXLINE);  
  41. memset(recvBuf, 0, MAXLINE);  
  42. int retVal = 0;  
  43. while (fgets(sendBuf, MAXLINE, fp) != NULL)  
  44. {  
  45. if (sendBuf[0] == ‘q‘)  
  46. {  
  47. printf("程序退出!");  
  48. return;  
  49. }  
  50. if (send(sd, sendBuf, sizeof(sendBuf), 0) <= 0)  
  51. {  
  52. printf("send函数调用错误,错误号:%d\n", WSAGetLastError());  
  53. closesocket(sd);  
  54. WSACleanup();  
  55. return;  
  56. }  
  57. if (!recvline(sd, recvBuf))  
  58. {  
  59. closesocket(sd);  
  60. WSACleanup();  
  61. return;  
  62. }  
  63. //打印收到的回显字符串    
  64. fputs(recvBuf, stdout);  
  65. //清空缓冲区  
  66. memset(recvBuf, 0, MAXLINE);  
  67. }  
  68. }  
  69. int main()  
  70. {  
  71. WSADATA wsd; //WSADATA变量  
  72. SOCKET sConnectfd;  
  73. sockaddr_in Servaddr;  
  74. DWORD dwError = 0;  
  75. memset(&Servaddr, 0, sizeof(Servaddr));  
  76. //初始化套结字动态库  
  77. if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)  
  78. {  
  79. printf("WSAStartup failed!\n");  
  80. return -1;  
  81. }  
  82. //创建套接字  
  83. sConnectfd = socket(AF_INET, SOCK_STREAM, 0);  
  84. if (INVALID_SOCKET == sConnectfd)  
  85. {  
  86. printf("socket failed!\n");  
  87. WSACleanup();//释放套接字资源  
  88. return  -1;  
  89. }  
  90. //设置服务器地址  
  91. Servaddr.sin_family = AF_INET;  
  92. Servaddr.sin_port = htons(SERVER_PORT);  
  93. Servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");  
  94. //连接服务器  
  95. if (SOCKET_ERROR == connect(sConnectfd, (SOCKADDR *)&Servaddr, sizeof(Servaddr)))  
  96. {  
  97. dwError = WSAGetLastError();  
  98. printf("connect函数调用错误,错误号: %d\n", dwError);  
  99. return -1;  
  100. }  
  101. printf("连接成功~");  
  102. str_echo(stdin, sConnectfd);  
  103. closesocket(sConnectfd);  
  104. return 0;  
  105. }  

3)基于流式套接字的服务器回射程序设计——服务器接收数据以recvn定长方式接收

服务器

  1. #include <stdio.h>  
  2. #include "winsock2.h"  
  3. #pragma comment(lib, "ws2_32.lib")  
  4. #define SERVER_PORT 4566  
  5. #define MAXLINE 1024    //接收缓冲区长度  
  6. #define LISTENQ 1024    //监听队列长度  
  7. int recvn(SOCKET s, char * recvbuf, unsigned int fixedlen)  
  8. {  
  9. int iResult; //存储单次recv操作的返回值  
  10. int cnt; //用于统计相对于固定长度,剩余多少字节尚未接收  
  11. cnt = fixedlen;  
  12. while (cnt > 0) {  
  13. iResult = recv(s, recvbuf, cnt, 0);  
  14. if (iResult < 0) {  
  15. //数据接收出现错误,返回失败  
  16. printf("接收发生错误: %d\n", WSAGetLastError());  
  17. return -1;  
  18. }  
  19. if (iResult == 0) {  
  20. //对方关闭连接,返回已接收到的小于fixedlen的字节数  
  21. printf("连接关闭\n");  
  22. return fixedlen - cnt;  
  23. }  
  24. //printf("接收到的字节数: %d\n", iResult);  
  25. if (recvbuf[iResult - 1] == ‘\n‘)  
  26. break;  
  27. //接收缓存指针向后移动  
  28. recvbuf += iResult;  
  29. //更新cnt值  
  30. cnt -= iResult;  
  31. }  
  32. return fixedlen;  
  33. }  
  34. int main()  
  35. {  
  36. WSADATA wsd; //WSADATA变量  
  37. SOCKET sListenfd, sConnectfd;//服务器监听套接字和连接套接字  
  38. int iClilen = 0;//连接客户端数  
  39. DWORD dwThreadId = 0;  
  40. sockaddr_in Cliaddr, Servaddr;//通信地址  
  41. DWORD dwError = 0;  
  42. HLOCAL hHlocal = NULL;  
  43. HANDLE hThread = NULL;  
  44. char szBuf[MAXLINE];//发送缓冲区  
  45. int retVal = 0;  
  46. int recvLen = 0;  
  47. memset(szBuf, 0, MAXLINE);  
  48. //初始化套结字动态库  
  49. if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)  
  50. {  
  51. printf("WSAStartup failed!\n");  
  52. return -1;  
  53. }  
  54. sListenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  
  55. if (INVALID_SOCKET == sListenfd)  
  56. {  
  57. printf("socket failed!\n");  
  58. WSACleanup();//释放套接字资源;  
  59. return  -1;  
  60. }  
  61. memset(&Servaddr, 0, sizeof(Servaddr));  
  62. memset(&Cliaddr, 0, sizeof(Cliaddr));  
  63. //服务器监听套接字地址   
  64. Servaddr.sin_family = AF_INET;  
  65. Servaddr.sin_port = htons(SERVER_PORT);  
  66. Servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//将IP地址指定为INADDR_ANY,允许套接字向任何分配给本地机器的IP地址发送或接收数据。  
  67. //绑定监听套接字  
  68. if (SOCKET_ERROR == bind(sListenfd, (SOCKADDR *)&Servaddr, sizeof(Servaddr)))  
  69. {  
  70. dwError = WSAGetLastError();  
  71. printf("bind函数调用错误,错误号: %d\n", dwError);  
  72. closesocket(sListenfd); //关闭套接字  
  73. WSACleanup();           //释放套接字资源  
  74. return -1;  
  75. }  
  76. if (SOCKET_ERROR == listen(sListenfd, LISTENQ))  
  77. {  
  78. dwError = WSAGetLastError();  
  79. printf("listen函数调用错误,错误号: %d\n", dwError);  
  80. closesocket(sListenfd); //关闭套接字  
  81. WSACleanup();           //释放套接字资源  
  82. return -1;  
  83. }  
  84. for (;;)  
  85. {  
  86. iClilen = sizeof(Cliaddr);  
  87. printf("等待连接~n(*≧▽≦*)n\n");  
  88. printf("请指定服务器接收数据长度:");  
  89. scanf("%d", &recvLen);  
  90. sConnectfd = accept(sListenfd, (SOCKADDR *)&Cliaddr, &iClilen);  
  91. if (SOCKET_ERROR == sConnectfd)  
  92. {  
  93. dwError = WSAGetLastError();  
  94. printf("connect函数调用错误,错误号: %d\n", dwError);  
  95. continue;  
  96. }  
  97. else  
  98. {  
  99. printf("接收到新连接,连接套接口为:%d\n", sConnectfd);  
  100. while (recvn(sConnectfd, szBuf, recvLen)>0)//循环接收数据  
  101. {  
  102. printf("%d 接收到的内容:%s\n", sConnectfd, szBuf);  
  103. retVal = send(sConnectfd, szBuf, strlen(szBuf), 0);  
  104. memset(szBuf, 0, MAXLINE);  
  105. if (SOCKET_ERROR == retVal)  
  106. {  
  107. int dwError = WSAGetLastError();  
  108. printf("send函数调用错误,错误号: %d\n", dwError);  
  109. closesocket(sConnectfd);  
  110. closesocket(sListenfd);  
  111. WSACleanup();  
  112. return -1;  
  113. }  
  114. }  
  115. }  
  116. }  
  117. closesocket(sConnectfd);  
  118. closesocket(sListenfd);  
  119. WSACleanup();  
  120. return 0;  
  121. }  

客户端

该客户端代码同(2中②客户端代码相同,即客户端接收数据以recvline接收一行数据

4)基于流式套接字的服务器回射程序设计——服务器和客户端接收数据均以recvvl变长方式接收

服务器

  1. #include <stdio.h>  
  2. #include "winsock2.h"  
  3. #pragma comment(lib, "ws2_32.lib")  
  4. #define SERVER_PORT 4560  
  5. #define MAXLINE 1024    //接收缓冲区长度  
  6. #define LISTENQ 1024    //监听队列长度  
  7. int recvn(SOCKET s, char * recvbuf, unsigned int fixedlen)  
  8. {  
  9. int iResult; //存储单次recv操作的返回值  
  10. int cnt; //用于统计相对于固定长度,剩余多少字节尚未接收  
  11. cnt = fixedlen;  
  12. while (cnt > 0) {  
  13. iResult = recv(s, recvbuf, cnt, 0);  
  14. if (iResult < 0) {  
  15. //数据接收出现错误,返回失败  
  16. printf("接收发生错误: %d\n", WSAGetLastError());  
  17. return -1;  
  18. }  
  19. if (iResult == 0) {  
  20. //对方关闭连接,返回已接收到的小于fixedlen的字节数  
  21. printf("连接关闭\n");  
  22. return fixedlen - cnt;  
  23. }  
  24. //printf("接收到的字节数: %d\n", iResult);  
  25. //接收缓存指针向后移动  
  26. recvbuf += iResult;  
  27. //更新cnt值  
  28. cnt -= iResult;  
  29. }  
  30. return fixedlen;  
  31. }  
  32. int recvvl(SOCKET s, char * recvbuf, unsigned int recvbufLen)  
  33. {  
  34. int iResult;//存储单次recv操作的返回值  
  35. unsigned int reclen; //用于存储报文头部存储的长度信息  
  36. //获取接收报文长度信息  
  37. iResult = recvn(s, (char *)&reclen, sizeof(unsigned int));  
  38. if (iResult != sizeof(unsigned int))   
  39. {  
  40. /*如果长度字段在接收时没有返回一个整型数据就返回(连接关闭) 
  41. 或-1(发生错误)*/  
  42. if (iResult == -1) {  
  43. printf("接收发生错误: %d\n", WSAGetLastError());  
  44. return -1;  
  45. }  
  46. else {  
  47. printf("连接关闭\n");  
  48. return 0;  
  49. }  
  50. }  
  51. //转换网络字节顺序到主机字节顺序  
  52. reclen = ntohl(reclen); if (reclen > recvbufLen) {  
  53. //如果recvbuf没有足够的空间存储变长消息,则接收该消息并丢弃,返回错误  
  54. while (reclen > 0) {  
  55. iResult = recvn(s, recvbuf, recvbufLen);  
  56. if (iResult != recvbufLen) {  
  57. //如果变长消息在接收时没有返回足够的数据就返回(连接关闭)或-1(发生错误)  
  58. if (iResult == -1) {  
  59. printf("接收发生错误: %d\n", WSAGetLastError());  
  60. return -1;  
  61. }  
  62. else {  
  63. printf("连接关闭\n");  
  64. return 0;  
  65. }  
  66. }  
  67. reclen -= recvbufLen;  
  68. //处理最后一段数据长度  
  69. if (reclen < recvbufLen)  
  70. recvbufLen = reclen;  
  71. }  
  72. printf("可变长度的消息超出预分配的接收缓存\r\n");  
  73. return -1;  
  74. }  
  75. //接收可变长消息  
  76. iResult = recvn(s, recvbuf, reclen);  
  77. if (iResult != reclen) {  
  78. /*(如果消息在接收时没有返回足够的数据就返回(连接关闭)或-1(发 
  79. 生错误)*/  
  80. if (iResult == -1) {  
  81. printf("接收发生错误: %d\n", WSAGetLastError());  
  82. return -1;  
  83. }  
  84. else {  
  85. printf("连接关闭\n");  
  86. return 0;  
  87. }  
  88. }  
  89. return iResult;  
  90. }  
  91. void str_echo(SOCKET s)  
  92. {  
  93. SOCKET sConnfd = s;  
  94. int retVal = 0;  
  95. char recvBuf[MAXLINE];//接收缓存    
  96. char sendBuf[MAXLINE];//发送缓存    
  97. unsigned int sendDataLen = 0; //发送数据长度    
  98. unsigned int bufLen = MAXLINE;  
  99. while (TRUE)  //循环接收数据    
  100. {  
  101. memset(recvBuf, 0, MAXLINE);  
  102. memset(sendBuf, 0, MAXLINE);  
  103. retVal = recvvl(sConnfd, recvBuf, bufLen);  
  104. if (retVal == SOCKET_ERROR)  
  105. {  
  106. printf("recvvl函数调用错误,错误号:%d\n", WSAGetLastError());  
  107. closesocket(sConnfd);  
  108. WSACleanup();  
  109. return;  
  110. }  
  111. sprintf(sendBuf, "echo:%s", recvBuf);  
  112. printf("%s\n", sendBuf);  
  113. sendDataLen = (unsigned int)strlen(sendBuf);  
  114. sendDataLen = htonl(sendDataLen);  
  115. retVal = send(sConnfd, (char*)&sendDataLen, sizeof(unsigned int), 0);  
  116. if (SOCKET_ERROR == retVal)  
  117. {  
  118. printf("send函数调用错误,错误号:%d\n", WSAGetLastError());  
  119. closesocket(sConnfd);  
  120. WSACleanup();  
  121. return ;  
  122. }  
  123. retVal = send(sConnfd, sendBuf, strlen(sendBuf), 0);  
  124. if (SOCKET_ERROR == retVal)  
  125. {  
  126. printf("send函数调用错误,错误号:%d\n", WSAGetLastError());  
  127. closesocket(sConnfd);  
  128. WSACleanup();  
  129. return;  
  130. }  
  131. }  
  132. }  
  133. int main()  
  134. {  
  135. WSADATA wsd; //WSADATA变量  
  136. SOCKET sListenfd, sConnectfd;//服务器监听套接字和连接套接字  
  137. int iClilen = 0;//连接客户端数  
  138. DWORD dwThreadId = 0;  
  139. sockaddr_in Cliaddr, Servaddr;//通信地址  
  140. DWORD dwError = 0;  
  141. HLOCAL hHlocal = NULL;  
  142. HANDLE hThread = NULL;  
  143. //初始化套结字动态库  
  144. if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)  
  145. {  
  146. printf("WSAStartup failed!\n");  
  147. return -1;  
  148. }  
  149. sListenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  
  150. if (INVALID_SOCKET == sListenfd)  
  151. {  
  152. printf("socket failed!\n");  
  153. WSACleanup();//释放套接字资源;  
  154. return  -1;  
  155. }  
  156. memset(&Servaddr, 0, sizeof(Servaddr));  
  157. memset(&Cliaddr, 0, sizeof(Cliaddr));  
  158. //服务器监听套接字地址   
  159. Servaddr.sin_family = AF_INET;  
  160. Servaddr.sin_port = htons(SERVER_PORT);  
  161. Servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//将IP地址指定为INADDR_ANY,允许套接字向任何分配给本地机器的IP地址发送或接收数据。  
  162. //绑定监听套接字  
  163. if (SOCKET_ERROR == bind(sListenfd, (SOCKADDR *)&Servaddr, sizeof(Servaddr)))  
  164. {  
  165. dwError = WSAGetLastError();  
  166. printf("bind函数调用错误,错误号: %d\n", dwError);  
  167. closesocket(sListenfd); //关闭套接字  
  168. WSACleanup();           //释放套接字资源  
  169. return -1;  
  170. }  
  171. if (SOCKET_ERROR == listen(sListenfd, LISTENQ))  
  172. {  
  173. dwError = WSAGetLastError();  
  174. printf("listen函数调用错误,错误号: %d\n", dwError);  
  175. closesocket(sListenfd); //关闭套接字  
  176. WSACleanup();           //释放套接字资源  
  177. return -1;  
  178. }  
  179. for (;;)  
  180. {  
  181. iClilen = sizeof(Cliaddr);  
  182. printf("等待连接~n(*≧▽≦*)n\n");  
  183. sConnectfd = accept(sListenfd, (SOCKADDR *)&Cliaddr, &iClilen);  
  184. if (SOCKET_ERROR == sConnectfd)  
  185. {  
  186. dwError = WSAGetLastError();  
  187. printf("accept函数调用错误,错误号: %d\n", dwError);  
  188. continue;  
  189. }  
  190. else  
  191. {  
  192. printf("接收到新连接,连接套接口为:%d\n", sConnectfd);  
  193. str_echo(sConnectfd);  
  194. }  
  195. }  
  196. closesocket(sListenfd);  
  197. return 0;  
  198. }  

客户端

  1. #include <stdio.h>  
  2. #include "winsock2.h"  
  3. #pragma comment(lib, "ws2_32.lib")  
  4. #define SERVER_PORT 4560  
  5. #define MAXLINE 1024    //接收缓冲区长度  
  6. #define LISTENQ 1024    //监听队列长度  
  7. int recvn(SOCKET s, char * recvbuf, unsigned int fixedlen)  
  8. {  
  9. int iResult; //存储单次recv操作的返回值  
  10. int cnt; //用于统计相对于固定长度,剩余多少字节尚未接收  
  11. cnt = fixedlen;  
  12. while (cnt > 0) {  
  13. iResult = recv(s, recvbuf, cnt, 0);  
  14. if (iResult < 0) {  
  15. //数据接收出现错误,返回失败  
  16. printf("接收发生错误: %d\n", WSAGetLastError());  
  17. return -1;  
  18. }  
  19. if (iResult == 0) {  
  20. //对方关闭连接,返回已接收到的小于fixedlen的字节数  
  21. printf("连接关闭\n");  
  22. return fixedlen - cnt;  
  23. }  
  24. //printf("接收到的字节数: %d\n", iResult);  
  25. //接收缓存指针向后移动  
  26. recvbuf += iResult;  
  27. //更新cnt值  
  28. cnt -= iResult;  
  29. }  
  30. return fixedlen;  
  31. }  
  32. int recvvl(SOCKET s, char * recvbuf, unsigned int recvbufLen)  
  33. {  
  34. int iResult;//存储单次recv操作的返回值  
  35. unsigned int reclen; //用于存储报文头部存储的长度信息  
  36. //获取接收报文长度信息  
  37. iResult = recvn(s, (char *)&reclen, sizeof(unsigned int));  
  38. if (iResult != sizeof(unsigned int))   
  39. {  
  40. /*如果长度字段在接收时没有返回一个整型数据就返回(连接关闭) 
  41. 或-1(发生错误)*/  
  42. if (iResult == -1) {  
  43. printf("接收发生错误: %d\n", WSAGetLastError());  
  44. return -1;  
  45. }  
  46. else {  
  47. printf("连接关闭\n");  
  48. return 0;  
  49. }  
  50. }  
  51. //转换网络字节顺序到主机字节顺序  
  52. reclen = ntohl(reclen); if (reclen > recvbufLen) {  
  53. //如果recvbuf没有足够的空间存储变长消息,则接收该消息并丢弃,返回错误  
  54. while (reclen > 0) {  
  55. iResult = recvn(s, recvbuf, recvbufLen);  
  56. if (iResult != recvbufLen) {  
  57. //如果变长消息在接收时没有返回足够的数据就返回(连接关闭)或-1(发生错误)  
  58. if (iResult == -1) {  
  59. printf("接收发生错误: %d\n", WSAGetLastError());  
  60. return -1;  
  61. }  
  62. else {  
  63. printf("连接关闭\n");  
  64. return 0;  
  65. }  
  66. }  
  67. reclen -= recvbufLen;  
  68. //处理最后一段数据长度  
  69. if (reclen < recvbufLen)  
  70. recvbufLen = reclen;  
  71. }  
  72. printf("可变长度的消息超出预分配的接收缓存\r\n");  
  73. return -1;  
  74. }  
  75. //接收可变长消息  
  76. iResult = recvn(s, recvbuf, reclen);  
  77. if (iResult != reclen) {  
  78. /*(如果消息在接收时没有返回足够的数据就返回(连接关闭)或-1(发 
  79. 生错误)*/  
  80. if (iResult == -1) {  
  81. printf("接收发生错误: %d\n", WSAGetLastError());  
  82. return -1;  
  83. }  
  84. else {  
  85. printf("连接关闭\n");  
  86. return 0;  
  87. }  
  88. }  
  89. return iResult;  
  90. }  
  91. void str_echo(SOCKET s)  
  92. {  
  93. SOCKET sConnfd = s;  
  94. int retVal = 0;  
  95. char recvBuf[MAXLINE];//接收缓存    
  96. char sendBuf[MAXLINE];//发送缓存    
  97. unsigned int sendDataLen = 0; //发送数据长度    
  98. unsigned int bufLen = MAXLINE;  
  99. while (TRUE)  //循环接收数据    
  100. {  
  101. memset(recvBuf, 0, MAXLINE);  
  102. memset(sendBuf, 0, MAXLINE);  
  103. retVal = recvvl(sConnfd, recvBuf, bufLen);  
  104. if (retVal == SOCKET_ERROR)  
  105. {  
  106. printf("recvvl函数调用错误,错误号:%d\n", WSAGetLastError());  
  107. closesocket(sConnfd);  
  108. WSACleanup();  
  109. return;  
  110. }  
  111. sprintf(sendBuf, "echo:%s", recvBuf);  
  112. printf("%s\n", sendBuf);  
  113. sendDataLen = (unsigned int)strlen(sendBuf);  
  114. sendDataLen = htonl(sendDataLen);  
  115. retVal = send(sConnfd, (char*)&sendDataLen, sizeof(unsigned int), 0);  
  116. if (SOCKET_ERROR == retVal)  
  117. {  
  118. printf("send函数调用错误,错误号:%d\n", WSAGetLastError());  
  119. closesocket(sConnfd);  
  120. WSACleanup();  
  121. return ;  
  122. }  
  123. retVal = send(sConnfd, sendBuf, strlen(sendBuf), 0);  
  124. if (SOCKET_ERROR == retVal)  
  125. {  
  126. printf("send函数调用错误,错误号:%d\n", WSAGetLastError());  
  127. closesocket(sConnfd);  
  128. WSACleanup();  
  129. return;  
  130. }  
  131. }  
  132. }  
  133. int main()  
  134. {  
  135. WSADATA wsd; //WSADATA变量  
  136. SOCKET sListenfd, sConnectfd;//服务器监听套接字和连接套接字  
  137. int iClilen = 0;//连接客户端数  
  138. DWORD dwThreadId = 0;  
  139. sockaddr_in Cliaddr, Servaddr;//通信地址  
  140. DWORD dwError = 0;  
  141. HLOCAL hHlocal = NULL;  
  142. HANDLE hThread = NULL;  
  143. //初始化套结字动态库  
  144. if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)  
  145. {  
  146. printf("WSAStartup failed!\n");  
  147. return -1;  
  148. }  
  149. sListenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  
  150. if (INVALID_SOCKET == sListenfd)  
  151. {  
  152. printf("socket failed!\n");  
  153. WSACleanup();//释放套接字资源;  
  154. return  -1;  
  155. }  
  156. memset(&Servaddr, 0, sizeof(Servaddr));  
  157. memset(&Cliaddr, 0, sizeof(Cliaddr));  
  158. //服务器监听套接字地址   
  159. Servaddr.sin_family = AF_INET;  
  160. Servaddr.sin_port = htons(SERVER_PORT);  
  161. Servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//将IP地址指定为INADDR_ANY,允许套接字向任何分配给本地机器的IP地址发送或接收数据。  
  162. //绑定监听套接字  
  163. if (SOCKET_ERROR == bind(sListenfd, (SOCKADDR *)&Servaddr, sizeof(Servaddr)))  
  164. {  
  165. dwError = WSAGetLastError();  
  166. printf("bind函数调用错误,错误号: %d\n", dwError);  
  167. closesocket(sListenfd); //关闭套接字  
  168. WSACleanup();           //释放套接字资源  
  169. return -1;  
  170. }  
  171. if (SOCKET_ERROR == listen(sListenfd, LISTENQ))  
  172. {  
  173. dwError = WSAGetLastError();  
  174. printf("listen函数调用错误,错误号: %d\n", dwError);  
  175. closesocket(sListenfd); //关闭套接字  
  176. WSACleanup();           //释放套接字资源  
  177. return -1;  
  178. }  
  179. for (;;)  
  180. {  
  181. iClilen = sizeof(Cliaddr);  
  182. printf("等待连接~n(*≧▽≦*)n\n");  
  183. sConnectfd = accept(sListenfd, (SOCKADDR *)&Cliaddr, &iClilen);  
  184. if (SOCKET_ERROR == sConnectfd)  
  185. {  
  186. dwError = WSAGetLastError();  
  187. printf("accept函数调用错误,错误号: %d\n", dwError);  
  188. continue;  
  189. }  
  190. else  
  191. {  
  192. printf("接收到新连接,连接套接口为:%d\n", sConnectfd);  
  193. str_echo(sConnectfd);  
  194. }  
  195. }  
  196. closesocket(sListenfd);  
  197. return 0;  
  198. }  

5)基于流式套接字的并发服务器设计

服务器

  1. #include<stdio.h>  
  2. #include<winsock2.h>  
  3. #pragma comment(lib, "WS2_32")  // 链接到WS2_32.lib  
  4. #define SERVER_PORT 4500  
  5. #define MAXLINE 1024  
  6. #define LISTENQ 1024    //监听队列长度  
  7. DWORD WINAPI str_echo(LPVOID sd)  
  8. {  
  9. char sendBuf[MAXLINE];  //发送缓冲区  
  10. char recvBuf[MAXLINE];  //接收缓冲区  
  11. memset(sendBuf, 0, MAXLINE);  
  12. memset(recvBuf, 0, MAXLINE);  
  13. SOCKET  sConnectfd = (SOCKET)(LPVOID)sd;    //客户端套接字  
  14. int retVal;  
  15. while (TRUE)   
  16. {  
  17. retVal = recv(sConnectfd, recvBuf, MAXLINE, 0);  
  18. if (retVal == SOCKET_ERROR)  
  19. {  
  20. int dwError = WSAGetLastError();//错误代码  
  21. printf("recv函数调用错误,错误号: %d\n", dwError);  
  22. closesocket(sConnectfd);  
  23. break;  
  24. }  
  25. sprintf_s(sendBuf, "%d echo:%s", sConnectfd,recvBuf);//加echo  
  26. printf("%s", sendBuf);  
  27. retVal = send(sConnectfd, sendBuf, strlen(sendBuf), 0);  
  28. if (SOCKET_ERROR == retVal)  
  29. {  
  30. int dwError = WSAGetLastError();//错误代码  
  31. printf("send函数调用错误,错误号: %d\n", dwError);  
  32. closesocket(sConnectfd);  
  33. break;  
  34. }  
  35. }  
  36. closesocket(sConnectfd);  
  37. return 0;  
  38. }  
  39. int main()  
  40. {  
  41. WSADATA wsd; //WSADATA变量  
  42. SOCKET sListenfd, sConnectfd;//监听套接字\连接套接字  
  43. sockaddr_in Cliaddr, Servaddr;//通信地址  
  44. DWORD dwError = 0;  
  45. int iClilen = 0;//客户端地址长度  
  46. int retVal;  
  47. HANDLE  hThread = NULL; //新线程  
  48. //初始化套结字动态库  
  49. if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)  
  50. {  
  51. printf("WSAStartup failed!\n");  
  52. return -1;  
  53. }  
  54. //创建监听套接字  
  55. sListenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  
  56. if (INVALID_SOCKET == sListenfd)  
  57. {  
  58. printf("socket failed!\n");  
  59. WSACleanup();//释放套接字资源;  
  60. return  -1;  
  61. }  
  62. memset(&Servaddr, 0, sizeof(Servaddr));  
  63. memset(&Cliaddr, 0, sizeof(Cliaddr));  
  64. //服务器监听套接字地址   
  65. Servaddr.sin_family = AF_INET;  
  66. Servaddr.sin_port = htons(SERVER_PORT);  
  67. Servaddr.sin_addr.s_addr = htonl(INADDR_ANY);//将IP地址指定为INADDR_ANY,允许套接字向任何分配给本地机器的IP地址发送或接收数据。  
  68. //绑定监听套接字  
  69. if (SOCKET_ERROR == bind(sListenfd, (SOCKADDR *)&Servaddr, sizeof(Servaddr)))  
  70. {  
  71. dwError = WSAGetLastError();  
  72. printf("bind函数调用错误,错误号: %d\n", dwError);  
  73. closesocket(sListenfd); //关闭套接字  
  74. WSACleanup();           //释放套接字资源  
  75. return -1;  
  76. }  
  77. if (SOCKET_ERROR == listen(sListenfd, LISTENQ))  
  78. {  
  79. dwError = WSAGetLastError();  
  80. printf("listen函数调用错误,错误号: %d\n", dwError);  
  81. closesocket(sListenfd); //关闭套接字  
  82. WSACleanup();           //释放套接字资源  
  83. return -1;  
  84. }  
  85. for (;;)  
  86. {  
  87. iClilen = sizeof(Cliaddr);  
  88. printf("等待连接~n(*≧▽≦*)n\n");  
  89. sConnectfd = accept(sListenfd, (SOCKADDR *)&Cliaddr, &iClilen);  
  90. if (SOCKET_ERROR == sConnectfd)  
  91. {  
  92. dwError = WSAGetLastError();  
  93. printf("connect函数调用错误,错误号: %d\n", dwError);  
  94. continue;  
  95. }  
  96. else  
  97. {  
  98. printf("接收到新连接,连接套接口为:%d\n", sConnectfd);  
  99. hThread = CreateThread(NULL, 0, str_echo, (LPVOID)sConnectfd, 0, NULL);  
  100. if (hThread == NULL)  
  101. {  
  102. printf("创建线程失败!\n");  
  103. }  
  104. else  
  105. {  
  106. printf("创建线程成功!\n");  
  107. }  
  108. }  
  109. }  
  110. closesocket(sListenfd);  
  111. return 0;  
  112. }  

客户端

  1. //180201121-郑中华  
  2. #include <stdio.h>  
  3. #include <winsock2.h>  
  4. #pragma comment(lib, "WS2_32")  // 链接到WS2_32.lib  
  5. #define SERVER_PORT  4500    
  6. #define MAXLINE 1024   
  7. //接收一行数据  
  8. BOOL recvline(SOCKET S, char* buf)  
  9. {  
  10. BOOL retval = TRUE; //返回值    
  11. BOOL bLineEnd = FALSE;//一行读取结束    
  12. int nReadLen = 0; //读入字节数    
  13. int nDataLen = 0; //数据长度    
  14. while (!bLineEnd)  
  15. {  
  16. nReadLen = recv(S, buf, MAXLINE, 0);  
  17. if (nReadLen == SOCKET_ERROR)  
  18. {  
  19. int dwError = WSAGetLastError();   // 获取错误代码      
  20. printf("recv函数调用错误,错误号: %d\n", dwError);  
  21. retval = FALSE;   // 读取数据失败      
  22. break;              // 跳出循环      
  23. }  
  24. if (0 == nReadLen)  
  25. {  
  26. retval = FALSE; //读取数据失败    
  27. break;  
  28. }  
  29. for (int i = 0; i<nReadLen; i++)  
  30. {  
  31. if (‘\n‘ == *(buf + i))  
  32. bLineEnd = TRUE;  
  33. }  
  34. }  
  35. return retval;  
  36. }  
  37. void str_echo(FILE *fp, SOCKET sd)  
  38. {  
  39. char sendBuf[MAXLINE];  
  40. char recvBuf[MAXLINE];  
  41. memset(sendBuf, 0, MAXLINE);  
  42. memset(recvBuf, 0, MAXLINE);  
  43. int retVal = 0;  
  44. while (fgets(sendBuf, MAXLINE, fp) != NULL)  
  45. {  
  46. if (sendBuf[0] == ‘q‘)  
  47. {  
  48. printf("程序退出!");  
  49. return;  
  50. }  
  51. if (send(sd, sendBuf, sizeof(sendBuf), 0) <= 0)  
  52. {  
  53. printf("send函数调用错误,错误号:%d\n", WSAGetLastError());  
  54. closesocket(sd);  
  55. WSACleanup();  
  56. return;  
  57. }  
  58. if (!recvline(sd, recvBuf))  
  59. {  
  60. closesocket(sd);  
  61. WSACleanup();  
  62. return;  
  63. }  
  64. //打印收到的回显字符串    
  65. fputs(recvBuf, stdout);  
  66. //清空缓冲区  
  67. memset(recvBuf, 0, MAXLINE);  
  68. }  
  69. }  
  70. int main()  
  71. {  
  72. WSADATA wsd; //WSADATA变量  
  73. SOCKET sConnectfd;  
  74. sockaddr_in Servaddr;  
  75. DWORD dwError = 0;  
  76. memset(&Servaddr, 0, sizeof(Servaddr));  
  77. //初始化套结字动态库  
  78. if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)  
  79. {  
  80. printf("WSAStartup failed!\n");  
  81. return -1;  
  82. }  
  83. //创建套接字  
  84. sConnectfd = socket(AF_INET, SOCK_STREAM, 0);  
  85. if (INVALID_SOCKET == sConnectfd)  
  86. {  
  87. printf("socket failed!\n");  
  88. WSACleanup();//释放套接字资源  
  89. return  -1;  
  90. }  
  91. //设置服务器地址  
  92. Servaddr.sin_family = AF_INET;  
  93. Servaddr.sin_port = htons(SERVER_PORT);  
  94. Servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");  
  95. //连接服务器  
  96. if (SOCKET_ERROR == connect(sConnectfd, (SOCKADDR *)&Servaddr, sizeof(Servaddr)))  
  97. {  
  98. dwError = WSAGetLastError();  
  99. printf("connect函数调用错误,错误号: %d\n", dwError);  
  100. return -1;  
  101. }  
  102. printf("连接成功~");  
  103. str_echo(stdin, sConnectfd);  
  104. closesocket(sConnectfd);  
  105. return 0;  
  106. }  

3.用户使用说明(输入 / 输出规定)

1基于流式套接字的时间同步服务器设计

用户与服务器成功建立连接之后,等待1000毫秒,便显示当前时间,而后服务器方主动关闭连接。

客户端输出“对方连接关闭,退出”。

2基于流式套接字的服务器回射程序设计

④ 客户端接收数据以recvline接收一行数据

输入:用户与服务器成功建立连接之后,即可输入要发送的消息或字符’q’,按“回车”键;

输出:客户端原样显示服务器回射的消息。若输入为q”,则显示“程序退出!”。

⑤ 服务器接收数据以recvn定长方式接收

输入: a.首先在服务器输入定长接收数据长度大小n,按“回车”键;

b.用户与服务器成功建立连接之后,即可输入要发送的消息或字符’q’,按“回车”键;

输出:客户端按n个字符一组(最后一组消息字符长度小于等于n)显示刚刚发送的消息。若输入为“q”,则显示“程序退出!”。

⑥ 服务器和客户端接收数据均以recvvl变长方式接收

输入:用户与服务器成功建立连接之后,即可输入要发送的消息或字符’q’,按“回车”键;

输出:客户端显示echo:”并连接刚刚发送的消息。若输入为“q”,则显示“程序退出!”。

4)基于流式套接字的并发服务器设计

输入:用户与服务器成功建立连接之后,即可输入要发送的消息或字符’q’,按“回车”键;

输出:客户端显示“端口号 echo:”并连接刚刚发送的消息。若输入为“q”,则显示“程序退出!”。

4.运行结果(要求截图)

(1) 基于流式套接字的时间同步服务器设计

时间同步服务器-客户端运行结果如图1-3所示。

 

1-3  时间同步服务器-客户端运行结果截图

客户端与服务器成功建立连接之后,等待1000毫秒,便显示当前时间“Thu Dec 28 17:56:33 2017”,而后服务器方主动关闭连接,客户端输出“对方连接关闭,退出”。

2基于流式套接字的服务器回射程序设计

⑦ 客户端接收数据以recvline接收一行数据

客户端接收数据以recvline接收一行数据的回射服务器-客户端运行结果如图1-4所示。

 

1-4  recvline客户端方式的服务器-客户端运行结果截图

a.用户与服务器成功建立连接之后,发送“你好呀”,按“回车”键,服务器显示“264 接收到的内容:你好呀”,同时客户端回射消息“你好呀”;

b.如同a,用户依次发送消息“helloWorld”、“hiahia”,并回射相应消息;

c.用户最后输入“q”,则显示“程序退出!”,服务器端继续等待新的连接。

⑧ 服务器接收数据以recvn定长方式接收

服务器以recvn定长方式接收数据的回射服务器-客户端运行结果如图1-5所示。

 

1-5  recvn服务器方式的服务器-客户端运行结果截图

a.打开服务器等待连接,并设置服务器接收数据长度4

b.用户与服务器成功建立连接之后,发送“你好呀”,按“回车”键,服务器显示“200 接收到的内容:你好 \n200 接收到的内容:呀 \n”,同时客户端回射消息“你好 \n\n”;

c.如同a,用户依次发送消息“helloWorld”、“hiahia”,并按4个字符一组回射相应消息;

d.用户最后输入“q”,则显示“程序退出!”,服务器端继续等待新的连接,并且可以设置新的定长接收长度。

⑨ 服务器和客户端接收数据均以recvvl变长方式接收

服务器和客户端均以recvvl变长方式接收数据的回射服务器-客户端运行结果如图1-6所示。

 

1-6  recvvl接收方式的服务器-客户端运行结果截图

a.用户与服务器成功建立连接之后,发送“你好呀”,按“回车”键,服务器显示“echo:你好呀”,同时客户端回射消息“echo:你好呀”;

b.如同a,用户依次发送消息“helloWorld”、“hiahia”,并按格式回射相应消息;

c.用户最后输入“q”,则显示“程序退出!”,服务器端继续等待新的连接

4)基于流式套接字的并发服务器设计

基于流式套接字的并发服务器-客户端运行结果如图1-7所示。

 

1-7  并发服务器-客户端运行结果截图

a.首先打开服务器等待连接,接着打开三个客户端,服务器与其成功建立连接,分配端口,并为其创建三个线程;

b.端口号为296的用户首先与服务器进行通信,发送“你好呀”,按“回车”键,服务器显示“296 echo:你好呀”,同时客户端回射消息“296 echo:你好呀”;

c.如同b,不同用户依次发送消息“helloWorld”、“hiahia”、“emmmmmmmmmm”、“23333”、“1234567”、“emmm”,并在对应用户窗口回射相应的端口号连接“echo:”连接刚刚发送的消息;

d.其中端口号为192用户最后输入“q”,则显示“程序退出!”,服务器端则断开与端口号为192的用户的连接;

e端口号为296的用户继续与服务器进行通信,发送“emmm”,按“回车”键,服务器显示“296 echoemmm”,同时客户端回射消息“296 echoemmm”,服务器与其他用户通信正常;

f.此时,再接入一个用户,为该用户分配端口号288,发送“嘻嘻”,按“回车”键,服务器显示“288 echo:嘻嘻”,同时客户端回射消息“288 echo:嘻嘻”,运行正确。

 

5.实验中遇到问题及解决

  1. 遇到VS报错创建备份文件Client\Backup\Client.sln 时出错,原因是路径问题,VS对此项目要进行项目迁移后解决;

2. 遇到报错Error C4996 ‘inet_addr‘: Use inet_pton() or InetPton() instead or define _WINSOCK_DEPRECATED_NO_WA,原因是用到的函数比较旧,取消编译器的SDL检查即可;

遇到报错:‘strcpy‘: This function or variable may be unsafe.找到【项目属性】,点击【C++】里的【预处理器】,对【预处理器】进行编辑,在里面加入一段代码:_CRT_SECURE_NO_WARNINGS;

  1. gets_s()函数不接收一个参数,查阅文档后发现第一个参数为数据,第二个为最大长度,设置为999解决了问题;
  2. 设计并发时函数传参错误,误把常量当成指针;
  3. 编程时要注意常量的初始化,否则会导致一些函数传参时的错误;
  4. 不会用的函数要敢于直接查官方文档;

 备注:仅供参考

实验1 基于流式套接字的网络程序设计(计算机网络Ⅱ)

原文:https://www.cnblogs.com/pandazheng/p/13966072.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!