距離上次更博已經(jīng)好長(zhǎng)的時(shí)間了砚哆,上班了工作了,確實(shí)空閑下來的時(shí)間少了屑墨,然而我更多的在思考在于躁锁,寫些啥,有啥東西是可以放進(jìn)博客里的卵史,千篇一律的教程亦或是自己研究出來的東西战转,我想更應(yīng)該是后者吧,不然我這博客也是搭得沒意思以躯。
話不多說槐秧,今天我們來了解下Socket相關(guān)的知識(shí),可以說的上是基礎(chǔ)寸潦,做了點(diǎn)小通信(其實(shí)也簡(jiǎn)單啦)~
實(shí)現(xiàn)效果
先說整個(gè)實(shí)例在實(shí)現(xiàn)效果上,由于本實(shí)例只是對(duì)Socket相關(guān)的使用做了個(gè)基本的測(cè)試社痛,所以在效果上確實(shí)是差強(qiáng)人意见转,也就不上圖了,大體的效果描述就是:在Unity搭建的客戶端蒜哀,創(chuàng)建的UI按鈕上進(jìn)行點(diǎn)擊斩箫,給服務(wù)端進(jìn)行消息的發(fā)送,而后服務(wù)端返回相應(yīng)的消息撵儿,不同的消息發(fā)送有不同的回應(yīng)而已乘客。
效果不重要,畢竟才基礎(chǔ)淀歇,里面的原理才是我們需要了解的~
服務(wù)端搭建
這里的服務(wù)端易核,博主采取的是通過VS創(chuàng)建控制臺(tái)程序,在電腦上開啟服務(wù)端代碼浪默,主程序入口如下
Program.cs
using System;
class Program
{
static void Main(string[] args)
{
Server server = new Server("127.0.0.1",8899);
server.Start();
Console.ReadKey();
}
}
其實(shí)主程序無非就是創(chuàng)建了服務(wù)類牡直,開啟服務(wù)而已,而這里的服務(wù)類就是Socket相關(guān)使用的主體了
Server.cs
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
class Server
{
private Socket serverSocket;
private Socket clientSocket;
private IPAddress ipAddress;
private IPEndPoint ipEndPoint;
private List<Client> clientList = new List<Client>();
public Server(string ip,int port)
{
//創(chuàng)建Socket
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//聲明ip地址和端口號(hào)
this.ipAddress = IPAddress.Parse(ip);
this.ipEndPoint = new IPEndPoint(ipAddress, port);
}
public void Start()
{
//Socket綁定IP和端口號(hào)
serverSocket.Bind(ipEndPoint);
//Socket開始監(jiān)聽
serverSocket.Listen(0);
Console.WriteLine("服務(wù)器已經(jīng)啟動(dòng)");
//客戶端接入
serverSocket.BeginAccept(AcceptCallback,null);
}
private void AcceptCallback(IAsyncResult ar)
{
clientSocket = serverSocket.EndAccept(ar);
//獲取客戶端名稱
string clientName = clientSocket.RemoteEndPoint.ToString();
Client client = new Client(clientSocket, clientName);
client.Start();
Console.WriteLine(clientName + "已經(jīng)登錄纳决。碰逸。。");
clientList.Add(client);
//繼續(xù)循環(huán)監(jiān)聽客戶端的連接
serverSocket.BeginAccept(AcceptCallback, null);
}
}
Server類的主要功能無非就是開啟個(gè)Socket綁定確定的IP和端口阔加,時(shí)刻監(jiān)聽客戶端的連接饵史。程序在這里采取的是異步方式,之所以代碼上沒有明顯的線程調(diào)用,是因?yàn)樵诓捎昧朔庋b好的API胳喷,像 BeginXXX 這樣的函數(shù)湃番,為了能夠循環(huán)監(jiān)聽客戶端的連接也是在AcceptCallback里面繼續(xù)開啟客戶端的監(jiān)聽,大體的邏輯是這樣厌蔽。
而這里的Client類可不是代表實(shí)際意義上的客戶端牵辣,這里只是對(duì)客戶端進(jìn)行的相關(guān)管理,主要是對(duì)客戶端的消息發(fā)送和接收進(jìn)行著處理奴饮,之所以采取兩個(gè)類的方式纬向,也是受到面向?qū)ο笏枷氲挠绊懀瑯颖阌诖a的管理戴卜。
Client.cs
using System;
using System.Text;
using System.Net.Sockets;
class Client
{
private Socket clientSocket;
private string clientName;
private byte[] recieveData;
public Client(Socket client,string clientName)
{
this.clientSocket = client;
this.clientName = clientName;
recieveData = new byte[clientSocket.ReceiveBufferSize];
}
public void Start()
{
clientSocket.BeginReceive(recieveData,0, clientSocket.ReceiveBufferSize,SocketFlags.None, RecieveCallback, null);
}
//接收客戶端的消息
private void RecieveCallback(IAsyncResult ar)
{
try
{
//接收到的數(shù)據(jù)長(zhǎng)度
int count = clientSocket.EndReceive(ar);
//防止客戶端異常退出
if (count == 0)
{
clientSocket.Close();
return;
}
//對(duì)接收到的數(shù)據(jù)進(jìn)行處理
string msgRec = Encoding.UTF8.GetString(recieveData, 0, count);
HandleResponse(msgRec);
//輸出到控制臺(tái)
Console.WriteLine(msgRec);
//循環(huán)接收客戶端發(fā)送過來的數(shù)據(jù)
clientSocket.BeginReceive(recieveData, 0, clientSocket.ReceiveBufferSize, SocketFlags.None, RecieveCallback, null);
}
catch (Exception)
{
if (clientSocket != null)
{
clientSocket.Close();
return;
}
}
}
//對(duì)客戶端返回過來的數(shù)據(jù)進(jìn)行處理
private void HandleResponse(string data)
{
if (data.Equals("hello"))
{
SendMessage("hello too");
}else if (data.Equals("haha"))
{
SendMessage("heihei");
}
}
//發(fā)送消息給客戶端
private void SendMessage(string data)
{
byte[] msgData = Encoding.UTF8.GetBytes(data);
clientSocket.Send(msgData);
}
}
服務(wù)器端對(duì)客戶端發(fā)出的請(qǐng)求是在對(duì)消息解析后才處理的逾条,也就是我們的HandleResponse函數(shù)里面,這里只是簡(jiǎn)單的對(duì)客戶端發(fā)送過來的字符串進(jìn)行簡(jiǎn)單的if else判斷而已~
客戶端創(chuàng)建
相對(duì)服務(wù)器端的構(gòu)建投剥,客戶端算是簡(jiǎn)單了許多师脂,這里也就只有一個(gè)類來測(cè)試的。
Client.cs
using UnityEngine;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System;
using UnityEngine.UI;
public class Client : MonoBehaviour {
private Socket clientSocket;
private byte[] recieveData;
private Button btn1;
private Button btn2;
// Use this for initialization
void Start () {
InitNet();
btn1 = transform.Find("Btn1").GetComponent<Button>();
btn2 = transform.Find("Btn2").GetComponent<Button>();
btn1.onClick.AddListener(() => {
SendMsg("hello");
});
btn2.onClick.AddListener(() => {
SendMsg("haha");
});
}
private void InitNet()
{
// 首先聲明一個(gè)Socket
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//申明IP和端口號(hào)
IPAddress iPAddress = IPAddress.Parse("127.0.0.1");
IPEndPoint iPEndPoint = new IPEndPoint(iPAddress, 8899);
//連接服務(wù)器端
clientSocket.Connect(iPEndPoint);
//接收服務(wù)端發(fā)送過來的消息
recieveData = new byte[clientSocket.ReceiveBufferSize];
clientSocket.BeginReceive(recieveData, 0, clientSocket.ReceiveBufferSize, SocketFlags.None, RevieveCallback, null);
}
private void RevieveCallback(IAsyncResult ar)
{
int count = clientSocket.EndReceive(ar);
string msgRec = Encoding.UTF8.GetString(recieveData, 0, clientSocket.ReceiveBufferSize);
//輸出信息
Debug.Log(msgRec);
recieveData = new byte[clientSocket.ReceiveBufferSize];
clientSocket.BeginReceive(recieveData, 0, clientSocket.ReceiveBufferSize, SocketFlags.None, RevieveCallback, null);
}
private void SendMsg(string data)
{
byte[] sendMsg = Encoding.UTF8.GetBytes(data);
clientSocket.Send(sendMsg);
}
}
在客戶端初始Socket江锨,確認(rèn)IP和端口號(hào)就能連上服務(wù)器了吃警,然后通過Unity的組件進(jìn)行消息的發(fā)送,然后接收相關(guān)消息啄育,流程一步了然酌心,簡(jiǎn)直不要太簡(jiǎn)單~
源碼地址
源碼工程已上傳Github了,在這里
總結(jié)
通過這個(gè)小的實(shí)例可以了解到Socket的基本編程挑豌,以及簡(jiǎn)單的網(wǎng)絡(luò)通信安券,當(dāng)然這只是在局域網(wǎng)下能夠通信哈,而后博主也是會(huì)加大難度氓英,基于此開發(fā)一個(gè)網(wǎng)絡(luò)游戲侯勉,在廣域網(wǎng)下也能完美運(yùn)行(當(dāng)然現(xiàn)在服務(wù)器都沒有233333)
就這么多,白了個(gè)白~