【网络编程】利用I/O复用模型实现一个时间同步服务器

来源:互联网 发布:js控制下拉列表反显 编辑:程序博客网 时间:2024/06/03 06:42

实验要求:

1.    服务端采用I/O复用模型(select函数)接收客户端的时间同步请求;

2.    服务端采用单线程,但要能同时接收多客户端的连接请求,显示客户端IP和端口,并向其回送时间信息。

3.    客户端尝试同时使用UDP和TCP来实现。

注:借助I/O复用模型,用单线程达到多线程的效果


server:

// EchoTCPServer-select.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#undef UNICODE #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <winsock2.h> #include <ws2tcpip.h> #include <stdlib.h> #include <stdio.h> #include<cstdio>#include<cstring>#include<iostream>#include<ctime>#include<time.h>using namespace std;// 连接到winsock2对应的lib文件: Ws2_32.lib #pragma comment (lib, "Ws2_32.lib") #define DEFAULT_BUFLEN 512    //默认缓冲区长度为512#define DEFAULT_PORT 27015    //默认服务器端口号为27015int _tmain(int argc, _TCHAR* argv[]){/////////////////////////////////////////udpWORD sockVersion = MAKEWORD(2, 2);WSADATA wsaDataudp;if (WSAStartup(sockVersion, &wsaDataudp) != 0){return 0;}SOCKET serSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);if (serSocket == INVALID_SOCKET){printf("socket error !");WSACleanup();return 0;}sockaddr_in serAddr;serAddr.sin_family = AF_INET;serAddr.sin_port = htons(DEFAULT_PORT);serAddr.sin_addr.S_un.S_addr = INADDR_ANY;if (bind(serSocket, (sockaddr *)&serAddr, sizeof(serAddr)) == SOCKET_ERROR){printf("bind error !");closesocket(serSocket);WSACleanup();return 0;}cout << "listening the client" << endl;sockaddr_in remoteAddr;int nAddrLen = sizeof(remoteAddr);int cnt = 0;char RecvBuffer[64];/////////////////////////////////////////tcp    WSADATA wsaDatatcp;     int iResult; SOCKET ServerSocket = INVALID_SOCKET; SOCKET AcceptSocket = INVALID_SOCKET;     char recvbuf[DEFAULT_BUFLEN];     int recvbuflen = DEFAULT_BUFLEN; sockaddr_in addrClient;int addrClientlen = sizeof(sockaddr_in);    // 初始化 Winsock     iResult = WSAStartup(MAKEWORD(2,2), &wsaDatatcp);     if (iResult != 0)     { printf("WSAStartup failed with error: %d\n", iResult);     return 1;     } // 创建用于监听的套接字       ServerSocket = socket(AF_INET,SOCK_STREAM, IPPROTO_IP);           if(ServerSocket == INVALID_SOCKET)     {         printf("socket failed with error: %ld\n", WSAGetLastError());         WSACleanup();        return 1;     }     // 为套接字绑定地址和端口号SOCKADDR_IN addrServ;       addrServ.sin_family = AF_INET;       addrServ.sin_port = htons(DEFAULT_PORT);// 监听端口为DEFAULT_PORT    addrServ.sin_addr.S_un.S_addr = htonl(INADDR_ANY);        iResult = bind(ServerSocket,(const struct sockaddr*)&addrServ,sizeof(SOCKADDR_IN));     if (iResult == SOCKET_ERROR)     {        printf("bind failed with error: %d\n", WSAGetLastError());         closesocket(ServerSocket);         WSACleanup();         return 1;     }     // 监听套接字       iResult = listen(ServerSocket, SOMAXCONN);       if(iResult == SOCKET_ERROR)       {           printf("listen failed !\n");           closesocket(ServerSocket);           WSACleanup();           return -1;       }     printf("TCP server starting\n");printf("UDP server starting\n");fd_set fdRead,fdSocket;fd_set fdReadudp, fdSocketudp;FD_ZERO(&fdSocketudp);FD_SET(serSocket, &fdSocketudp);    FD_ZERO( &fdSocket );FD_SET( ServerSocket, &fdSocket);while( TRUE){//////udp/////fdReadudp = fdSocketudp;iResult = select(0, &fdReadudp, NULL, NULL, NULL);if (iResult > 0){//有网络事件发生//确定有哪些套接字有未决的I/O,并进一步处理这些I/Ofor (int i = 0; i < (int)fdSocketudp.fd_count; i++){if (FD_ISSET(fdSocketudp.fd_array[i], &fdReadudp)){if (fdSocketudp.fd_array[i] == serSocket){if (fdSocketudp.fd_count < FD_SETSIZE){memset(RecvBuffer, 0, sizeof(RecvBuffer));int Ret = recvfrom(serSocket, RecvBuffer, 64, 0, (sockaddr *)&remoteAddr, &nAddrLen);if (Ret < 0){cout << "receive error " << WSAGetLastError();continue;}RecvBuffer[Ret] = 0x00;cout << "接收到新的连接:ip: %s " << ++cnt << ": " << inet_ntoa(remoteAddr.sin_addr);cout << "   post: " << remoteAddr.sin_port << endl;time_t t = time(0);char tmp[64];strftime(tmp, sizeof(tmp), "%Y/%m/%d %X", localtime(&t));cout << "send time :  ";puts(tmp);//将本地时间发送给客户端 sendto(serSocket, tmp, sizeof(tmp), 0, (sockaddr *)&remoteAddr, nAddrLen);}else{printf("连接个数超限!\n");continue;}}}}}//tcp//通过select等待数据到达事件,如果有事件发生,select函数移除fdRead集合中没有未决I/O操作的套接字句柄,然后返回    fdRead = fdSocket;iResult = select( 0, &fdRead, NULL, NULL, NULL);if (iResult >0){//有网络事件发生//确定有哪些套接字有未决的I/O,并进一步处理这些I/Ofor (int i=0; i<(int)fdSocket.fd_count; i++){if (FD_ISSET( fdSocket.fd_array[i] ,&fdRead)){if( fdSocket.fd_array[i] == ServerSocket){if( fdSocket.fd_count < FD_SETSIZE){//同时复用的套接字数量不能大于FD_SETSIZE//有新的连接请求AcceptSocket = accept(ServerSocket,(sockaddr FAR*)&addrClient,&addrClientlen);   if( AcceptSocket == INVALID_SOCKET)   {   printf("accept failed !\n");   closesocket(ServerSocket);   WSACleanup();   return 1;   }//增加新的连接套接字进行复用等待FD_SET( AcceptSocket, &fdSocket);printf("接收到新的连接:ip: %s    ", inet_ntoa(addrClient.sin_addr));cout << "post:  " << addrClient.sin_port << endl;time_t t = time(0);char tmp[64];strftime(tmp, sizeof(tmp), "%Y/%m/%d %X", localtime(&t));send(AcceptSocket, tmp, strlen(tmp), 0);cout << "send time :";puts(tmp);//将本地时间发送给客户端 }else{printf("连接个数超限!\n"); continue;}}else{//有数据到达memset(recvbuf,0,recvbuflen);iResult = recv( fdSocket.fd_array[i], recvbuf, recvbuflen, 0); if (iResult > 0) {     //情况1:成功接收到数据printf("\nBytes received: %d\n", iResult); } else if (iResult == 0) {//情况2:连接关闭//printf("Current Connection closing...\n"); closesocket(fdSocket.fd_array[i]);FD_CLR(fdSocket.fd_array[i], &fdSocket);}else { //情况3:接收失败printf("recv failed with error: %d\n",WSAGetLastError() ); closesocket(fdSocket.fd_array[i]); FD_CLR(fdSocket.fd_array[i], &fdSocket);} }}}}else{printf("select failed with error: %d\n",WSAGetLastError() ); break; }}    // cleanup     closesocket(ServerSocket);     WSACleanup();     return 0; }


client_tcp:

#include "stdafx.h"  #include <stdio.h>  #include <winsock2.h>  #include <wincrypt.h>#include <time.h>#include <cstring>#include <iostream>  #include <string.h> #pragma comment(lib, "user32.lib")#pragma comment(lib, "shlwapi.lib")#pragma comment(lib, "ws2_32.lib")   #pragma comment(lib, "crypt32.lib")const int MAX1 = 1000;using namespace std;int main(int argc, const char*argv[]){WORD sockVersion = MAKEWORD(2, 2);WSADATA wsaData;int error = WSAStartup(sockVersion, &wsaData);if (error){cout << "fail to startup" << GetLastError() << endl;WSACleanup();return 0;}SOCKET socketClient = socket(AF_INET, SOCK_STREAM, 0);if (socketClient == INVALID_SOCKET){cout << "socket error!  " << GetLastError() << endl;WSACleanup();closesocket(socketClient);return 0;}char ip[20];memset(ip, 0, sizeof(ip));cout << "Please input the ip: ";gets_s(ip);cout << endl;sockaddr_in addrServer;addrServer.sin_addr.S_un.S_addr = inet_addr(ip);addrServer.sin_family = AF_INET;addrServer.sin_port = htons(27015);connect(socketClient, (SOCKADDR*)&addrServer, sizeof(SOCKADDR));//FileSend(socketClient);char revbuf[64]; memset(revbuf, 0, sizeof(revbuf));recv(socketClient,revbuf, 64, 0);puts(revbuf);closesocket(socketClient);getchar();return 0;}


client_udp:

#include "stdafx.h" #include <iostream>#include <stdio.h>  #include <winsock2.h>  #include <windows.h>using namespace std;#pragma comment(lib, "ws2_32.lib")   const int MAX_IP_PATH = 15;const int  MAX_BUFFER = 10240;const int NAME = 10240;int main(int argc, char* argv[]){WORD socketVersion = MAKEWORD(2, 2);WSADATA wsaData;if (WSAStartup(socketVersion, &wsaData) != 0){return 0;}SOCKET ClientSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);if (ClientSocket == INVALID_SOCKET){cout << "Create Socket Failed::" << GetLastError() << endl;WSACleanup();return -1;}char IP_ADDRESS[MAX_IP_PATH];unsigned int PORT = 27015;cout << "connect IP:";cin >> IP_ADDRESS;cout << endl;sockaddr_in sin;sin.sin_family = AF_INET;sin.sin_port = htons(PORT);sin.sin_addr.S_un.S_addr = inet_addr(IP_ADDRESS);unsigned int msglen;char SendBuffer[64]="ask time";int len = sizeof(sin);cout << "asking time"<<endl;msglen = strlen(SendBuffer);sendto(ClientSocket, SendBuffer, strlen(SendBuffer), 0, (sockaddr *)&sin, len);int Ret;char RecvBuffer[64];Ret = recvfrom(ClientSocket, RecvBuffer, sizeof(RecvBuffer) , 0, (sockaddr *)&sin, &len);if (Ret <0){cout << "receive error:" << WSAGetLastError();closesocket(ClientSocket);WSACleanup();return -1;}cout << "time:   " << RecvBuffer << endl;system("pause");WSACleanup();return 0;}

0 0
原创粉丝点击