AI智能
改变未来

Windows网络编程(TCP流式套接字实验)

一、
实验环境

Windows10 vs2017 c/c++

二、实验内容

1.设计思路

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

要求使用流式套接字编程,实现时间同步服务器和客户端,该服务器能够接收客户端的查询请求,获取本地时间,并将结果发送回客户端,客户端将该时间显示出来,如图1所示。

(1) time()函数、ctime()函数为时间处理函数。

(2)客户的recv函数需要循环接收。

1.1服务器(时间服务器)#include \"pch.h\"#include <iostream>#include <time.h>#include \"winsock2.h\"#pragma comment(lib, \"ws2_32.lib\")#define BUF_SZIE 64int main(){WSADATA         wsd;        //WSADATA变量SOCKET          sServer;    //服务器套接字SOCKET          sClient;    //客户端套接字SOCKADDR_IN     addrServ;;      //服务器地址char        buf[BUF_SZIE];  //接收数据缓冲区char            buf1[BUF_SZIE];  //发送int             retVal;         //返回值//初始化套结字动态库if (WSAStartup(MAKEWORD(2, 2), &wsd) !=0){printf(\"WSAStartupfailed!\\n\");return -1;}//创建套接字sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (INVALID_SOCKET == sServer){printf(\"socketfailed!\\n\");WSACleanup();//释放套接字资源;return  -1;}//服务器套接字地址addrServ.sin_family = AF_INET;addrServ.sin_port = htons(4999);addrServ.sin_addr.s_addr = INADDR_ANY;//绑定套接字retVal=bind(sServer(LPSOCKADDR)&addrServ,sizeof(SOCKADDR_IN));if (SOCKET_ERROR == retVal){printf(\"bindfailed!\\n\");closesocket(sServer);    //关闭套接字WSACleanup();           //释放套接字资源;return -1;}//开始监听retVal = listen(sServer, 3);if (SOCKET_ERROR == retVal){printf(\"listenfailed!\\n\");closesocket(sServer);    //关闭套接字WSACleanup();           //释放套接字资源;return -1;}//接受客户端请求sockaddr_in addrClient;int addrClientlen = sizeof(addrClient);sClient=accept(sServer(sockaddr*)&addrClient,&addrClientlen);if (INVALID_SOCKET == sClient){printf(\"acceptfailed!\\n\");closesocket(sServer);    //关闭套接字WSACleanup();           //释放套接字资源;return -1;}while (SOCKET_ERROR != retVal) {//接收客户端数据ZeroMemory(buf, BUF_SZIE);retVal = recv(sClient, buf, BUF_SZIE, 0);if (SOCKET_ERROR == retVal){printf(\"recvfailed!\\n\");closesocket(sClient);    //关闭套接字closesocket(sServer);    //关闭套接字WSACleanup();           //释放套接字资源;return -1;}printf(\"%s\\n\", buf); //输出\"MyTCP\"//退出time_t t1;t1 = time(&t1);//      printf(\"%s\\n\", ctime(&t1));ZeroMemory(buf1, BUF_SZIE);·         strcpy_s(buf1, ctime(&t1));retVal = send(sClient, buf1,strlen(buf1), 0);if (SOCKET_ERROR == retVal){printf(\"sendfailed!\\n\");closesocket(sClient);    //关闭套接字WSACleanup();       //释放套接字资源return -1;}}closesocket(sClient);    //关闭套接字//}closesocket(sServer);    //关闭套接字WSACleanup();           //释放套接字资源;return 0;}
1.2客户端(时间请求)#include \"pch.h\"#include <iostream>#include <time.h>#define  BUF_SZIE 64#include \"winsock2.h\"#pragma comment(lib, \"ws2_32.lib\")int main(int argc, char* argv[]){WSADATA          wsd;         //WSADATA变量SOCKET           sHost;           //服务器套接字SOCKADDR_IN      servAddr;        //服务器地址char         buf[BUF_SZIE];   //发送数据缓冲区char         buf1[BUF_SZIE];  //接收数据缓冲区int              retVal;          //返回值//初始化套结字动态库if (WSAStartup(MAKEWORD(2, 2), &wsd) !=0){printf(\"WSAStartupfailed!\\n\");return -1;}//创建套接字sHost= socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (INVALID_SOCKET == sHost){printf(\"socketfailed!\\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)4999);int nServAddlen = sizeof(servAddr);//连接服务器retVal=connect(sHost(LPSOCKADDR)&servAddr,sizeof(servAddr));if (SOCKET_ERROR == retVal){printf(\"connectfailed!\\n\");closesocket(sHost);   //关闭套接字WSACleanup();    //释放套接字资源return -1;}while (SOCKET_ERROR != retVal) {//向服务器发送数据ZeroMemory(buf, BUF_SZIE);strcpy_s(buf,\"timeplease!\");retVal= send(sHost, buf, strlen(buf), 0);if (SOCKET_ERROR == retVal){printf(\"sendfailed!\\n\");closesocket(sHost);   //关闭套接字WSACleanup();    //释放套接字资源return -1;}ZeroMemory(buf1, BUF_SZIE);retVal= recv(sHost, buf1, BUF_SZIE, 0);if (SOCKET_ERROR == retVal){printf(\"recvfailed!\\n\");closesocket(sHost);   //关闭套接字WSACleanup();         //释放套接字资源;return -1;}printf(\"%s\\n\", buf1); //输出Sleep(1000);}closesocket(sHost);   //关闭套接字WSACleanup();    //释放套接字资源return 0;}

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

编写一服务器程序和客户程序,要求客户每输入一行数据,服务器接收后加上echo:回送给客户程序,当客户输入“q”后退出。

(1) 为了提高流式套接字网络程序对流数据的接收能力,以循环服务器方式分别实现以下要求:

a. 如图2所示,客户端接收数据以recvline接收一行数据。

b. 如图3所示,服务器和客户端接收数据以recvn定长方式接收,其中长度由运行程序时指定。

c. 如图4所示,服务器和客户端接收数据均以recvvl变长方式接收,即将发送的消息设计为消息头和消息体,消息头包含消息体的长度。

服务器:#include \"pch.h\"#include <iostream>#include <time.h>#include \"winsock2.h\"#pragma comment(lib, \"ws2_32.lib\")#define  BUF_SZIE 64using namespace std;int main(){WSADATA          wsd;         //WSADATA变量SOCKET           sServer;     //服务器套接字SOCKET           sClient;     //客户端套接字SOCKADDR_IN      addrServ;;       //服务器地址char         buf[BUF_SZIE];   //接收数据缓冲区char            buf1[BUF_SZIE];  //发送int              retVal;          //返回值//初始化套结字动态库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(\"socketfailed!\\n\");WSACleanup();//释放套接字资源;return  -1;}//服务器套接字地址addrServ.sin_family= AF_INET;addrServ.sin_port= htons(4999);addrServ.sin_addr.s_addr = INADDR_ANY;//绑定套接字retVal=bind(sServer(LPSOCKADDR)&addrServ,sizeof(SOCKADDR_IN));if (SOCKET_ERROR == retVal){printf(\"bindfailed!\\n\");closesocket(sServer); //关闭套接字WSACleanup();         //释放套接字资源;return -1;}//开始监听retVal= listen(sServer, 3);if (SOCKET_ERROR == retVal){printf(\"listenfailed!\\n\");closesocket(sServer); //关闭套接字WSACleanup();         //释放套接字资源;return -1;}//接受客户端请求sockaddr_in addrClient;int addrClientlen = sizeof(addrClient);sClient=accept(sServer(sockaddr*)&addrClient,&addrClientlen);if (INVALID_SOCKET == sClient){printf(\"acceptfailed!\\n\");closesocket(sServer); //关闭套接字WSACleanup();         //释放套接字资源;return -1;}//显示客户端的IP和端口char *pClientIP = inet_ntoa(addrClient.sin_addr);u_short  clientPort = ntohs(addrClient.sin_port);cout<< \"Accept aclient.\" << endl;cout<< \"IP: \" << pClientIP << endl;cout<< \"Port: \" << clientPort << endl;//接收客户端数据并回射while (SOCKET_ERROR != retVal) {ZeroMemory(buf, BUF_SZIE);retVal= recv(sClient, buf, BUF_SZIE, 0);if (SOCKET_ERROR == retVal){printf(\"recvfailed!\\n\");closesocket(sClient); //关闭套接字closesocket(sServer); //关闭套接字WSACleanup();         //释放套接字资源;return -1;}printf(\"echo:%s\\n\", buf);ZeroMemory(buf1, BUF_SZIE);strcpy_s(buf1,\"echo:\");strncat(buf1, buf, BUF_SZIE);retVal= send(sClient, buf1, strlen(buf1), 0);if (SOCKET_ERROR == retVal){printf(\"sendfailed!\\n\");closesocket(sClient); //关闭套接字WSACleanup();    //释放套接字资源return -1;}}closesocket(sClient); //关闭套接字closesocket(sServer); //关闭套接字WSACleanup();         //释放套接字资源;return 0;
客户端:#include \"pch.h\"#include <iostream>#include <time.h>#include <stdio.h>#define  BUF_SZIE 64#include \"winsock2.h\"#pragma comment(lib, \"ws2_32.lib\")using namespace std;BOOL RecvLine(SOCKET s, char* buf); //读取一行数据int recvn(SOCKET s, char * recvbuf, unsigned int fixedlen);int recvvl(SOCKET s, char * recvbuf, unsigned int recvbuflen);int main(int argc, char* argv[]){WSADATA          wsd;         //WSADATA变量SOCKET           sHost;           //服务器套接字SOCKADDR_IN      servAddr;        //服务器地址char         buf[BUF_SZIE];   //发送数据缓冲区char         buf1[BUF_SZIE];  //接收数据缓冲区int              retVal;          //返回值//初始化套结字动态库if (WSAStartup(MAKEWORD(2, 2), &wsd) !=0){printf(\"WSAStartupfailed!\\n\");return -1;}//创建套接字sHost= socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (INVALID_SOCKET == sHost){printf(\"socketfailed!\\n\");WSACleanup();//释放套接字资源return  -1;}//准备连接服务器cout<< \"Clientsucceeded!\" << endl;cout<< \"Be ready toconnect to server...\" << endl;//设置服务器地址servAddr.sin_family= AF_INET;servAddr.sin_addr.s_addr = inet_addr(\"127.0.0.1\");servAddr.sin_port= htons((short)4999);int nServAddlen = sizeof(servAddr);//连接服务器retVal=connect(sHost(LPSOCKADDR)&servAddr,sizeof(servAddr));if (SOCKET_ERROR == retVal){printf(\"connectfailed!\\n\");closesocket(sHost);   //关闭套接字WSACleanup();    //释放套接字资源return -1;}//连接服务器成功cout<< \"Connectsuccessfully!\" << endl;while (SOCKET_ERROR != retVal) {//向服务器发送数据ZeroMemory(buf, BUF_SZIE);gets_s(buf);unsigned int len = strlen(buf);//获取读入字符串长度。buf[len]= \'\\n\';//结尾增加换行符。buf[len+ 1] = \'\\0\';//赋值新的结束符if (buf[0] == \'q\' || buf[0] == \'Q\') break;retVal= send(sHost, buf, strlen(buf), 0);if (SOCKET_ERROR == retVal){printf(\"send failed!\\n\");closesocket(sHost);   //关闭套接字WSACleanup();    //释放套接字资源return -1;}//行接收if (!RecvLine(sHost,buf1)){printf(\"recvfailed!\\n\");closesocket(sHost);   //关闭套接字WSACleanup();         //释放套接字资源;return -1;}printf(\"%s\\n\", buf1);}closesocket(sHost);   //关闭套接字WSACleanup();    //释放套接字资源return 0;}BOOL RecvLine(SOCKET s, char* buf){BOOL retVal = TRUE;            //返回值BOOL bLineEnd = FALSE;     //行结束int      nReadLen = 0;         //读入字节数int      nDataLen = 0;         //数据长度ZeroMemory(buf, BUF_SZIE);while (!bLineEnd) //与客户端连接 没有换行{nReadLen= recv(s, buf + nDataLen, 1, 0);//每次接收一个字节//错误处理if (SOCKET_ERROR == nReadLen){retVal= FALSE;  //读数据失败break;           //跳出循环}if (0 == nReadLen)//客户端关闭{retVal= FALSE;  //读数据失败break;           //跳出循环}//读入数据if (\'\\n\' == *(buf + nDataLen)) //换行符{bLineEnd= TRUE;          //接收数据结束}else {nDataLen+= nReadLen;     //增加数据长度}}return retVal;}

服务器:(主要代码)//接收客户端数据并回射while (SOCKET_ERROR != retVal) {ZeroMemory(buf, BUF_SZIE);int iResult =recvn(sClient, buf, 2);if (iResult != 2){//如果变长消息在接收时没有返回足够的数据就返回0(连接关闭)或-1(发生错误)if (iResult == -1){printf(\"接收发生错误: %d\\n\",WSAGetLastError());return -1;}else{printf(\"连接关闭\\n\");return 0;}}printf(\"ehco:%s\\n\", buf);ZeroMemory(buf1, BUF_SZIE);strcpy_s(buf1,\"echo:\");strncat(buf1,buf, BUF_SZIE);retVal= send(sClient, buf1, strlen(buf1), 0);if (SOCKET_ERROR == retVal){printf(\"sendfailed!\\n\");closesocket(sClient); //关闭套接字WSACleanup();    //释放套接字资源return -1;}}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;}//接收缓存指针向后移动recvbuf += iResult;//更新cnt值cnt-= iResult;}return fixedlen;}

服务器://接收客户端数据并回射while (SOCKET_ERROR != retVal) {ZeroMemory(buf, BUF_SZIE);retVal= recvvl(sClient, buf, BUF_SZIE);if (SOCKET_ERROR == retVal){printf(\"recv failed!\\n\");closesocket(sClient); //关闭套接字closesocket(sServer); //关闭套接字WSACleanup();         //释放套接字资源;return -1;}printf(\"ehco:%s\\n\", buf);ZeroMemory(buf1, BUF_SZIE);strcpy_s(buf1,\"echo:\");strncat(buf1,buf, BUF_SZIE);unsigned int len = (unsigned int)strlen(buf1);len= htonl(len);retVal= send(sClient, (char*)&len, sizeof(unsigned int), 0);if (SOCKET_ERROR == retVal){printf(\"send failed!\\n\");closesocket(sClient); //关闭套接字WSACleanup();    //释放套接字资源return -1;}retVal= send(sClient, buf1, strlen(buf1), 0);if (SOCKET_ERROR == retVal){printf(\"sendfailed!\\n\");closesocket(sClient); //关闭套接字WSACleanup();    //释放套接字资源return -1;}}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)){//如果长度字段在接收时没有返回一个整型数据就返回0(连接关闭)或-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){//如果变长消息在接收时没有返回足够的数据就返回0(连接关闭)或-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){//如果消息在接收时没有返回足够的数据就返回0(连接关闭)或-1(发生错误)if (iResult == -1){printf(\"接收发生错误: %d\\n\", WSAGetLastError());return -1;}else{printf(\"连接关闭\\n\");return 0;}}return iResult;}

并发服务器

并发服务器:#include \"pch.h\"#include <iostream>#include <time.h>#include \"winsock2.h\"#pragma comment(lib, \"ws2_32.lib\")#define  BUF_SZIE 64using namespace std;typedef struct my_file{SOCKET clientSocket; //文件内部包含了一个SOCKET 用于和客户端进行通信sockaddr_in clientAddr; //用于保存客户端的socket地址int id; //文件块的序号}F;DWORD WINAPI mywork(const LPVOID arg){char         buf[BUF_SZIE];   //接收数据缓冲区char            buf1[BUF_SZIE];  //发送int              retVal=0;             //返回值F *temp = (F*)arg;char *pClientIP =inet_ntoa(temp->clientAddr.sin_addr);u_short  clientPort = ntohs(temp->clientAddr.sin_port);cout<< \"Accept a client.\" << endl;cout<< \"IP: \" << pClientIP << endl;cout<< \"Port: \" << clientPort << endl;//接收客户端数据并回射while (SOCKET_ERROR != retVal) {ZeroMemory(buf, BUF_SZIE);retVal= recv(temp->clientSocket, buf, BUF_SZIE, 0);if (SOCKET_ERROR == retVal){printf(\"recvfailed!\\n\");closesocket(temp->clientSocket);   //关闭套接字return -1;}printf(\"%s\\n\", buf);ZeroMemory(buf1, BUF_SZIE);strcpy_s(buf1,\"echo:\");strncat(buf1,buf, BUF_SZIE);retVal= send(temp->clientSocket, buf1, strlen(buf1), 0);if (SOCKET_ERROR == retVal){printf(\"send failed!\\n\");closesocket(temp->clientSocket);   //关闭套接字return -1;}}closesocket(temp->clientSocket);   //关闭套接字}int main(){WSADATA          wsd;         //WSADATA变量SOCKET           sServer;     //服务器套接字SOCKET           sClient;     //客户端套接字SOCKADDR_IN      addrServ;;       //服务器地址char         buf[BUF_SZIE];   //接收数据缓冲区char            buf1[BUF_SZIE];  //发送int              retVal;          //返回值//初始化套结字动态库if (WSAStartup(MAKEWORD(2, 2), &wsd) !=0){printf(\"WSAStartupfailed!\\n\");return -1;}//创建套接字sServer= socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);if (INVALID_SOCKET == sServer){printf(\"socketfailed!\\n\");WSACleanup();//释放套接字资源;return  -1;}//服务器套接字地址addrServ.sin_family= AF_INET;addrServ.sin_port= htons(4999);addrServ.sin_addr.s_addr = INADDR_ANY;//绑定套接字retVal= bind(sServer, (LPSOCKADDR)&addrServ, sizeof(SOCKADDR_IN));if (SOCKET_ERROR == retVal){printf(\"bindfailed!\\n\");closesocket(sServer); //关闭套接字WSACleanup();         //释放套接字资源;return -1;}HANDLE hThread[20]; //获取句柄//开始监听retVal= listen(sServer, 20);if (SOCKET_ERROR == retVal){printf(\"listenfailed!\\n\");closesocket(sServer); //关闭套接字WSACleanup();         //释放套接字资源;return -1;}for (int i = 0; i < 20;i++) {F *temp = new F; //创建新的传输结构体sockaddr_in clntAddr;int nSize = sizeof(SOCKADDR);SOCKET clientSock=accept(sServer(SOCKADDR*)&clntAddr,&nSize);     //temp数据成员赋值temp->clientSocket= clientSock;temp->id= i + 1;temp->clientAddr= clntAddr;       //通过句柄创建子线程hThread[i]= CreateThread(NULL, 0, &mywork, temp, 0, NULL);}WaitForMultipleObjects(20,hThread, TRUE, INFINITE);cout<< WSAGetLastError() << endl; //查看错误信息closesocket(sServer); //关闭套接字WSACleanup();         //释放套接字资源;cout<< \"serverclose!\" << endl;return 0;}
赞(0) 打赏
未经允许不得转载:爱站程序员基地 » Windows网络编程(TCP流式套接字实验)