此教程純屬Socket初級應用篇,由於網上全是理論篇(實踐纔是王道)java
建立Socket實例,別忘了引用System.Net和System.Net.Socketsgit
Socket client = new Socket(SocketType.Stream, ProtocolType.Tcp); // TCP連接
設置要連接的服務器ip地址,IPAddress是C#提供的ip封裝類github
IPAddress ip = IPAddress.Parse("127.0.0.1"); // 本地地址127.0.0.1(別說你不知道)
設置要連接的服務器ip和端口,IPEndPoint是C#提供的ip和端口的封裝類api
IPEndPoint point = new IPEndPoint(ip, 2333); // 端口爲2333,ip爲上一段代碼的ip
連接數組
client.Connect(point); // client.Connect("127.0.0.1", 2333); // 等同於3,4
開啓線程接收服務器消息,別忘了引用System.Threading服務器
Thread thread = new Thread(Recive); thread.IsBackground = true; // 後臺執行線程 thread.Start(client); // 傳入客戶端的Socket
// Recive函數 static void Recive(object o) { var client = o as Socket; while (true) { byte[] buffer = new byte[1024 * 1024 * 2]; int effective = client.Receive(buffer); //二進制數據存儲在buffer中,數據長度爲effective if (effective == 0) { break; } var str = Encoding.UTF8.GetString(buffer, 0, effective); // 將二進制數據轉換爲UTF8格式的String Console.WriteLine(str); } }
發送自定義數據給服務器socket
while (true) { string s = Console.ReadLine(); byte[] buffer = Encoding.ASCII.GetBytes(s); // 將數據轉換爲ASCII編碼的二進制數組形式 socketClient.Send(buffer); // 發送消息 Console.WriteLine("Send Message"); }
client完整代碼函數
using System; using System.IO; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; namespace SocketTest { class Program { static void Main(string[] args) { Socket client = new Socket(SocketType.Stream, ProtocolType.Tcp); IPAddress ip = IPAddress.Parse("127.0.0.1"); IPEndPoint point = new IPEndPoint(ip, 2333); client.Connect("127.0.0.1", 2333); Thread thread = new Thread(Recive); thread.IsBackground = true; thread.Start(client); while (true) { string s = Console.ReadLine(); byte[] buffer = Encoding.ASCII.GetBytes(s); client.Send(buffer); Console.WriteLine("Send Message"); } } static void Recive(object o) { var client = o as Socket; while (true) { byte[] buffer = new byte[1024 * 1024 * 2]; var effective = client.Receive(buffer); if (effective == 0) { break; } var str = Encoding.UTF8.GetString(buffer, 0, effective); Console.WriteLine(str); } } } }
設置服務器的ip地址工具
IPAddress ip = IPAddress.Parse("127.0.0.1"); IPEndPoint point = new IPEndPoint(ip, 2333); server.Bind(point);
設置服務器的最大監聽數this
server.Listen(10);
開啓線程接收客戶端鏈接和數據(※注意是鏈接)
Thread thread = new Thread(Listen); thread.IsBackground = true; thread.Start(serverSocket);
// Listen函數,等待客戶端鏈接 static void Listen(object o) { var serverSocket = o as Socket; while (true) { client = serverSocket.Accept(); // 等待客戶端鏈接,返回客戶端的Socket,以前的10限制就是在這裏,最多有10個客戶端能夠創建鏈接 var sendIpoint = client.RemoteEndPoint.ToString(); // 客戶端的ip和端口 Console.WriteLine($"{sendIpoint}Connection"); // 鏈接成功則開啓一個接收線程接收客戶端發來的消息 Thread thread = new Thread(Recive); thread.IsBackground = true; thread.Start(client); } }
// Recive函數,同客戶端
server完整代碼
using System; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; namespace SocketServer { class Program { static void Main(string[] args) { Socket server = new Socket(SocketType.Stream, ProtocolType.Tcp); IPAddress ip = IPAddress.Parse("127.0.0.1"); IPEndPoint point = new IPEndPoint(ip, 2333); server.Bind(point); server.Listen(10); Thread thread = new Thread(Listen); thread.IsBackground = true; thread.Start(server); Console.Read(); } static void Listen(object o) { var server = o as Socket; while (true) { client = server.Accept(); var clientIpoint = client.RemoteEndPoint.ToString(); Console.WriteLine($"{clientIpoint}Connection"); Thread thread = new Thread(Recive); thread.IsBackground = true; thread.Start(client); } } static void Recive(object o) { var client = o as Socket; while (true) { byte[] buffer = new byte[1024 * 1024 * 2]; var effective = client.Receive(buffer); if (effective == 0) { break; } var str = Encoding.UTF8.GetString(buffer, 0, effective); Console.WriteLine(str); } } } }
若是咱們要用ProtoBuf用在C#中就得集齊各類神器
.proto
文件都說ProtoBuf不依賴於任何語言是一個跨語言的神器,然而他的語言格式是scheme(Lisp的方言),而且編譯器就是把.proto
文件翻譯成各個不一樣語言的編譯器。
proto文件示例(教程中示例的文件)
// [START declaration] syntax = "proto3"; package tutorial; import "google/protobuf/timestamp.proto"; // [END declaration] // [START java_declaration] option java_package = "com.example.tutorial"; option java_outer_classname = "AddressBookProtos"; // [END java_declaration] // [START csharp_declaration] option csharp_namespace = "Google.Protobuf.Examples.AddressBook"; // [END csharp_declaration] // [START messages] message Person { string name = 1; int32 id = 2; // Unique ID number for this person. string email = 3; enum PhoneType { MOBILE = 0; HOME = 1; WORK = 2; } message PhoneNumber { string number = 1; PhoneType type = 2; } repeated PhoneNumber phones = 4; google.protobuf.Timestamp last_updated = 5; } // Our address book file is just one of these. message AddressBook { repeated Person people = 1; } // [END messages]
將上面的文件用proto.exe編譯
格式:proto -I=當前目錄 -out_csharp=當前目錄 目錄/文件名.擴展名 例: 文件名爲:address.proto 目錄爲:D:/Work下 proto -I=D:/Work -out_csharp=D:/Work D:/Wrok/address.proto
編譯完後會生成一個.cs
文件,文件很長我就不展現了
nuget install Google.Protobuf -Version 3.8.0 // 版本隨意
下載下來找到dll
Google.Protobuf.3.8.0/bin/net45/Google.Protobuf.dll
引用到客戶端和服務器中,而後把生成的.cs
文件也複製到項目中
在客戶端和服務器的Program(上面的代碼)中加入序列化和反序列化的函數,須要引用Google.Protobuf、Google.Protobuf.Examples.AddressBook和static Google.Protobuf.Examples.AddressBook.Person.Types(C#6 才支持)
public static byte[] Serialize<T>(T obj) where T : IMessage { return obj.ToByteArray(); } public static T Deserialize<T>(byte[] data) where T : class, IMessage, new() { T obj = new T(); IMessage message = obj.Descriptor.Parser.ParseFrom(data); return message as T; }
註釋:
.proto
轉換成.cs
的文件的父接口爲IMessage知道這些以後就能夠傳輸二進制數據啦,因此咱們的客戶端代碼的發送數據部分改成
// 創建數據 Person john = new Person { Id = 1234, Name = "John Doe", Email = "jdoe@example.com", Phones = { new PhoneNumber { Number = "555-4321", Type = PhoneType.Home } } }; var message = Serialize(john); // 獲得byte[]的message while (true) { string s = Console.ReadLine(); socketClient.Send(message); // 直接傳輸message Console.WriteLine("Send Message"); }
服務端的接受部分改一下,這部分注意反序列化素組的長度必須和序列化後的一致,因此這邊新建了一個正確長度的b2數組把數據複製過去
static void Recive(object o) { var send = o as Socket; while (true) { byte[] buffer = new byte[1024 * 1024 * 2]; var effective = send.Receive(buffer); byte[] b2 = new byte[effective]; Array.Copy(buffer, 0, b2, 0, effective); // 把數據拷貝給b2 if (effective == 0) { break; } var message = Deserialize<Person>(b2); // 解析時候必須爲正確的長度 Console.WriteLine("ID = {0}", message.Id); Console.WriteLine("Name = {0}", message.Name); Console.WriteLine("Email = {0}", message.Email); Console.WriteLine("Phone Number = {0}", message.Phones[0].Number); Console.WriteLine("Phone Type = {0}", message.Phones[0].Type); } }
完結撒花~