Socket通信介紹
socket本質是編程接口(API),對TCP/IP的封裝,TCP/IP也要提供可供程序員做網絡開發所用的接口,這就是Socket編程接口。一個socket接口包括ip和對應的端口,建立連接的過程如下:
1. 服務器監聽:是服務器端套接字並不定位具體的客戶端套接字,而是處於等待連接的狀態,實時監控網絡狀態。
2. 客戶端請求:是指由客戶端的套接字提出連接請求,要連接的目標是服務器端的套接字。
3. 連接確認:是指當服務器端套接字監聽到或者說接收到客戶端套接字的連接請求,它就響應客戶端套接字的請求,建立一個新的線程,把服務器端套接字的描述發給客戶端,一旦客戶端確認了此描述,連接就建立好了。而服務器端套接字繼續處於監聽狀態,繼續接收其他客戶端套接字的連接請求。
異步編程
同步(Synchronous)
在執行某個操作時,應用程序必須等待該操作執行完成後才能繼續執行。
異步(Asynchronous)
在執行某個操作時,應用程序可在異步操作執行時繼續執行。實質:異步操作,啓動了新的線程,主線程與方法線程並行執行。
- 異步的實質是開啓了新的線程,異步線程是由線程池負責管理,多線程是由編程人員控制。
- 服務端使用異步編程,可以方便的接受多個客戶端進行連接,否則需要爲每一個連接的客戶端開闢一個新的線程。
- 當編寫GUI程序時,如果使用同步編程將會造成界面無法刷新,卡死,解決方法包括開闢新線程或者使用異步編程。
- C#中提供了方便的異步編程。
代碼解析
服務端
啓動服務器
private void startServer()
{
ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 2000);
serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
serverSocket.Bind(ipep);
serverSocket.Listen(10);
// 開啓異步監聽端口
serverSocket.BeginAccept(new AsyncCallback(AcceptFunc), serverSocket);
Console.WriteLine("Waiting to connect");
}
監聽連接
private void AcceptFunc(IAsyncResult iar)
{
try
{
Socket s = (Socket)iar.AsyncState; // 異步對象,用於遞歸
Socket client = s.EndAccept(iar); // 客戶端的實例
IPEndPoint clientep = (IPEndPoint)client.RemoteEndPoint; // 獲得遠端的端點,並打印遠端的實例信息
Console.WriteLine("Client:" + clientep.Address + "(" + clientep.Port + ")" + "is Connect.");
// 異步接受消息
client.BeginReceive(receiveBuffer, 0, receiveBuffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), client);
string str = "this is the 1st msg!";
buffer = Encoding.Unicode.GetBytes(str);
// 異步發送消息
client.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(SendFunc), client);
s.BeginAccept(new AsyncCallback(AcceptFunc), s); //使用異步對象進行遞歸
}
catch(Exception e)
{
Console.WriteLine("accept Exception: "+e.Message);
}
}
異步接收消息
private void ReceiveMessage(IAsyncResult ar)
{
try
{
Socket s = (Socket)ar.AsyncState;
int length = s.EndReceive(ar);
string message = Encoding.Unicode.GetString(receiveBuffer, 0, length);
Console.WriteLine(message);
//遞歸進行調用
s.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), s);
}
catch(Exception e)
{
Console.WriteLine(e.Message);
}
}
異步發送消息
每隔一秒發送一條消息
private void SendFunc(IAsyncResult iar)
{
try
{
Socket s = (Socket)iar.AsyncState;
var message = "this is manager from server!" + cnt;
cnt++;
buffer = Encoding.Unicode.GetBytes(message);
Thread.Sleep(1000);
s.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(SendFunc), s);
}
catch(Exception e)
{
Console.WriteLine("sendException: "+e.Message);
}
}
發送端
啓動客戶端
private void startConnect()
{
ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 2000);
clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// 異步連接
clientSocket.BeginConnect(ipep, new AsyncCallback(ConnectFunc),clientSocket);
Console.WriteLine("Waiting to connect");
}
異步連接
在服務器未開啓的時候,會一直報異常。連接成功後開啓異步接收
private void ConnectFunc(IAsyncResult iar)
{
try
{
Socket s = (Socket)iar.AsyncState; // 獲得原始的socket
s.EndConnect(iar);
Console.WriteLine("Connected!");
// 異步接收
s.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), s);
Console.WriteLine("begin receive!");
}
catch(Exception e)
{
Console.WriteLine(e.Message);
// 持續的連接服務器
Socket s = (Socket)iar.AsyncState;
s.BeginConnect(ipep, new AsyncCallback(ConnectFunc), s);
}
}
異步接收
private void ReceiveMessage(IAsyncResult ar)
{
try
{
Socket s = (Socket)ar.AsyncState;
int length = s.EndReceive(ar);
string message = Encoding.Unicode.GetString(buffer, 0, length);
Console.WriteLine(message);
//遞歸進行調用,使用獲得的異步對象進行遞歸
s.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveMessage), s);
}
catch(Exception e)
{
Console.WriteLine("receive Exception: " + e.Message);
}
}
由於這裏寫的是控制檯程序,不適合進行發送,客戶端發送程序將在下一篇GUI發送端實現。
程序效果截圖
- 左邊是服務器,可以接受多個客戶端連接
- 中間和右邊分別是兩個客戶端連接服務器並接收消息。
初次接觸C#的Socket通信,C#提供了豐富的接口函數,可以方便的編程。下一篇將介紹GUI的socket通信