C# Socket編程 同步以及異步通信
標(biāo)簽:
雜談套接字簡介:套接字最早是Unix的,window是借鑒過來的。TCP/IP協(xié)議族提供三種套接字:流式、數(shù)據(jù)報式、原始套接字。其中原始套接字允許對底層協(xié)議直接訪問,一般用于檢驗新協(xié)議或者新設(shè)備問題,很少使用。
套接字編程原理:延續(xù)文件作用思想,打開-讀寫-關(guān)閉的模式。
C/S編程模式如下:
Ø 服務(wù)器端:
打開通信通道,告訴本地機器,愿意在該通道上接受客戶請求——監(jiān)聽,等待客戶請求——接受請求,創(chuàng)建專用鏈接進行讀寫——處理完畢,關(guān)閉專用鏈接——關(guān)閉通信通道(當(dāng)然其中監(jiān)聽到關(guān)閉專用鏈接可以重復(fù)循環(huán))
Ø 客戶端:打開通信通道,連接服務(wù)器——數(shù)據(jù)交互——關(guān)閉信道。
Socket通信方式:
Ø 同步:客戶端在發(fā)送請求之后必須等到服務(wù)器回應(yīng)之后才可以發(fā)送下一條請求。串行運行
Ø 異步:客戶端請求之后,不必等到服務(wù)器回應(yīng)之后就可以發(fā)送下一條請求。并行運行
套接字模式:
Ø 阻塞:執(zhí)行此套接字調(diào)用時,所有調(diào)用函數(shù)只有在得到返回結(jié)果之后才會返回。在調(diào)用結(jié)果返回之前,當(dāng)前進程會被掛起。即此套接字一直被阻塞在網(wǎng)絡(luò)調(diào)用上。
Ø 非阻塞:執(zhí)行此套接字調(diào)用時,調(diào)用函數(shù)即使得不到得到返回結(jié)果也會返回。
套接字工作步驟:
Ø 服務(wù)器監(jiān)聽:監(jiān)聽時服務(wù)器端套接字并不定位具體客戶端套接字,而是處于等待鏈接的狀態(tài),實時監(jiān)控網(wǎng)絡(luò)狀態(tài)
Ø 客戶端鏈接:客戶端發(fā)出鏈接請求,要連接的目標(biāo)是服務(wù)器端的套接字。為此客戶端套接字必須描述服務(wù)器端套接字的服務(wù)器地址與端口號。
Ø 鏈接確認:是指服務(wù)器端套接字監(jiān)聽到客戶端套接字的鏈接請求時,它響應(yīng)客戶端鏈接請求,建立一個新的線程,把服務(wù)器端套接字的描述發(fā)送給客戶端,一旦客戶端確認此描述,則鏈接建立好。而服務(wù)器端的套接字繼續(xù)處于監(jiān)聽狀態(tài),繼續(xù)接受其他客戶端套接字請求。
在TCP/IP網(wǎng)絡(luò)中,IP網(wǎng)絡(luò)交互分類兩大類:面向連接的交互與面向無連接的交互。
Socket構(gòu)造函數(shù):public socket(AddressFamily 尋址類型, SocketType 套接字類型, ProtocolType 協(xié)議類型)。但需要注意的是套接字類型與協(xié)議類型并不是可以隨便組合。
SocketType
ProtocolType
描述
Stream
Tcp
面向連接
Dgram
Udp
面向無連接
Raw
Icmp
網(wǎng)際消息控制
Raw
Raw
基礎(chǔ)傳輸協(xié)議
Socket類的公共屬性:
屬性名
描述
AddressFamily
獲取Socket的地址族
Available
獲取已經(jīng)從網(wǎng)絡(luò)接收且可供讀取的數(shù)據(jù)量
Blocking
獲取或設(shè)置一個值,只是socket是否處于阻塞模式
Connected
獲取一個值,指示當(dāng)前連接狀態(tài)
Handle
獲取socket的操作系統(tǒng)句柄
LocalEndPoint
獲取本地終端EndPoint
RemoteEndPoint
獲取遠程終端EndPoint
ProtocolType
獲取協(xié)議類型
SocketType
獲取SocketType類型
Socket常用方法:
Bind(EndPoint)
服務(wù)器端套接字需要綁定到特定的終端,客戶端也可以先綁定再請求連接
Listen(int)
監(jiān)聽端口,其中parameters表示最大監(jiān)聽數(shù)
Accept()
接受客戶端鏈接,并返回一個新的鏈接,用于處理同客戶端的通信問題
Send()
發(fā)送數(shù)據(jù)
Send(byte[])
簡單發(fā)送數(shù)據(jù)
Send(byte[],SocketFlag)
使用指定的SocketFlag發(fā)送數(shù)據(jù)
Send(byte[], int, SocketFlag)
使用指定的SocketFlag發(fā)送指定長度數(shù)據(jù)
Send(byte[], int, int, SocketFlag)
使用指定的SocketFlag,將指定字節(jié)數(shù)的數(shù)據(jù)發(fā)送到已連接的socket(從指定偏移量開始)
Receive()
接受數(shù)據(jù)
Receive(byte[])
簡單接受數(shù)據(jù)
Receive (byte[],SocketFlag)
使用指定的SocketFlag接受數(shù)據(jù)
Receive (byte[], int, SocketFlag)
使用指定的SocketFlag接受指定長度數(shù)據(jù)
Receive (byte[], int, int, SocketFlag)
使用指定的SocketFlag,從綁定的套接字接收指定字節(jié)數(shù)的數(shù)據(jù),并存到指定偏移量位置的緩沖區(qū)
Connect(EndPoint)
連接遠程服務(wù)器
ShutDown(SocketShutDown)
禁用套接字,其中SocketShutDown為枚舉,Send禁止發(fā)送,Receive為禁止接受,Both為兩者都禁止
Close()
關(guān)閉套接字,釋放資源
異步通信方法:
BeginAccept(AsynscCallBack,object)
開始一個一步操作接受一個連接嘗試。參數(shù):一個委托。一個對象。對象包含此請求的狀態(tài)信息。其中回調(diào)方法中必須使用EndAccept方法。應(yīng)用程序調(diào)用BegineAccept方法后,系統(tǒng)會使用單獨的線程執(zhí)行指定的回調(diào)方法并在EndAccept上一直處于阻塞狀態(tài),直至監(jiān)測到掛起的鏈接。EndAccept會返回新的socket對象。供你來同遠程主機數(shù)據(jù)交互。不能使用返回的這個socket接受隊列中的任何附加連接。調(diào)用BeginAccept當(dāng)希望原始線程阻塞的時候,請調(diào)用WaitHandle.WaitOne方法。當(dāng)需要原始線程繼續(xù)執(zhí)行時請在回調(diào)方法中使用ManualResetEvent的set方法
BeginConnect(EndPoint, AsyncCallBack, Object)
回調(diào)方法中必須使用EndConnect()方法。Object中存儲了連接的詳細信息。
BeginSend(byte[], SocketFlag, AsyncCallBack, Object)
BegineReceive(byte[], SocketFlag, AsyncCallBack, Object)
BegineDisconnect(bool, AsyncCallBack, Object)
同步通信:
預(yù)定義結(jié)構(gòu)體,同步通信沒有多線程異步委托回調(diào),所以無需預(yù)定義結(jié)構(gòu)體
客戶端Client:
class Program
{
static void Main()
{
try{
int port = 2000;
string host = "127.0.0.1";
IPAddress ip = IPAddress.Parse(host);
IPEndPoint ipe = new IPEndPoint(ip, port);//把ip和端口轉(zhuǎn)化為IPEndPoint實例
Socket c = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//創(chuàng)建一個Socket
Console.WriteLine("Conneting...");
c.Connect(ipe);//連接到服務(wù)器
string sendStr = "hello!This is a socket test";
byte[] bs = Encoding.ASCII.GetBytes(sendStr);
Console.WriteLine("Send Message");
c.Send(bs, bs.Length, 0);//發(fā)送測試信息
string recvStr = "";
byte[] recvBytes = new byte[1024];
int bytes;
bytes = c.Receive(recvBytes, recvBytes.Length, 0);//從服務(wù)器端接受返回信息
recvStr += Encoding.ASCII.GetString(recvBytes, 0, bytes);
Console.WriteLine("Client Get Message:{0}", recvStr);//顯示服務(wù)器返回信息
c.Close();
}
catch (ArgumentNullException e){
Console.WriteLine("ArgumentNullException: {0}", e);
}
catch (SocketException e){
Console.WriteLine("SocketException: {0}", e);
}
Console.WriteLine("Press Enter to Exit");
Console.ReadLine();
}
}
服務(wù)器端:
class Program
{
static void Main()
{
try{
int port = 2000;
string host = "127.0.0.1";
IPAddress ip = IPAddress.Parse(host);
IPEndPoint ipe = new IPEndPoint(ip, port);
Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);//創(chuàng)建一個Socket類
s.Bind(ipe);//綁定2000端口
s.Listen(0);//開始監(jiān)聽
Console.WriteLine("Wait for connect");
Socket temp = s.Accept();//為新建連接創(chuàng)建新的Socket。
Console.WriteLine("Get a connect");
string recvStr = "";
byte[] recvBytes = new byte[1024];
int bytes;
bytes = temp.Receive(recvBytes, recvBytes.Length, 0);//從客戶端接受信息
recvStr += Encoding.ASCII.GetString(recvBytes, 0, bytes);
Console.WriteLine("Server Get Message:{0}", recvStr);//把客戶端傳來的信息顯示出來
string sendStr = "Ok!Client Send Message Sucessful!";
byte[] bs = Encoding.ASCII.GetBytes(sendStr);
temp.Send(bs, bs.Length, 0);//返回客戶端成功信息
temp.Close();
s.Close();
}
catch (ArgumentNullException e){
Console.WriteLine("ArgumentNullException: {0}", e);}
catch (SocketException e){
Console.WriteLine("SocketException: {0}", e);}
Console.WriteLine("Press Enter to Exit");
Console.ReadLine();
}
}
異步通信:
客戶端Client:
預(yù)定義結(jié)構(gòu)體,用于異步委托之間的傳遞。用戶根據(jù)自己需要定制即可
public class StateObject
{
// Client socket.
public Socket workSocket = null;
// Size of receive buffer.
public const int BufferSize = 256;
// Receive buffer.
public byte[] buffer = new byte[BufferSize];
// Received data string.
public StringBuilder sb = new StringBuilder();
}
正文:
public class AsynchronousClient
{
// The port number for the remote device.
private const int port = 11000;
// ManualResetEvent instances signal completion.
private static ManualResetEvent connectDone = new ManualResetEvent(false);
private static ManualResetEvent sendDone = new ManualResetEvent(false);
private static ManualResetEvent receiveDone = new ManualResetEvent(false);
// The response from the remote device.
private static String response = String.Empty;
private static void StartClient(){
// Connect to a remote device.
try{
// Establish the remote endpoint for the socket.
// The name of the remote device is "host.contoso.com".
IPHostEntry ipHostInfo = Dns.Resolve("host.contoso.com");
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint remoteEP = new IPEndPoint(ipAddress, port);
// Create a TCP/IP socket.
Socket client = new Socket(AddressFamily.InterNetwork,
SocketType.Stream, ProtocolType.Tcp);
// Connect to the remote endpoint.
client.BeginConnect(remoteEP,
new AsyncCallback(ConnectCallback), client);
connectDone.WaitOne();
// Send test data to the remote device.
Send(client, "This is a test<EOF>");
sendDone.WaitOne();
// Receive the response from the remote device.
Receive(client);
receiveDone.WaitOne();
// Write the response to the console.
Console.WriteLine("Response received : {0}", response);
// Release the socket.
client.Shutdown(SocketShutdown.Both);
client.Close();
}
catch (Exception e){
Console.WriteLine(e.ToString());}
}
private static void ConnectCallback(IAsyncResult ar)
{
try{
// Retrieve the socket from the state object.
Socket client = (Socket)ar.AsyncState;
// Complete the connection.
client.EndConnect(ar);
Console.WriteLine("Socket connected to {0}",
client.RemoteEndPoint.ToString());
// Signal that the connection has been made.
connectDone.Set();
}
catch (Exception e){
Console.WriteLine(e.ToString());}
}
private static void Receive(Socket client)
{
try{
// Create the state object.
StateObject state = new StateObject();
state.workSocket = client;
// Begin receiving the data from the remote device.
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
}
catch (Exception e){
Console.WriteLine(e.ToString());}
}
private static void ReceiveCallback(IAsyncResult ar)
{
try{
// Retrieve the state object and the client socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket client = state.workSocket;
// Read data from the remote device.
int bytesRead = client.EndReceive(ar);
if (bytesRead > 0){
// There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
// Get the rest of the data.
client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReceiveCallback), state);
}
else{
// All the data has arrived; put it in response.
if (state.sb.Length > 1)
{
response = state.sb.ToString();
}
// Signal that all bytes have been received.
receiveDone.Set();
}
}
catch (Exception e){
Console.WriteLine(e.ToString());}
}
private static void Send(Socket client, String data)
{
// Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.ASCII.GetBytes(data);
// Begin sending the data to the remote device.
client.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), client);
}
private static void SendCallback(IAsyncResult ar)
{
try{
// Retrieve the socket from the state object.
Socket client = (Socket)ar.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = client.EndSend(ar);
Console.WriteLine("Sent {0} bytes to server.", bytesSent);
// Signal that all bytes have been sent.
sendDone.Set();
}
catch (Exception e){
Console.WriteLine(e.ToString());}
}
public static int Main(String[] args)
{
StartClient();
return 0;
}
}
服務(wù)器端Server:
預(yù)定義結(jié)構(gòu)體,用于異步委托之間的傳遞。同客戶端的一致。不再贅述
正文:
// State object for reading client data asynchronously
public class AsynchronousSocketListener
{
// Thread signal.
public static ManualResetEvent allDone = new ManualResetEvent(false);
public AsynchronousSocketListener(){}
public static void StartListening()
{
// Data buffer for incoming data.
byte[] bytes = new Byte[1024];
// Establish the local endpoint for the socket.
// The DNS name of the computer
// running the listener is "host.contoso.com".
//IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
IPHostEntry ipHostInfo = Dns.Resolve("127.0.0.1");
IPAddress ipAddress = ipHostInfo.AddressList[0];
IPEndPoint localEndPoint = new IPEndPoint(ipAddress, 11000);
// Create a TCP/IP socket.
Socket listener = new Socket(AddressFamily.InterNetwork,SocketType.Stream, ProtocolType.Tcp);
// Bind the socket to the local endpoint and listen for incoming connections.
try{
listener.Bind(localEndPoint);
listener.Listen(100);
while (true){
// Set the event to nonsignaled state.
allDone.Reset();
// Start an asynchronous socket to listen for connections.
Console.WriteLine("Waiting for a connection...");
listener.BeginAccept(new AsyncCallback(AcceptCallback),listener);
// Wait until a connection is made before continuing.
allDone.WaitOne();
}
}
catch (Exception e){
Console.WriteLine(e.ToString());}
Console.WriteLine("\nPress ENTER to continue...");
Console.Read();
}
public static void AcceptCallback(IAsyncResult ar)
{
// Signal the main thread to continue.
allDone.Set();
// Get the socket that handles the client request.
Socket listener = (Socket)ar.AsyncState;
Socket handler = listener.EndAccept(ar);
// Create the state object.
StateObject state = new StateObject();
state.workSocket = handler;
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,new AsyncCallback(ReadCallback), state);
}
public static void ReadCallback(IAsyncResult ar)
{
String content = String.Empty;
// Retrieve the state object and the handler socket
// from the asynchronous state object.
StateObject state = (StateObject)ar.AsyncState;
Socket handler = state.workSocket;
// Read data from the client socket.
int bytesRead = handler.EndReceive(ar);
if (bytesRead > 0)
{
// There might be more data, so store the data received so far.
state.sb.Append(Encoding.ASCII.GetString(state.buffer, 0, bytesRead));
// Check for end-of-file tag. If it is not there, read
// more data.
content = state.sb.ToString();
if (content.IndexOf("<EOF>") > -1){
// All the data has been read from the
// client. Display it on the console.
Console.WriteLine("Read {0} bytes from socket. \n Data : {1}",
content.Length, content);
// Echo the data back to the client.
Send(handler, "Server return :" + content);
}
else{
// Not all data received. Get more.
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
new AsyncCallback(ReadCallback), state);
}
}
}
private static void Send(Socket handler, String data){
// Convert the string data to byte data using ASCII encoding.
byte[] byteData = Encoding.ASCII.GetBytes(data);
// Begin sending the data to the remote device.
handler.BeginSend(byteData, 0, byteData.Length, 0,
new AsyncCallback(SendCallback), handler);
}
private static void SendCallback(IAsyncResult ar)
{
try{
// Retrieve the socket from the state object.
Socket handler = (Socket)ar.AsyncState;
// Complete sending the data to the remote device.
int bytesSent = handler.EndSend(ar);
Console.WriteLine("Sent {0} bytes to client.", bytesSent);
handler.Shutdown(SocketShutdown.Both);
handler.Close();
}
catch (Exception e){
Console.WriteLine(e.ToString());
}
}
public static int Main(String[] args)
{
StartListening();
return 0;
}
}