RakNet Client和公网Server通信 例子 -- 修改远程电脑时间

前言:

很多游戏引擎都是不带网络操作的,比如Cocos2d-x ,那我们只好自己去写或者使用现有的,技术有限的我们只能用现有的了。

RakNet是一个开源的C语言网络操作库,被广泛应用于各种网络程序中,当然我这里只关注手机网络游戏。。。。

这算是RakNet的入门一个例子。


本文适合对RakNet有一定了解的朋友


Client 可以直接指定Server 端的IP地址进行连接,从而发送消息、数据到服务端。但是服务端接收到客户端的消息后怎么回复客户端?


其实Server端和Client端发送消息数据都是同样的操作,只是获取消息目的地址的方式不一样。


Server端通过

pPacket->systemAddress

获取到Client 的地址!


下面是完整的例子。


(1) Client 端


MyClient.h

#pragma once
#include <iostream>
#include "RakPeerInterface.h"   // RakNet Peer
#include "RakNetTypes.h"      
#include "MessageIdentifiers.h" // RakNet自定义消息枚举定义处
#include "BitStream.h"         // RakNet消息包的Bit数据流

#define MAX_CLIENTS 10
#define SERVER_PORT 60000

enum ChatMessagesDefine
{// 自定义消息枚举值,消息ID
	MSG_CHATWORD = ID_USER_PACKET_ENUM + 1,        // 消息ID从RakNet定义的最后一个枚举开始
};

main.cpp

#include "MyClient.h"
#include <string>
#include <sstream>
#include <iostream>
#include "RakSleep.h"

using namespace std;

int main(void)
{

	/*设置变量控制是否服务器已经返回操作结果*/
	bool serverHaveSendBack=true;

	RakNet::RakPeerInterface *pPeer = RakNet::RakPeerInterface::GetInstance();

	RakNet::SocketDescriptor sd;
	pPeer->Startup(1,&sd, 1);

	std::string ipAddrStr;
	cout<<"输入ip:";
	cin>>ipAddrStr;

	//作为客户端连接,连接到主机ip地址和端口号
	pPeer->Connect(ipAddrStr.c_str(), SERVER_PORT, 0, 0);

	string timeStrArray[6]={"10:00","11:59:58","19:59:58","20:04:58","20:29:58","23:59:55"};


	float k = 0;
	int i=0;
	while(1)
	{

		cout<<"改变时间:[0] 10:00  -  [1] 11:59:58  -  [2] 19:59:58  - [3] 20:04:58   [4]  20:29:58  [5] 23:59:55"<<endl;

		int chooseTime;

		cin>>chooseTime;

		std::string time=timeStrArray[chooseTime];

		cout<<"将改变时间为:"<<time<<"  !!"<<endl;

		string timeStr=time;
		string changeTimeCmd=string("time")+timeStr;

		//给主机发送数据
		RakNet::BitStream bsOut;
		bsOut.Write((RakNet::MessageID)MSG_CHATWORD);
		std::ostringstream format_messege;
		format_messege <<timeStr;    //输入信息到字符串流
		bsOut.Write(format_messege.str().c_str());
		pPeer->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,RakNet::UNASSIGNED_SYSTEM_ADDRESS,true);
		Sleep(500);

		serverHaveSendBack=false;

		/*等待服务器返回数据*/
		RakNet::Packet* pPacket;
		while(!serverHaveSendBack)
		{
			RakSleep(30);
			for (pPacket=pPeer->Receive();pPacket;pPeer->DeallocatePacket(pPacket),pPacket=pPeer->Receive())
			{
				switch ( pPacket->data[0] )
				{
				case MSG_CHATWORD:
					{
						RakNet::RakString rs;
						RakNet::BitStream bsIn(pPacket->data,pPacket->length,false);
						bsIn.IgnoreBytes(sizeof(RakNet::MessageID));
						bsIn.Read(rs); //提取我自定的字符
						istringstream input_str(rs.C_String());
						string serverBackStr;
						input_str>>serverBackStr;
						cout<<"Server返回消息: "<<serverBackStr<<endl<<endl;
						serverHaveSendBack=true;
					}
					break;
				default:
					break;
				}
			}

		}

	}

	//断开连接,这个会给对方发送一个消息,告诉它要断开连接了。
	pPeer->Shutdown(300);

	RakNet::RakPeerInterface::DestroyInstance(pPeer);

	return 0;
}

(2) Server 端


MyChatServer.h 

#pragma once

#include <iostream>
#include "RakPeerInterface.h"   // RakNet Peer
#include "RakNetTypes.h"       
#include "MessageIdentifiers.h" // RakNet自定义消息枚举定义处
#include "BitStream.h"          // RakNet消息包的Bit数据流


#define MAX_CLIENTS 10
#define SERVER_PORT 60000

enum ChatMessagesDefine
{// 自定义消息枚举值,消息ID
	MSG_CHATWORD = ID_USER_PACKET_ENUM + 1,        // 消息ID从RakNet定义的最后一个枚举开始
};


main.cpp

#include "MyChatServer.h"
#include <string>
#include <iostream>
#include <sstream>
#include "RakNetTypes.h"  

using namespace std;

int main()
{
	//给服务器端创建一个实例
	RakNet::RakPeerInterface* pPeer = RakNet::RakPeerInterface::GetInstance();
	if ( NULL == pPeer )
	{
		std::cout << "RakNet::RakPeerInterface::GetInstance() Error!" << std::endl;
		return -1;
	}
	else
	{
		std::cout << "———MyChatServer Init Success(C)———–" << std::endl;
	}

	RakNet::Packet* pPacket;
	std::cout << "Start Server ……" << std::endl;
	//启动服务器
	pPeer->Startup( MAX_CLIENTS, &RakNet::SocketDescriptor( SERVER_PORT, 0 ), 1 );

	//设置最大链接数
	pPeer->SetMaximumIncomingConnections( MAX_CLIENTS );

	//
	while (1)
	{
		for ( pPacket = pPeer->Receive(); pPacket; pPeer->DeallocatePacket( pPacket ), pPacket = pPeer->Receive() )
		{
			switch ( pPacket->data[0] )
			{
			case ID_REMOTE_DISCONNECTION_NOTIFICATION:
				std::cout << "Another client has disconnected" << std::endl;
				break;
			case ID_REMOTE_CONNECTION_LOST:
				std::cout << "Another client has lost the connection"<< std::endl;
				break;
			case ID_REMOTE_NEW_INCOMING_CONNECTION:
				std::cout << "Another client has connected" << std::endl;
				break;
			case ID_CONNECTION_REQUEST_ACCEPTED:
				{
					std::cout << "Our connection request has been accepted" << std::endl;

					RakNet::BitStream bsOut;
					bsOut.Write( ( RakNet::MessageID )MSG_CHATWORD );
					bsOut.Write("Hello world");
					pPeer->Send( &bsOut, HIGH_PRIORITY, RELIABLE_ORDERED, 0, pPacket->systemAddress, false );
				}
				break;
			case ID_NEW_INCOMING_CONNECTION:
				{
					std::cout << "A connection is incoming" << std::endl;
					std::string clientIpAddr=pPacket->systemAddress.ToString(true);
					std::string GUID=pPacket->guid.ToString();
					UINT clientPort=pPacket->systemAddress.GetPort();
					cout<<"clientIpAddr= "<<clientIpAddr<<"  GUID= "<<GUID<<endl;
				}
				break;
			case ID_NO_FREE_INCOMING_CONNECTIONS:
				std::cout << "The server is full"<< std::endl;
				break;
			case ID_DISCONNECTION_NOTIFICATION:
				std::cout << "A client has disconnected"<< std::endl;
				break;
			case ID_CONNECTION_LOST:
				std::cout << "A client lost the connection" << std::endl;
				break;

				//用户自定义数据包
			case MSG_CHATWORD:
				{
					RakNet::RakString rs;
					RakNet::BitStream bsIn( pPacket->data, pPacket->length, false );
					bsIn.IgnoreBytes(sizeof(RakNet::MessageID));
					bsIn.Read(rs);    //提取字符串
					std::istringstream input_str(rs.C_String()); //存入字符串流
					std::string timeStr;
					input_str >>timeStr; //提取想要的变量

					std::string changeTimeCmd=std::string("time ")+timeStr;

					int a= system(changeTimeCmd.c_str());

					std::cout << "时间改变为:"<<changeTimeCmd<< std::endl; //打印结果

					//给主机发送数据
					int connectState= pPeer->GetConnectionState(pPacket->guid);
					RakNet::BitStream bsOut;
					bsOut.Write((RakNet::MessageID)MSG_CHATWORD);
					bsOut.Write("时间改变成功!");
					pPeer->Send(&bsOut,HIGH_PRIORITY,RELIABLE_ORDERED,0,pPacket->systemAddress,false);
				}
				break;

			default:
				std::cout << "Message with identifier %i has arrived" << pPacket->data[0] << std::endl;
				break;
			}
		}
	}

	//
	RakNet::RakPeerInterface::DestroyInstance( pPeer );
	return 0;
}

最后是程序运行的测试结果:



转载请注明 http://blog.csdn.net/huutu   谢谢!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Gophertunnel 是一个基于 Go 语言编写的 Minecraft 代理库,用于在 Minecraft 客户端和服务器之间进行中间人攻击。它允许你拦截、修改和重定向 Minecraft 客户端和服务器之间的数据包,以实现各种有趣的效果。 以下是一个示例,演示如何使用 Gophertunnel 代理 Minecraft 客户端连接到服务器: ```go package main import ( "fmt" "github.com/sandertv/gophertunnel/minecraft/login" "github.com/sandertv/gophertunnel/minecraft/protocol" "github.com/sandertv/gophertunnel/minecraft/protocol/packet" "github.com/sandertv/gophertunnel/minecraft/text" "github.com/sandertv/gophertunnel/minecraft/world" "github.com/sandertv/gophertunnel/net" ) func main() { // 创建一个 net.Listener,监听来自 Minecraft 客户端的连接请求。 listener, err := net.Listen("raknet", "localhost:19132") if err != nil { panic(err) } defer listener.Close() // 监听连接请求,打印客户端的 IP 地址。 fmt.Println("Listening on", listener.Addr()) for { conn, err := listener.Accept() if err != nil { panic(err) } fmt.Println("Accepted connection from", conn.RemoteAddr()) // 启动一个 Minecraft 登录会话,以验证客户端的身份。 clientData := &login.ClientData{} serverData := &login.ServerData{} serverData.CurrentProtocol = protocol.MinecraftBedrock_1_16_201 serverData.GameVersion = "1.16.201" session := login.NewSession(conn, clientData, serverData) session.Start() // 如果验证成功,接受客户端和服务器之间的数据包。 go func() { for { pk, err := session.Conn.ReadPacket() if err != nil { panic(err) } handlePacket(pk) } }() // 发送一个 MOTD 描述信息到客户端。 pk := &packet.Text{ Type: text.TypeRaw, Text: "Welcome to my Minecraft server!", } if err := session.Conn.WritePacket(pk); err != nil { panic(err) } // 加载一个新的 Minecraft 世界,并将客户端连接到该世界。 world, err := world.NewWorld() if err != nil { panic(err) } if err := session.Conn.WritePacket(&packet.StartGame{ EntityUniqueID: 1, PlayerGameMode: protocol.GameModeSurvival, Dimension: world.Dimension, Difficulty: world.Difficulty, SpawnVector: world.Spawn, }); err != nil { panic(err) } } } func handlePacket(pk packet.Packet) { // 处理客户端和服务器之间传输的数据包。 switch pk := pk.(type) { case *packet.MovePlayer: fmt.Println("Player moved:", pk.Position) case *packet.Text: fmt.Println("Received text message:", pk.Text) } } ``` 在此示例中,我们创建了一个 Minecraft 服务器代理,监听来自 Minecraft 客户端的连接请求,并在客户端连接时创建了一个新的 Minecraft 世界。我们还向客户端发送了一个简单的欢迎信息,并处理了客户端和服务器之间传输的数据包,以进行一些基本的操作(例如,打印玩家移动事件和接收文本消息)。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值