启动PhotonServer
1.下载PhotonServer:
下载地址:PhotonServer下载
2.双击解压到D盘:
3.在官网注册帐号下载免费license文件,将最大连接数由20扩展为100:
并点击PhotonControl.exe文件启动PhotonServer
4.点击Start as applicatiobn启动一个应用,点击Open Logs开启日志
5.修改PhotonServer.config文件
文件:D:\Photon-OnPremise-Server-SDK_v4-0-29-11263\deploy\bin_Win64\ PhotonServer.config
创建第一个服务器端项目,并设置部署的目录
1.使用VisualStudio2013新建一个C#的类库项目:
2.创建项目部署文件夹
在PhotonServer的deploy文件夹中创建项目文件夹(MyGameServer可变)
在MyGameServer中创建bin文件夹(固定)
3.将项目生成动态链接库(.dll)路径与项目部署路径匹配
右键项目点击属性进入项目属性设置界面
将生成项中的输出路径改为项目部署文件夹中bin的路径
4.生成项目动态链接库文件至项目部署目录
右键项目点击生成即可
开发ApplicationBase和ClientPeer
1.添加相关引用
操作:
右键项目添加引用
浏览PhotontServer目录中的lib文件夹中,添加ExitGameLibs.dll、Photon.SocketServer.dll、PhotonHostRuntimeInterfaces.dll三个引用
2.创建MyGameServer和MyClientPeer
MyGameServer:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Photon.SocketServer;
namespace MyGameServer
{
//每个Server端主类都要继承ApplicationBase类
public class MyGameServer:ApplicationBase
{
//当每个客户端连接进来的时候调用此方法
//返回一个PeerBase对象表示一个客户端和服务器端的连接
protected override PeerBase CreatePeer(InitRequest initRequest)
{
return new MyClientPeer();
}
//初始化时候调用
protected override void Setup()
{
}
//Server关闭的时候调用
protected override void TearDown()
{
}
}
}
MyClientPeer:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Photon.SocketServer;
namespace MyGameServer
{
public class MyClientPeer : Photon.SocketServer.ClientPeer
{
//处理客户端连接断开的后续工作
protected override void OnDisconnect(PhotonHostRuntimeInterfaces.DisconnectReason reasonCode, string reasonDetail)
{
}
//处理客户端请求操作
protected override void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters)
{
}
}
}
部署并启动第一个服务器端项目
前提:上面我们已经将项目生成至PhotonServer中创建的项目的bin目录下!
配置PhotonServer.config文件如下(直接修改示例项目的配置文件即可):
<MyGameInstance
MaxMessageSize="512000"
MaxQueuedDataPerPeer="512000"
PerPeerMaxReliableDataInTransit="51200"
PerPeerTransmitRateLimitKBSec="256"
PerPeerTransmitRatePeriodMilliseconds="200"
MinimumTimeout="5000"
MaximumTimeout="30000"
DisplayName="My Game"
>
<!-- 0.0.0.0 opens listeners on all available IPs. Machines with multiple IPs should define the correct one here. -->
<!-- Port 5055 is Photon's default for UDP connections. -->
<UDPListeners>
<UDPListener
IPAddress="0.0.0.0"
Port="5055"
OverrideApplication="MyGame1">
</UDPListener>
</UDPListeners>
<!-- 0.0.0.0 opens listeners on all available IPs. Machines with multiple IPs should define the correct one here. -->
<!-- Port 4530 is Photon's default for TCP connecttions. -->
<!-- A Policy application is defined in case that policy requests are sent to this listener (known bug of some some flash clients) -->
<TCPListeners>
<TCPListener
IPAddress="0.0.0.0"
Port="4530"
PolicyFile="Policy\assets\socket-policy.xml"
InactivityTimeout="10000"
OverrideApplication="MyGame1"
>
</TCPListener>
</TCPListeners>
<!-- Defines the Photon Runtime Assembly to use. -->
<Runtime
Assembly="PhotonHostRuntime, Culture=neutral"
Type="PhotonHostRuntime.PhotonDomainManager"
UnhandledExceptionPolicy="Ignore">
</Runtime>
<!-- Defines which applications are loaded on start and which of them is used by default. Make sure the default application is defined. -->
<!-- Application-folders must be located in the same folder as the bin_win32 folders. The BaseDirectory must include a "bin" folder. -->
<Applications Default="MyGame1">
<!-- MMO Demo Application -->
<Application
Name="MyGame1"
BaseDirectory="MyGameServer"
Assembly="MyGameServer"
Type="MyGameServer.MyGameServer"
ForceAutoRestart="true"
WatchFiles="dll;config"
ExcludeFiles="log4net.config">
</Application>
</Applications>
</MyGameInstance>
配置PhotonServer中日志Log4Net
1.在项目里添加log4net的配置文件(log4net.config),可以直接在PhotonServer示例项目中拷贝,拷贝至项目的根目录
修改拷贝的log4net.config文件:修改原项目名为自己的项目名
<file type="log4net.Util.PatternString" value="%property{Photon:ApplicationLogPath}\\MyGame.Server.log" />
将log4net.config文件属性设为“始终复制”:
若为“不可复制”,log4net.config文件则无法生成至PhotonServer部署目录
2.添加相关引用
3.代码:
using System;
using System.IO;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Photon.SocketServer;
using ExitGames.Logging;
using ExitGames.Logging.Log4Net;
using log4net.Config;
namespace MyGameServer
{
class MyGameServer:ApplicationBase
{
//获取当前的Log对象
public static readonly ILogger log = LogManager.GetCurrentClassLogger();
//当客户端连接过来时调用
protected override PeerBase CreatePeer(InitRequest initRequest)
{
return new MyClientPeer(initRequest);
}
//Server启动时调用此方法
protected override void Setup()
{
InitLogging();
log.Info("==============Server Setup Complete===================");
}
//日志的初始化
protected virtual void InitLogging()
{
log4net.GlobalContext.Properties["Photon:ApplicationLogPath"] = Path.Combine(Path.Combine(this.ApplicationRootPath, "bin_Win32"), "log");
FileInfo configFileInfo = new FileInfo(Path.Combine(this.BinaryPath, "log4net.config"));
if (configFileInfo.Exists) //判断文件是否存在
{
LogManager.SetLoggerFactory(Log4NetLoggerFactory.Instance); //让Photon知道使用的是log4net插件
XmlConfigurator.ConfigureAndWatch(configFileInfo); //让log4net插件读取配置文件
}
}
//Server关闭时调用此方法
protected override void TearDown()
{
log.Info("==============Server Tear Down===================");
}
}
}
创建Unity客户端
1.把PhotonServer中的bin目录中Photon3Unity3D.dll添加到unity客户端项目中
2.创建单例的PhotonEngine来管理和服务器端的通信
using UnityEngine;
public class PhotonEngine : MonoBehaviour {
private static PhotonEngine Instance; //单例
void Awake()
{
if(Instance == null)
{
Instance = this;
DontDestroyOnLoad(this.gameObject);
}
else if(Instance != this)
{
Destroy(this.gameObject);
return;
}
}
void Start () {
}
// Update is called once per frame
void Update () {
}
}
在客户端发起连接服务器端的请求
1.引入命名空间using ExitGames.Client.Photon;
2.实现IPhotonPeerListener 接口,实现相应方法,进行客户端监听
3.创建一个PhotonPeer 对象处理客户端与服务器端的通信,进行连接服务器端的请求
peer = new PhotonPeer(this, ConnectionProtocol.Udp);
peer.Connect(“127.0.0.1:5055”,”MyGame1”);
4.在当peer一直处于连接状态时,Update方法中一直调用peer.Service();方法
5.在OnDestory()方法中处理客户端与服务器端的断开连接操作
peer.Disconnect();
using UnityEngine;
using ExitGames.Client.Photon;
using System;
public class PhotonEngine : MonoBehaviour,IPhotonPeerListener {
private static PhotonEngine Instance; //单例
private static PhotonPeer peer;
public static PhotonPeer Peer
{
get
{
return peer;
}
}
void Awake()
{
if(Instance == null)
{
Instance = this;
DontDestroyOnLoad(this.gameObject);
}
else if(Instance != this)
{
Destroy(this.gameObject);
return;
}
}
void Start () {
//通过Listener来接受服务器端的响应
peer = new PhotonPeer(this, ConnectionProtocol.Udp);
peer.Connect("127.0.0.1:5055","MyGame1");
}
void Update () {
peer.Service();
}
void OnDestroy()
{
if (peer != null && peer.PeerState == PeerStateValue.Connected)
{
peer.Disconnect();
}
}
public void DebugReturn(DebugLevel level, string message)
{
throw new NotImplementedException();
}
public void OnEvent(EventData eventData)
{
throw new NotImplementedException();
}
//当服务器response时调用此方法
public void OnOperationResponse(OperationResponse operationResponse)
{
throw new NotImplementedException();
}
//当peer状态发生改变时调用该方法
public void OnStatusChanged(StatusCode statusCode)
{
throw new NotImplementedException();
}
}
在客户端发起请求
第一个参数(OperationCode):唯一标识此请求的Code,用来在服务器端区分不同的请求
第二个参数(OperationParams):请求时发送的数据
第三个参数:true表示数据稳定发送
PhotonEngine.Peer.OpCustom(1, data, true);
using System.Collections.Generic;
using UnityEngine;
public class Test : MonoBehaviour {
void Start () {
}
void Update () {
if (Input.GetMouseButtonDown(0)) //按下鼠标左键
{
Dictionary<byte, object> data = new Dictionary<byte, object>();
PhotonEngine.Peer.OpCustom(1, data, true);
}
}
}
通过SendOperationResponse带参数响应给客户端
1.operationRequest.OperationCode
服务器端通过接收客户端的OperationCode来处理相应的请求,该操作在OnOperationRequest方法里执行
2.SendOperationResponse(operationResponse, sendParameters);
服务器端响应给客户端
3.客户端在OnOperationResponse方法里接收客户端的响应,并通过operationResponse.OperationCode来区别不同的响应,进行不同的操作
服务器端(MyClientPeer):
//处理客户端请求操作
protected override void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters)
{
switch (operationRequest.OperationCode)
{
case 1:
MyGameServer.log.Info("========================receive a client reauest=============================");
OperationResponse operationResponse = new OperationResponse(operationRequest.OperationCode);
SendOperationResponse(operationResponse, sendParameters); //response to client
break;
default:
break;
}
}
客户端:
//当服务器response时调用此方法
public void OnOperationResponse(OperationResponse operationResponse)
{
switch (operationResponse.OperationCode)
{
case 1:
Debug.Log("receive a server response,the operationCode is " + operationResponse.OperationCode);
break;
default:
break;
}
}
通过SendEvent带参数响应给客户端
1.SendEvent(eventData, sendParameters);
服务器端响应给客户端
2.客户端在OnEvent方法里接收客户端的响应,并通过eventData.Code来区别不同的响应,进行不同的操作
服务器端(MyClientPeer):
//处理客户端请求操作
protected override void OnOperationRequest(OperationRequest operationRequest, SendParameters sendParameters)
{
switch (operationRequest.OperationCode)
{
case 1:
MyGameServer.log.Info("========================receive a client request=============================");
object value1; object value2;
Dictionary<byte, object> data1 = operationRequest.Parameters;
data1.TryGetValue(1, out value1);
data1.TryGetValue(2, out value2);
MyGameServer.log.Info("get data from client the data is " + value1 + " and " + value2);
OperationResponse operationResponse = new OperationResponse(operationRequest.OperationCode);
Dictionary<byte, object> data2 = new Dictionary<byte, object>();
data2.Add(3, "test");
data2.Add(4, true);
EventData eventData = new EventData(1);
eventData.SetParameters(data2);
SendEvent(eventData, sendParameters);
break;
default:
break;
}
}
客户端:
public void OnEvent(EventData eventData)
{
Debug.Log("receive a eventData from server ,the eventCode is " + eventData.Code);
switch (eventData.Code)
{
case 1:
Dictionary<byte, object> data = eventData.Parameters;
object value1, value2;
data.TryGetValue(3, out value1);
data.TryGetValue(4, out value2);
Debug.Log("get value from server ,the value is " + value1 + " and " + value2);
break;
default:
break;
}
}