這個例子是某網站的一個視頻教程的例子,但教程沒有提供代碼,跟隨教程敲代碼根本做不出東西,所以第一課的時候有1700多人蔘加學習,完整聽完的人數只有不到40,很多人都是第4課就放棄了,因爲老師沒有提供相關的工具代碼,字符串解析根本無法通信,造成了聽課人員的流失,其實聽過這個課程之後,我覺得課程還是不錯的,雖然沒有提供工程樣例和公共部分的代碼,但如果有c#網絡經驗的話,完成這個例子其實也是可以的,因爲,畢竟服務器端的代碼是提供了,我覺得,弄到一起完全可以當成3d網遊的一般框架來學習,看下我自己做的客戶端的運行效果,界面的問題就不要吐槽了,因爲沒有什麼都沒提供,所以只好在自己電腦裏面東拼西湊需要的東西,有些界面不太好看是很正常的,我們只是看這個的原理,
基本代碼,net連接
using UnityEngine;
using System.Collections;
using System.Net.Sockets;
using System.IO;
using System;
using System.Text;
using LitJson;
using System.Threading;
using System.Collections.Generic;
public class NetWorkScript
{
private static NetWorkScript script;
private Socket socket;
public static string host ="127.0.0.1";//服務器IP地址
private int port = 10100;//服務器端口
private byte[] readM = new byte[1024];
private ByteArray ioBuff=new ByteArray();
private int dataSize;
private List<SocketModel> messages = new List<SocketModel>();
private bool isRead = false;
//獲取連接對象
public static NetWorkScript getInstance() {
if (script == null) {
//第一次調用的時候 創建單例對象 並進行初始化操作
script = new NetWorkScript();
script.init();
}
return script;
}
private void init() {
try
{
//創建socket連接對象
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//連接到服務器
socket.Connect(host, port);
//連接後開始從服務器讀取網絡消息
socket.BeginReceive(readM, 0, 1024, SocketFlags.None, ReceiveCallBack,readM);
Debug.Log("socket session open");
}
catch (Exception e) {
//連接失敗 打印異常
Debug.Log("Connect error:"+e.Message);
}
}
public void sendMessage(int type, int area, int command, string message) {
ByteArray arr= new ByteArray();
arr.WriteInt(type);
arr.WriteInt(area);
arr.WriteInt(command);
if (message != null)
{
arr.WriteInt(message.Length);
arr.WriteUTFBytes(message);
// arr.WriteUTFBytes(message);
}
else {
arr.WriteInt(0);
}
try
{
socket.Send(arr.Buffer);
}
catch {
Debug.Log("socket error");
}
}
public void onData()
{
//消息讀取完成後開始解析
if(ioBuff.Length<4){
//包頭爲長度4的整型
isRead = false;
return;
}
dataSize=ioBuff.ReadInt();
Debug.Log("dataSize"+dataSize);
if(dataSize>ioBuff.Length-4){
//包長不夠 等下個包的到來
Debug.Log("包長不夠");
ioBuff.Postion=0;
isRead = false;
return;
}
ByteArray ioData=new ByteArray();
ioData.WriteBytes(ioBuff.Buffer,4,dataSize);
ioBuff.Postion+=dataSize;
int type = ioData.ReadInt();//表示消息類型 我們這裏有兩種
int area = ioData.ReadInt();//這裏表示消息的區域碼 在登錄這樣的服務器單例模塊中 沒有效果 在地圖消息的時候用於區分屬於哪張地圖來的消息
int command = ioData.ReadInt();//模塊內部協議---具體稍後描述
int len = ioData.ReadInt();
string m=null;
if (len > 0) { m = ioData.ReadUTFBytes((uint)len); }//這裏開始就是讀取服務器傳過來的消息對象了 是一串json字符串
//轉換爲Socket消息模型
SocketModel model= new SocketModel();
model.type = type;
model.area = area;
model.command = command;
model.message = m;
Debug.Log(type+" "+area+" "+command+"length"+(16+len));
//消息接收完畢後,存入收到消息隊列
messages.Add(model);
ByteArray bytes=new ByteArray();
bytes.WriteBytes(ioBuff.Buffer, ioBuff.Postion, ioBuff.Buffer.Length - ioBuff.Postion);
ioBuff=bytes;
onData();
//Debug.Log("插入隊列後"+arr.Buffer[12+len+1]);
}
//這是讀取服務器消息的回調--當有消息過來的時候BgenReceive方法會回調此函數
private void ReceiveCallBack(IAsyncResult ar)
{
int readCount = 0;
try
{
//讀取消息長度
readCount = socket.EndReceive(ar);//調用這個函數來結束本次接收並返回接收到的數據長度。
Debug.Log("讀取消息長度" + readCount);
byte[] bytes = new byte[readCount];//創建長度對等的bytearray用於接收
Buffer.BlockCopy(readM, 0, bytes, 0, readCount);//拷貝讀取的消息到 消息接收數組
ioBuff.WriteBytes(bytes);
Debug.Log(ioBuff.Buffer.Length);
if (!isRead){
isRead = true;
onData();//消息讀取完成
}
}
catch (SocketException)//出現Socket異常就關閉連接
{
socket.Close();//這個函數用來關閉客戶端連接
return;
}
socket.BeginReceive(readM, 0, 1024, SocketFlags.None, ReceiveCallBack,readM);
}
public List<SocketModel> getList() {
return messages;
}
}
消息管理using UnityEngine;
using System.Collections;
using System.Collections.Generic;
//消息管理用於分發,消息管理器
public class MessageManager : MonoBehaviour {
private LoginHandler login;
private UserHandler user;
private MapHandler map;
// Use this for initialization
void Start () {
login=GetComponent<LoginHandler>();
user=GetComponent<UserHandler>();
map=GetComponent<MapHandler>();
}
// Update is called once per frame
void Update () {
List<SocketModel> list=NetWorkScript.getInstance().getList();
for(int i=0;i<8;i++){
if(list.Count>0){
//Debug.Log("onmessage");
SocketModel model=list[0];
//xiaoxichhuanru
OnMessage(model);
//yichudiyitiaoxiaoxi
list.RemoveAt(0);
}else{
//Debug.Log("messmaneer");
break;
}
}
}
public void OnMessage(SocketModel model){
Debug.Log("model"+model.type);
// if(model.type==520093696){
// login.OnMessage(model);
// }
switch(model.type){
case Protocol.LOGIN:
login.OnMessage(model);
break;
case Protocol.USER:
user.OnMessage(model);
break;
case Protocol.MAP:
map.OnMessage(model);
break;
default:
WindowConstans.windowList.Add(WindowConstans.SOCKET_TYPE_FAIL);
break;
}
}
}
using UnityEngine;
using System.Collections;
public class LoginHandler : MonoBehaviour {
// Use this for initialization
void Start () {
}
// Update is called once per frame
void Update () {
}
public void OnMessage(SocketModel model){
switch(model.command){
case LoginProtocol.LOGIN_SRES:
//Debug.Log("chuli消息"+model.Command);
loginResult(model.message);
break;
case LoginProtocol.RES_SRES:
RegResult(model.message);
break;
}
}
private void loginResult(string message){
Debug.Log(message);
StringDTO dto=Coding<StringDTO>.decode(message);
GameInfo.ACC_ID=dto.value;
if(dto.value==null||dto.value==string.Empty){
WindowConstans.windowList.Add(WindowConstans.LOGIN_FAIL);
}else{
GameInfo.ACC_ID=dto.value;
//GameInfo.GAME_STATE=GameState.LOADING;
BroadcastMessage ("Loading",1);
}
}
private void RegResult(string message){
Debug.Log(message);
BoolDTO dto=Coding<BoolDTO>.decode(message);
if(dto.value){
WindowConstans.windowList.Add(WindowConstans.ACC_REG_OK);
}else{
WindowConstans.windowList.Add(WindowConstans.ACC_REG_FAIL);
}
}
}
using UnityEngine;
using System.Collections;
public class BoolDTO {
public bool value;
public BoolDTO(){}
public BoolDTO(bool v){
this.value=v;
}
}
運行,因爲服務器使用的go語言,所以我們用liteide打開
using UnityEngine;
using System.Collections;
using LitJson;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
public class Coding<T> {
public static string encode(T model){
return JsonMapper.ToJson(model);
}
public static T decode(string message){
return JsonMapper.ToObject<T>(message);
}
}
啓動服務器
全部的代碼我就不做說明了,因爲那是個很大的話題,可能篇幅要比仙劍demo還要大,這裏就不找麻煩了,需要可以去看網上的一套免費課程,只不過那個課程沒有提供源碼和工程樣例而已。
這裏貼出工程地址(提取碼:852e)