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

实验要求:

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    //默认服务器端口号为27015
int _tmain(int argc, _TCHAR* argv[])
{
/udp
	WORD 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/O
			for (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/O
			for (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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值