实验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()前等于定长值,之后随着接收的推进逐渐递减,直到最后一次数据接收后,cnt为0时退出循环接收过程。
③ 服务器和客户端接收数据均以recvvl变长方式接收
该实验采用长度字段标记变长消息长度,这样就把变长数据传输问题转换为两次定长数据接收问题。可变长消息的格式如图1-2所示。
图1-2 可变长度消息的格式
在数据发送时,首先发送定长的消息头声明本次传输的消息长度,再发送变长的消息体;在数据接收时,接收数据的应用程序把消息读取分成两个步骤:首先接收固定长度的消息头,从消息头中抽取出可变消息体的长度,然后再以定长数据接收的方式读取可变长度部分。
(4)基于流式套接字的并发服务器设计
实现基于流式套接字的并发服务器编程模型如(1)流式套接字编程模型。为了实现服务器同时为多个客户提供服务,服务器在调用accept()函数接收客户端的请求后,创建一个线程,每个客户端在各自的线程中与服务器进行读写数据。
2.程序清单(要求有详细的注释)
(1)基于流式套接字的时间同步服务器设计
①服务器
- #include <stdio.h>
- #include <time.h>
- #include "winsock2.h"
- #pragma comment(lib, "ws2_32.lib")
- #define BUF_SZIE 64 //接收缓冲区长度
- #define LISTENQ 1024 //监听队列长度
- int main()
- {
- WSADATA wsd; //WSADATA变量
- SOCKET sServer; //服务器套接字
- SOCKET sClient; //客户端套接字
- SOCKADDR_IN addrServ;; //服务器地址
- char buf[BUF_SZIE]; //接收数据缓冲区
- int retVal; //返回值
- time_t ticks;
- //初始化套结字动态库
- if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
- {
- printf("WSAStartup failed!\n");
- return -1;
- }
- //创建套接字
- sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- if (INVALID_SOCKET == sServer)
- {
- printf("socket failed!\n");
- WSACleanup();//释放套接字资源;
- return -1;
- }
- //服务器套接字地址
- addrServ.sin_family = AF_INET;
- addrServ.sin_port = htons(4567);
- addrServ.sin_addr.s_addr = INADDR_ANY;//将IP地址指定为INADDR_ANY,允许套接字向任何分配给本地机器的IP地址发送或接收数据。
- //绑定套接字
- retVal = bind(sServer, (LPSOCKADDR)&addrServ, sizeof(SOCKADDR_IN));
- if (SOCKET_ERROR == retVal)
- {
- printf("bind failed!\n");
- closesocket(sServer); //关闭套接字
- WSACleanup(); //释放套接字资源;
- return -1;
- }
- //开始监听
- retVal = listen(sServer, LISTENQ);
- if (SOCKET_ERROR == retVal)
- {
- printf("listen failed!\n");
- closesocket(sServer); //关闭套接字
- WSACleanup(); //释放套接字资源;
- return -1;
- }
- for (;;)
- {
- //接受客户端请求
- sClient = accept(sServer, NULL, NULL);
- if (INVALID_SOCKET == sClient)
- {
- printf("accept failed!\n");
- closesocket(sServer); //关闭套接字
- WSACleanup(); //释放套接字资源;
- return -1;
- }
- //获取当前时间
- ticks = time(NULL);
- memset(buf, 0, sizeof(buf));
- sprintf(buf, "%.24s\r\n", ctime(&ticks));
- printf("获取当前系统时间: %s\n", buf);
- //发送时间
- retVal = send(sClient, buf, strlen(buf), 0);
- if (SOCKET_ERROR == retVal)
- {
- printf("send 函数调用错误,错误号: %d\n", WSAGetLastError());
- closesocket(sClient);
- WSACleanup();
- return -1;
- }
- printf("向客户端发送时间成功\n");
- // 停止连接,不再发送数据
- retVal = shutdown(sClient, SD_SEND);
- if (retVal == SOCKET_ERROR)
- {
- printf("shutdown 函数调用错误,错误号: %d\n", WSAGetLastError());
- closesocket(sClient);
- WSACleanup();
- return -1;
- }
- // 关闭套接字
- closesocket(sClient);
- printf("主动关闭连接\n");
- }
- //退出
- closesocket(sClient); //关闭套接字*/
- closesocket(sServer); //关闭套接字
- WSACleanup(); //释放套接字资源;
- return 0;
- }
②客户端
- #include "winsock2.h"
- #include <stdio.h>
- #pragma comment(lib, "ws2_32.lib")
- #define BUF_SZIE 64
- int main()
- {
- WSADATA wsd; //WSADATA变量
- SOCKET sHost; //服务器套接字
- SOCKADDR_IN servAddr; //服务器地址
- char buf[BUF_SZIE]; //接收数据缓冲区
- int retVal; //返回值
- //初始化套结字动态库
- if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
- {
- printf("WSAStartup failed!\n");
- return -1;
- }
- //创建套接字
- sHost = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- if (INVALID_SOCKET == sHost)
- {
- printf("socket failed!\n");
- WSACleanup();//释放套接字资源
- return -1;
- }
- //设置服务器地址
- servAddr.sin_family = AF_INET;
- servAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
- servAddr.sin_port = htons((short)4567);
- int nServAddlen = sizeof(servAddr);
- //连接服务器
- retVal = connect(sHost, (LPSOCKADDR)&servAddr, sizeof(servAddr));
- if (SOCKET_ERROR == retVal)
- {
- printf("connect failed!\n");
- closesocket(sHost); //关闭套接字
- WSACleanup(); //释放套接字资源
- return -1;
- }
- // 持续接收数据,直到服务器方关闭连接
- memset(&buf, 0, sizeof(buf));
- printf("当前时间是:");
- do {
- Sleep(1000);
- retVal = recv(sHost, buf, BUF_SZIE, 0);
- if (retVal > 0)
- printf("%s", buf);
- else {
- if (retVal == 0)
- printf("对方连接关闭,退出\n");
- else
- printf("recv 函数调用错误,错误号: %d\n", WSAGetLastError());
- }
- memset(&buf, 0, sizeof(buf));
- } while (retVal > 0);
- //退出
- closesocket(sHost); //关闭套接字
- WSACleanup(); //释放套接字资源
- return 0;
- }
(2)基于流式套接字的服务器回射程序设计——客户端接收数据以recvline接收一行数据
①服务器
- #include <stdio.h>
- #include "winsock2.h"
- #pragma comment(lib, "ws2_32.lib")
- #define SERVER_PORT 4567
- #define MAXLINE 1024 //接收缓冲区长度
- #define LISTENQ 1024 //监听队列长度
- void str_echo(SOCKET s)
- {
- SOCKET sConnfd = s;
- int n = 0;
- char szBuf[MAXLINE];
- int retVal = 0;
- while ((n = recv(sConnfd, szBuf, sizeof(szBuf), 0)) >0)
- {
- printf("%d 接收到的内容:%s\n", sConnfd, szBuf);
- retVal = send(sConnfd, szBuf, n, 0);
- if (SOCKET_ERROR == retVal)
- {
- int dwError = WSAGetLastError();
- printf("send函数调用错误,错误号: %d\n", dwError);
- return;
- }
- }
- closesocket(sConnfd);
- }
- int main()
- {
- WSADATA wsd; //WSADATA变量
- SOCKET sListenfd, sConnectfd;//服务器监听套接字和连接套接字
- int iClilen = 0;//连接客户端数
- DWORD dwThreadId = 0;
- sockaddr_in Cliaddr, Servaddr;//通信地址
- DWORD dwError = 0;
- HLOCAL hHlocal = NULL;
- HANDLE hThread = NULL;
- //初始化套结字动态库
- if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
- {
- printf("WSAStartup failed!\n");
- return -1;
- }
- //创建监听套接字
- sListenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- if (INVALID_SOCKET == sListenfd)
- {
- printf("socket failed!\n");
- WSACleanup();//释放套接字资源;
- return -1;
- }
- memset(&Servaddr, 0, sizeof(Servaddr));
- memset(&Cliaddr, 0, sizeof(Cliaddr));
- //服务器监听套接字地址
- 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(sListenfd, (SOCKADDR *)&Servaddr, sizeof(Servaddr)))
- {
- dwError = WSAGetLastError();
- printf("bind函数调用错误,错误号: %d\n", dwError);
- closesocket(sListenfd); //关闭套接字
- WSACleanup(); //释放套接字资源
- return -1;
- }
- if (SOCKET_ERROR == listen(sListenfd, LISTENQ))
- {
- dwError = WSAGetLastError();
- printf("listen函数调用错误,错误号: %d\n", dwError);
- closesocket(sListenfd); //关闭套接字
- WSACleanup(); //释放套接字资源
- return -1;
- }
- for (;;)
- {
- iClilen = sizeof(Cliaddr);
- printf("等待连接~n(*≧▽≦*)n\n");
- sConnectfd = accept(sListenfd, (SOCKADDR *)&Cliaddr, &iClilen);
- if (SOCKET_ERROR == sConnectfd)
- {
- dwError = WSAGetLastError();
- printf("connect函数调用错误,错误号: %d\n", dwError);
- continue;
- }
- else
- {
- printf("接收到新连接,连接套接口为:%d\n",sConnectfd);
- str_echo(sConnectfd);
- }
- }
- closesocket(sListenfd);
- return 0;
- }
②客户端
- #include <stdio.h>
- #include <winsock2.h>
- #pragma comment(lib, "WS2_32") // 链接到WS2_32.lib
- #define SERVER_PORT 4567
- #define MAXLINE 1024
- //接收一行数据
- BOOL recvline(SOCKET S, char* buf)
- {
- BOOL retval = TRUE; //返回值
- BOOL bLineEnd = FALSE;//一行读取结束
- int nReadLen = 0; //读入字节数
- int nDataLen = 0; //数据长度
- while (!bLineEnd)
- {
- nReadLen = recv(S, buf, MAXLINE, 0);
- if (nReadLen == SOCKET_ERROR)
- {
- int dwError = WSAGetLastError(); // 获取错误代码
- printf("recv函数调用错误,错误号: %d\n", dwError);
- retval = FALSE; // 读取数据失败
- break; // 跳出循环
- }
- if (0 == nReadLen)
- {
- retval = FALSE; //读取数据失败
- break;
- }
- for (int i = 0; i<nReadLen; i++)
- {
- if (‘\n‘ == *(buf + i))
- bLineEnd = TRUE;
- }
- }
- return retval;
- }
- void str_echo(FILE *fp, SOCKET sd)
- {
- char sendBuf[MAXLINE];
- char recvBuf[MAXLINE];
- memset(sendBuf, 0, MAXLINE);
- memset(recvBuf, 0, MAXLINE);
- int retVal = 0;
- while (fgets(sendBuf, MAXLINE, fp) != NULL)
- {
- if (sendBuf[0] == ‘q‘)
- {
- printf("程序退出!");
- return;
- }
- if (send(sd, sendBuf, sizeof(sendBuf), 0) <= 0)
- {
- printf("send函数调用错误,错误号:%d\n", WSAGetLastError());
- closesocket(sd);
- WSACleanup();
- return;
- }
- if (!recvline(sd, recvBuf))
- {
- closesocket(sd);
- WSACleanup();
- return;
- }
- //打印收到的回显字符串
- fputs(recvBuf, stdout);
- //清空缓冲区
- memset(recvBuf, 0, MAXLINE);
- }
- }
- int main()
- {
- WSADATA wsd; //WSADATA变量
- SOCKET sConnectfd;
- sockaddr_in Servaddr;
- DWORD dwError = 0;
- memset(&Servaddr, 0, sizeof(Servaddr));
- //初始化套结字动态库
- if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
- {
- printf("WSAStartup failed!\n");
- return -1;
- }
- //创建套接字
- sConnectfd = socket(AF_INET, SOCK_STREAM, 0);
- if (INVALID_SOCKET == sConnectfd)
- {
- printf("socket failed!\n");
- WSACleanup();//释放套接字资源
- return -1;
- }
- //设置服务器地址
- Servaddr.sin_family = AF_INET;
- Servaddr.sin_port = htons(SERVER_PORT);
- Servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
- //连接服务器
- if (SOCKET_ERROR == connect(sConnectfd, (SOCKADDR *)&Servaddr, sizeof(Servaddr)))
- {
- dwError = WSAGetLastError();
- printf("connect函数调用错误,错误号: %d\n", dwError);
- return -1;
- }
- printf("连接成功~");
- str_echo(stdin, sConnectfd);
- closesocket(sConnectfd);
- return 0;
- }
(3)基于流式套接字的服务器回射程序设计——服务器接收数据以recvn定长方式接收
①服务器
- #include <stdio.h>
- #include "winsock2.h"
- #pragma comment(lib, "ws2_32.lib")
- #define SERVER_PORT 4566
- #define MAXLINE 1024 //接收缓冲区长度
- #define LISTENQ 1024 //监听队列长度
- int recvn(SOCKET s, char * recvbuf, unsigned int fixedlen)
- {
- int iResult; //存储单次recv操作的返回值
- int cnt; //用于统计相对于固定长度,剩余多少字节尚未接收
- cnt = fixedlen;
- while (cnt > 0) {
- iResult = recv(s, recvbuf, cnt, 0);
- if (iResult < 0) {
- //数据接收出现错误,返回失败
- printf("接收发生错误: %d\n", WSAGetLastError());
- return -1;
- }
- if (iResult == 0) {
- //对方关闭连接,返回已接收到的小于fixedlen的字节数
- printf("连接关闭\n");
- return fixedlen - cnt;
- }
- //printf("接收到的字节数: %d\n", iResult);
- if (recvbuf[iResult - 1] == ‘\n‘)
- break;
- //接收缓存指针向后移动
- recvbuf += iResult;
- //更新cnt值
- cnt -= iResult;
- }
- return fixedlen;
- }
- int main()
- {
- WSADATA wsd; //WSADATA变量
- SOCKET sListenfd, sConnectfd;//服务器监听套接字和连接套接字
- int iClilen = 0;//连接客户端数
- DWORD dwThreadId = 0;
- sockaddr_in Cliaddr, Servaddr;//通信地址
- DWORD dwError = 0;
- HLOCAL hHlocal = NULL;
- HANDLE hThread = NULL;
- char szBuf[MAXLINE];//发送缓冲区
- int retVal = 0;
- int recvLen = 0;
- memset(szBuf, 0, MAXLINE);
- //初始化套结字动态库
- if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
- {
- printf("WSAStartup failed!\n");
- return -1;
- }
- sListenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- if (INVALID_SOCKET == sListenfd)
- {
- printf("socket failed!\n");
- WSACleanup();//释放套接字资源;
- return -1;
- }
- memset(&Servaddr, 0, sizeof(Servaddr));
- memset(&Cliaddr, 0, sizeof(Cliaddr));
- //服务器监听套接字地址
- 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(sListenfd, (SOCKADDR *)&Servaddr, sizeof(Servaddr)))
- {
- dwError = WSAGetLastError();
- printf("bind函数调用错误,错误号: %d\n", dwError);
- closesocket(sListenfd); //关闭套接字
- WSACleanup(); //释放套接字资源
- return -1;
- }
- if (SOCKET_ERROR == listen(sListenfd, LISTENQ))
- {
- dwError = WSAGetLastError();
- printf("listen函数调用错误,错误号: %d\n", dwError);
- closesocket(sListenfd); //关闭套接字
- WSACleanup(); //释放套接字资源
- return -1;
- }
- for (;;)
- {
- iClilen = sizeof(Cliaddr);
- printf("等待连接~n(*≧▽≦*)n\n");
- printf("请指定服务器接收数据长度:");
- scanf("%d", &recvLen);
- sConnectfd = accept(sListenfd, (SOCKADDR *)&Cliaddr, &iClilen);
- if (SOCKET_ERROR == sConnectfd)
- {
- dwError = WSAGetLastError();
- printf("connect函数调用错误,错误号: %d\n", dwError);
- continue;
- }
- else
- {
- printf("接收到新连接,连接套接口为:%d\n", sConnectfd);
- while (recvn(sConnectfd, szBuf, recvLen)>0)//循环接收数据
- {
- printf("%d 接收到的内容:%s\n", sConnectfd, szBuf);
- retVal = send(sConnectfd, szBuf, strlen(szBuf), 0);
- memset(szBuf, 0, MAXLINE);
- if (SOCKET_ERROR == retVal)
- {
- int dwError = WSAGetLastError();
- printf("send函数调用错误,错误号: %d\n", dwError);
- closesocket(sConnectfd);
- closesocket(sListenfd);
- WSACleanup();
- return -1;
- }
- }
- }
- }
- closesocket(sConnectfd);
- closesocket(sListenfd);
- WSACleanup();
- return 0;
- }
②客户端
该客户端代码同(2)中②客户端代码相同,即客户端接收数据以recvline接收一行数据。
(4)基于流式套接字的服务器回射程序设计——服务器和客户端接收数据均以recvvl变长方式接收
①服务器
- #include <stdio.h>
- #include "winsock2.h"
- #pragma comment(lib, "ws2_32.lib")
- #define SERVER_PORT 4560
- #define MAXLINE 1024 //接收缓冲区长度
- #define LISTENQ 1024 //监听队列长度
- int recvn(SOCKET s, char * recvbuf, unsigned int fixedlen)
- {
- int iResult; //存储单次recv操作的返回值
- int cnt; //用于统计相对于固定长度,剩余多少字节尚未接收
- cnt = fixedlen;
- while (cnt > 0) {
- iResult = recv(s, recvbuf, cnt, 0);
- if (iResult < 0) {
- //数据接收出现错误,返回失败
- printf("接收发生错误: %d\n", WSAGetLastError());
- return -1;
- }
- if (iResult == 0) {
- //对方关闭连接,返回已接收到的小于fixedlen的字节数
- printf("连接关闭\n");
- return fixedlen - cnt;
- }
- //printf("接收到的字节数: %d\n", iResult);
- //接收缓存指针向后移动
- recvbuf += iResult;
- //更新cnt值
- cnt -= iResult;
- }
- return fixedlen;
- }
- int recvvl(SOCKET s, char * recvbuf, unsigned int recvbufLen)
- {
- int iResult;//存储单次recv操作的返回值
- unsigned int reclen; //用于存储报文头部存储的长度信息
- //获取接收报文长度信息
- iResult = recvn(s, (char *)&reclen, sizeof(unsigned int));
- if (iResult != sizeof(unsigned int))
- {
- /*如果长度字段在接收时没有返回一个整型数据就返回(连接关闭)
- 或-1(发生错误)*/
- if (iResult == -1) {
- printf("接收发生错误: %d\n", WSAGetLastError());
- return -1;
- }
- else {
- printf("连接关闭\n");
- return 0;
- }
- }
- //转换网络字节顺序到主机字节顺序
- reclen = ntohl(reclen); if (reclen > recvbufLen) {
- //如果recvbuf没有足够的空间存储变长消息,则接收该消息并丢弃,返回错误
- while (reclen > 0) {
- iResult = recvn(s, recvbuf, recvbufLen);
- if (iResult != recvbufLen) {
- //如果变长消息在接收时没有返回足够的数据就返回(连接关闭)或-1(发生错误)
- if (iResult == -1) {
- printf("接收发生错误: %d\n", WSAGetLastError());
- return -1;
- }
- else {
- printf("连接关闭\n");
- return 0;
- }
- }
- reclen -= recvbufLen;
- //处理最后一段数据长度
- if (reclen < recvbufLen)
- recvbufLen = reclen;
- }
- printf("可变长度的消息超出预分配的接收缓存\r\n");
- return -1;
- }
- //接收可变长消息
- iResult = recvn(s, recvbuf, reclen);
- if (iResult != reclen) {
- /*(如果消息在接收时没有返回足够的数据就返回(连接关闭)或-1(发
- 生错误)*/
- if (iResult == -1) {
- printf("接收发生错误: %d\n", WSAGetLastError());
- return -1;
- }
- else {
- printf("连接关闭\n");
- return 0;
- }
- }
- return iResult;
- }
- void str_echo(SOCKET s)
- {
- SOCKET sConnfd = s;
- int retVal = 0;
- char recvBuf[MAXLINE];//接收缓存
- char sendBuf[MAXLINE];//发送缓存
- unsigned int sendDataLen = 0; //发送数据长度
- unsigned int bufLen = MAXLINE;
- while (TRUE) //循环接收数据
- {
- memset(recvBuf, 0, MAXLINE);
- memset(sendBuf, 0, MAXLINE);
- retVal = recvvl(sConnfd, recvBuf, bufLen);
- if (retVal == SOCKET_ERROR)
- {
- printf("recvvl函数调用错误,错误号:%d\n", WSAGetLastError());
- closesocket(sConnfd);
- WSACleanup();
- return;
- }
- sprintf(sendBuf, "echo:%s", recvBuf);
- printf("%s\n", sendBuf);
- sendDataLen = (unsigned int)strlen(sendBuf);
- sendDataLen = htonl(sendDataLen);
- retVal = send(sConnfd, (char*)&sendDataLen, sizeof(unsigned int), 0);
- if (SOCKET_ERROR == retVal)
- {
- printf("send函数调用错误,错误号:%d\n", WSAGetLastError());
- closesocket(sConnfd);
- WSACleanup();
- return ;
- }
- retVal = send(sConnfd, sendBuf, strlen(sendBuf), 0);
- if (SOCKET_ERROR == retVal)
- {
- printf("send函数调用错误,错误号:%d\n", WSAGetLastError());
- closesocket(sConnfd);
- WSACleanup();
- return;
- }
- }
- }
- int main()
- {
- WSADATA wsd; //WSADATA变量
- SOCKET sListenfd, sConnectfd;//服务器监听套接字和连接套接字
- int iClilen = 0;//连接客户端数
- DWORD dwThreadId = 0;
- sockaddr_in Cliaddr, Servaddr;//通信地址
- DWORD dwError = 0;
- HLOCAL hHlocal = NULL;
- HANDLE hThread = NULL;
- //初始化套结字动态库
- if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
- {
- printf("WSAStartup failed!\n");
- return -1;
- }
- sListenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- if (INVALID_SOCKET == sListenfd)
- {
- printf("socket failed!\n");
- WSACleanup();//释放套接字资源;
- return -1;
- }
- memset(&Servaddr, 0, sizeof(Servaddr));
- memset(&Cliaddr, 0, sizeof(Cliaddr));
- //服务器监听套接字地址
- 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(sListenfd, (SOCKADDR *)&Servaddr, sizeof(Servaddr)))
- {
- dwError = WSAGetLastError();
- printf("bind函数调用错误,错误号: %d\n", dwError);
- closesocket(sListenfd); //关闭套接字
- WSACleanup(); //释放套接字资源
- return -1;
- }
- if (SOCKET_ERROR == listen(sListenfd, LISTENQ))
- {
- dwError = WSAGetLastError();
- printf("listen函数调用错误,错误号: %d\n", dwError);
- closesocket(sListenfd); //关闭套接字
- WSACleanup(); //释放套接字资源
- return -1;
- }
- for (;;)
- {
- iClilen = sizeof(Cliaddr);
- printf("等待连接~n(*≧▽≦*)n\n");
- sConnectfd = accept(sListenfd, (SOCKADDR *)&Cliaddr, &iClilen);
- if (SOCKET_ERROR == sConnectfd)
- {
- dwError = WSAGetLastError();
- printf("accept函数调用错误,错误号: %d\n", dwError);
- continue;
- }
- else
- {
- printf("接收到新连接,连接套接口为:%d\n", sConnectfd);
- str_echo(sConnectfd);
- }
- }
- closesocket(sListenfd);
- return 0;
- }
②客户端
- #include <stdio.h>
- #include "winsock2.h"
- #pragma comment(lib, "ws2_32.lib")
- #define SERVER_PORT 4560
- #define MAXLINE 1024 //接收缓冲区长度
- #define LISTENQ 1024 //监听队列长度
- int recvn(SOCKET s, char * recvbuf, unsigned int fixedlen)
- {
- int iResult; //存储单次recv操作的返回值
- int cnt; //用于统计相对于固定长度,剩余多少字节尚未接收
- cnt = fixedlen;
- while (cnt > 0) {
- iResult = recv(s, recvbuf, cnt, 0);
- if (iResult < 0) {
- //数据接收出现错误,返回失败
- printf("接收发生错误: %d\n", WSAGetLastError());
- return -1;
- }
- if (iResult == 0) {
- //对方关闭连接,返回已接收到的小于fixedlen的字节数
- printf("连接关闭\n");
- return fixedlen - cnt;
- }
- //printf("接收到的字节数: %d\n", iResult);
- //接收缓存指针向后移动
- recvbuf += iResult;
- //更新cnt值
- cnt -= iResult;
- }
- return fixedlen;
- }
- int recvvl(SOCKET s, char * recvbuf, unsigned int recvbufLen)
- {
- int iResult;//存储单次recv操作的返回值
- unsigned int reclen; //用于存储报文头部存储的长度信息
- //获取接收报文长度信息
- iResult = recvn(s, (char *)&reclen, sizeof(unsigned int));
- if (iResult != sizeof(unsigned int))
- {
- /*如果长度字段在接收时没有返回一个整型数据就返回(连接关闭)
- 或-1(发生错误)*/
- if (iResult == -1) {
- printf("接收发生错误: %d\n", WSAGetLastError());
- return -1;
- }
- else {
- printf("连接关闭\n");
- return 0;
- }
- }
- //转换网络字节顺序到主机字节顺序
- reclen = ntohl(reclen); if (reclen > recvbufLen) {
- //如果recvbuf没有足够的空间存储变长消息,则接收该消息并丢弃,返回错误
- while (reclen > 0) {
- iResult = recvn(s, recvbuf, recvbufLen);
- if (iResult != recvbufLen) {
- //如果变长消息在接收时没有返回足够的数据就返回(连接关闭)或-1(发生错误)
- if (iResult == -1) {
- printf("接收发生错误: %d\n", WSAGetLastError());
- return -1;
- }
- else {
- printf("连接关闭\n");
- return 0;
- }
- }
- reclen -= recvbufLen;
- //处理最后一段数据长度
- if (reclen < recvbufLen)
- recvbufLen = reclen;
- }
- printf("可变长度的消息超出预分配的接收缓存\r\n");
- return -1;
- }
- //接收可变长消息
- iResult = recvn(s, recvbuf, reclen);
- if (iResult != reclen) {
- /*(如果消息在接收时没有返回足够的数据就返回(连接关闭)或-1(发
- 生错误)*/
- if (iResult == -1) {
- printf("接收发生错误: %d\n", WSAGetLastError());
- return -1;
- }
- else {
- printf("连接关闭\n");
- return 0;
- }
- }
- return iResult;
- }
- void str_echo(SOCKET s)
- {
- SOCKET sConnfd = s;
- int retVal = 0;
- char recvBuf[MAXLINE];//接收缓存
- char sendBuf[MAXLINE];//发送缓存
- unsigned int sendDataLen = 0; //发送数据长度
- unsigned int bufLen = MAXLINE;
- while (TRUE) //循环接收数据
- {
- memset(recvBuf, 0, MAXLINE);
- memset(sendBuf, 0, MAXLINE);
- retVal = recvvl(sConnfd, recvBuf, bufLen);
- if (retVal == SOCKET_ERROR)
- {
- printf("recvvl函数调用错误,错误号:%d\n", WSAGetLastError());
- closesocket(sConnfd);
- WSACleanup();
- return;
- }
- sprintf(sendBuf, "echo:%s", recvBuf);
- printf("%s\n", sendBuf);
- sendDataLen = (unsigned int)strlen(sendBuf);
- sendDataLen = htonl(sendDataLen);
- retVal = send(sConnfd, (char*)&sendDataLen, sizeof(unsigned int), 0);
- if (SOCKET_ERROR == retVal)
- {
- printf("send函数调用错误,错误号:%d\n", WSAGetLastError());
- closesocket(sConnfd);
- WSACleanup();
- return ;
- }
- retVal = send(sConnfd, sendBuf, strlen(sendBuf), 0);
- if (SOCKET_ERROR == retVal)
- {
- printf("send函数调用错误,错误号:%d\n", WSAGetLastError());
- closesocket(sConnfd);
- WSACleanup();
- return;
- }
- }
- }
- int main()
- {
- WSADATA wsd; //WSADATA变量
- SOCKET sListenfd, sConnectfd;//服务器监听套接字和连接套接字
- int iClilen = 0;//连接客户端数
- DWORD dwThreadId = 0;
- sockaddr_in Cliaddr, Servaddr;//通信地址
- DWORD dwError = 0;
- HLOCAL hHlocal = NULL;
- HANDLE hThread = NULL;
- //初始化套结字动态库
- if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
- {
- printf("WSAStartup failed!\n");
- return -1;
- }
- sListenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- if (INVALID_SOCKET == sListenfd)
- {
- printf("socket failed!\n");
- WSACleanup();//释放套接字资源;
- return -1;
- }
- memset(&Servaddr, 0, sizeof(Servaddr));
- memset(&Cliaddr, 0, sizeof(Cliaddr));
- //服务器监听套接字地址
- 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(sListenfd, (SOCKADDR *)&Servaddr, sizeof(Servaddr)))
- {
- dwError = WSAGetLastError();
- printf("bind函数调用错误,错误号: %d\n", dwError);
- closesocket(sListenfd); //关闭套接字
- WSACleanup(); //释放套接字资源
- return -1;
- }
- if (SOCKET_ERROR == listen(sListenfd, LISTENQ))
- {
- dwError = WSAGetLastError();
- printf("listen函数调用错误,错误号: %d\n", dwError);
- closesocket(sListenfd); //关闭套接字
- WSACleanup(); //释放套接字资源
- return -1;
- }
- for (;;)
- {
- iClilen = sizeof(Cliaddr);
- printf("等待连接~n(*≧▽≦*)n\n");
- sConnectfd = accept(sListenfd, (SOCKADDR *)&Cliaddr, &iClilen);
- if (SOCKET_ERROR == sConnectfd)
- {
- dwError = WSAGetLastError();
- printf("accept函数调用错误,错误号: %d\n", dwError);
- continue;
- }
- else
- {
- printf("接收到新连接,连接套接口为:%d\n", sConnectfd);
- str_echo(sConnectfd);
- }
- }
- closesocket(sListenfd);
- return 0;
- }
(5)基于流式套接字的并发服务器设计
①服务器
- #include<stdio.h>
- #include<winsock2.h>
- #pragma comment(lib, "WS2_32") // 链接到WS2_32.lib
- #define SERVER_PORT 4500
- #define MAXLINE 1024
- #define LISTENQ 1024 //监听队列长度
- DWORD WINAPI str_echo(LPVOID sd)
- {
- char sendBuf[MAXLINE]; //发送缓冲区
- char recvBuf[MAXLINE]; //接收缓冲区
- memset(sendBuf, 0, MAXLINE);
- memset(recvBuf, 0, MAXLINE);
- SOCKET sConnectfd = (SOCKET)(LPVOID)sd; //客户端套接字
- int retVal;
- while (TRUE)
- {
- retVal = recv(sConnectfd, recvBuf, MAXLINE, 0);
- if (retVal == SOCKET_ERROR)
- {
- int dwError = WSAGetLastError();//错误代码
- printf("recv函数调用错误,错误号: %d\n", dwError);
- closesocket(sConnectfd);
- break;
- }
- sprintf_s(sendBuf, "%d echo:%s", sConnectfd,recvBuf);//加echo
- printf("%s", sendBuf);
- retVal = send(sConnectfd, sendBuf, strlen(sendBuf), 0);
- if (SOCKET_ERROR == retVal)
- {
- int dwError = WSAGetLastError();//错误代码
- printf("send函数调用错误,错误号: %d\n", dwError);
- closesocket(sConnectfd);
- break;
- }
- }
- closesocket(sConnectfd);
- return 0;
- }
- int main()
- {
- WSADATA wsd; //WSADATA变量
- SOCKET sListenfd, sConnectfd;//监听套接字\连接套接字
- sockaddr_in Cliaddr, Servaddr;//通信地址
- DWORD dwError = 0;
- int iClilen = 0;//客户端地址长度
- int retVal;
- HANDLE hThread = NULL; //新线程
- //初始化套结字动态库
- if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
- {
- printf("WSAStartup failed!\n");
- return -1;
- }
- //创建监听套接字
- sListenfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- if (INVALID_SOCKET == sListenfd)
- {
- printf("socket failed!\n");
- WSACleanup();//释放套接字资源;
- return -1;
- }
- memset(&Servaddr, 0, sizeof(Servaddr));
- memset(&Cliaddr, 0, sizeof(Cliaddr));
- //服务器监听套接字地址
- 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(sListenfd, (SOCKADDR *)&Servaddr, sizeof(Servaddr)))
- {
- dwError = WSAGetLastError();
- printf("bind函数调用错误,错误号: %d\n", dwError);
- closesocket(sListenfd); //关闭套接字
- WSACleanup(); //释放套接字资源
- return -1;
- }
- if (SOCKET_ERROR == listen(sListenfd, LISTENQ))
- {
- dwError = WSAGetLastError();
- printf("listen函数调用错误,错误号: %d\n", dwError);
- closesocket(sListenfd); //关闭套接字
- WSACleanup(); //释放套接字资源
- return -1;
- }
- for (;;)
- {
- iClilen = sizeof(Cliaddr);
- printf("等待连接~n(*≧▽≦*)n\n");
- sConnectfd = accept(sListenfd, (SOCKADDR *)&Cliaddr, &iClilen);
- if (SOCKET_ERROR == sConnectfd)
- {
- dwError = WSAGetLastError();
- printf("connect函数调用错误,错误号: %d\n", dwError);
- continue;
- }
- else
- {
- printf("接收到新连接,连接套接口为:%d\n", sConnectfd);
- hThread = CreateThread(NULL, 0, str_echo, (LPVOID)sConnectfd, 0, NULL);
- if (hThread == NULL)
- {
- printf("创建线程失败!\n");
- }
- else
- {
- printf("创建线程成功!\n");
- }
- }
- }
- closesocket(sListenfd);
- return 0;
- }
②客户端
- //180201121-郑中华
- #include <stdio.h>
- #include <winsock2.h>
- #pragma comment(lib, "WS2_32") // 链接到WS2_32.lib
- #define SERVER_PORT 4500
- #define MAXLINE 1024
- //接收一行数据
- BOOL recvline(SOCKET S, char* buf)
- {
- BOOL retval = TRUE; //返回值
- BOOL bLineEnd = FALSE;//一行读取结束
- int nReadLen = 0; //读入字节数
- int nDataLen = 0; //数据长度
- while (!bLineEnd)
- {
- nReadLen = recv(S, buf, MAXLINE, 0);
- if (nReadLen == SOCKET_ERROR)
- {
- int dwError = WSAGetLastError(); // 获取错误代码
- printf("recv函数调用错误,错误号: %d\n", dwError);
- retval = FALSE; // 读取数据失败
- break; // 跳出循环
- }
- if (0 == nReadLen)
- {
- retval = FALSE; //读取数据失败
- break;
- }
- for (int i = 0; i<nReadLen; i++)
- {
- if (‘\n‘ == *(buf + i))
- bLineEnd = TRUE;
- }
- }
- return retval;
- }
- void str_echo(FILE *fp, SOCKET sd)
- {
- char sendBuf[MAXLINE];
- char recvBuf[MAXLINE];
- memset(sendBuf, 0, MAXLINE);
- memset(recvBuf, 0, MAXLINE);
- int retVal = 0;
- while (fgets(sendBuf, MAXLINE, fp) != NULL)
- {
- if (sendBuf[0] == ‘q‘)
- {
- printf("程序退出!");
- return;
- }
- if (send(sd, sendBuf, sizeof(sendBuf), 0) <= 0)
- {
- printf("send函数调用错误,错误号:%d\n", WSAGetLastError());
- closesocket(sd);
- WSACleanup();
- return;
- }
- if (!recvline(sd, recvBuf))
- {
- closesocket(sd);
- WSACleanup();
- return;
- }
- //打印收到的回显字符串
- fputs(recvBuf, stdout);
- //清空缓冲区
- memset(recvBuf, 0, MAXLINE);
- }
- }
- int main()
- {
- WSADATA wsd; //WSADATA变量
- SOCKET sConnectfd;
- sockaddr_in Servaddr;
- DWORD dwError = 0;
- memset(&Servaddr, 0, sizeof(Servaddr));
- //初始化套结字动态库
- if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
- {
- printf("WSAStartup failed!\n");
- return -1;
- }
- //创建套接字
- sConnectfd = socket(AF_INET, SOCK_STREAM, 0);
- if (INVALID_SOCKET == sConnectfd)
- {
- printf("socket failed!\n");
- WSACleanup();//释放套接字资源
- return -1;
- }
- //设置服务器地址
- Servaddr.sin_family = AF_INET;
- Servaddr.sin_port = htons(SERVER_PORT);
- Servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");
- //连接服务器
- if (SOCKET_ERROR == connect(sConnectfd, (SOCKADDR *)&Servaddr, sizeof(Servaddr)))
- {
- dwError = WSAGetLastError();
- printf("connect函数调用错误,错误号: %d\n", dwError);
- return -1;
- }
- printf("连接成功~");
- str_echo(stdin, sConnectfd);
- closesocket(sConnectfd);
- return 0;
- }
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 echo:emmm”,同时客户端回射消息“296 echo:emmm”,服务器与其他用户通信正常;
f.此时,再接入一个用户,为该用户分配端口号288,发送“嘻嘻”,按“回车”键,服务器显示“288 echo:嘻嘻”,同时客户端回射消息“288 echo:嘻嘻”,运行正确。
5.实验中遇到问题及解决
- 遇到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;
- gets_s()函数不接收一个参数,查阅文档后发现第一个参数为数据,第二个为最大长度,设置为999解决了问题;
- 设计并发时函数传参错误,误把常量当成指针;
- 编程时要注意常量的初始化,否则会导致一些函数传参时的错误;
- 不会用的函数要敢于直接查官方文档;
备注:仅供参考
实验1 基于流式套接字的网络程序设计(计算机网络Ⅱ)
原文:https://www.cnblogs.com/pandazheng/p/13966072.html