u3d簡單網遊的通信,拿某簡單例子學習一下

這個例子是某網站的一個視頻教程的例子,但教程沒有提供代碼,跟隨教程敲代碼根本做不出東西,所以第一課的時候有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)

點我下載


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章