程序設計成為簡單的服務端和客戶端之間的通信, 但通過一些方法可以將這兩者進行統一起來, 讓服務端也成為客戶端, 讓客戶端也成為服務端, 使它們之間可以互相隨時不間斷的通信. 考慮到實現最原始的服務端和客戶端之間的通信所需要的步驟對于寫這樣的程序是很有幫助的.
作為服務端, 要聲明一個Socket A并綁定(Bind)某一個IP+這個IP指定的通信端口, 比如這個是127.0.0.1:9050, 然后開始監聽(Listen), Listen可以監聽來自多個IP傳過來的連接請求, 具體可以同時連接幾個客戶端, Listen方法中可以設定一個參數. 如果Listen到某一個客戶端發來連接請求了, 這時定義一個新的Socket B專門負責與這個客戶端的通信, Socket B = A.Accept(). 這時可以獲取這個客戶端的IP和端口, IPEndPoint C = (IPEndPoint)B.RemoteEndPoint, C.Address和C.Port分別表示客戶端C的IP地址和端口. 這時通過B.Send()方法就可以給C發送消息了, B.Receive()可以接收客戶端C發來的信息.
作為客戶端, 也需要聲明一個Socket D并綁定某一個IP+本機一個未被占用的端口, 定義IPEndPoint E表示要進行連接的服務端Socket, 要指明E的IP和端口, 這樣才可以進行端口對端口之間的通信, 接下來就可以嘗試D.Connect(E), 連接成功之后就可以發送和接收數據了, D.Send(), D.Receive.
發送消息時, 數據都是以字節或字節數組為單位進行傳輸的, 比如我客戶端D要發送"Hello World"則要這樣寫: D.Send(Encoding.ASCII.GetBytes("Hello World")). 接受消息時, 也是以字節或字節數組, 比如服務端要接受D剛才發送的Hello World, 可以這樣寫: Byte[] data = new Byte[1024]; int receivedDataLength = B.Receive(data); string stringdata = Encoding.ASCII.GetString(data, 0, receivedDataLength); stringdata這時就是Hello World.
上面只是大概的闡述了服務端與客戶端之間的通信過程, 在網上找到了具體的代碼例子, 也貼過來參考參考. 這個例子沒有將服務端與客戶端統一起來, 他是分別寫服務端和客戶端的.
服務端代碼
using System;using System;using System.Net;using System.Net.Sockets;using System.Text;namespace tcpserver{ /// <summary> /// Class1 的摘要說明。 /// </summary> class server { /// <summary> /// 應用程序的主入口點。 /// </summary> [STAThread] static void Main( string [] args) { // // TODO: 在此處添加代碼以啟動應用程序 // int recv; // 用于表示客戶端發送的信息長度 byte [] data;// = new byte [ 1024 ]; // 用于緩存客戶端所發送的信息,通過socket傳遞的信息必須為字節數組 IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 9050 ); // 本機預使用的IP和端口 Socket newsock = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp); newsock.Bind(ipep); // 綁定 newsock.Listen( 10 ); // 監聽 Console.WriteLine( " waiting for a client " ); Socket client = newsock.Accept(); //當有可用的客戶端連接嘗試時執行,并返回一個新的socket,用于與客戶端之間的通信 IPEndPoint clientip = (IPEndPoint)client.RemoteEndPoint; Console.WriteLine( " connect with client: " + clientip.Address + " at port: " + clientip.Port); string welcome = " welcome here! " ; data = Encoding.ASCII.GetBytes(welcome); client.Send(data,data.Length,SocketFlags.None); // 發送信息 while ( true ) { // 用死循環來不斷的從客戶端獲取信息 data = new byte [ 1024 ]; recv = client.Receive(data); Console.WriteLine( " recv= " + recv); if (recv == 0 ) // 當信息長度為0,說明客戶端連接斷開 break ; Console.WriteLine(Encoding.ASCII.GetString(data, 0 ,recv)); client.Send(data,recv,SocketFlags.None); } Console.WriteLine( " Disconnected from " + clientip.Address); client.Close(); newsock.Close(); } }}
客戶端代碼
using System;using System.Net;using System.Net.Sockets;using System.Text;namespace tcpclient{ /// <summary> /// Class1 的摘要說明。 /// </summary> class client { /// <summary> /// 應用程序的主入口點。 /// </summary> [STAThread] static void Main(string[] args) { // // TODO: 在此處添加代碼以啟動應用程序 // byte[] data = new byte[1024]; Socket newclient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); newclient.Bind(new IPEndPoint(IPAddress.Any, 905)); Console.Write(" please input the server ip: "); string ipadd = Console.ReadLine(); Console.WriteLine(); Console.Write(" please input the server port: "); int port = Convert.ToInt32(Console.ReadLine()); IPEndPoint ie = new IPEndPoint(IPAddress.Parse(ipadd), port); // 服務器的IP和端口 try { // 因為客戶端只是用來向特定的服務器發送信息,所以不需要綁定本機的IP和端口。不需要監聽。 newclient.Connect(ie); } catch (SocketException e) { Console.WriteLine(" unable to connect to server "); Console.WriteLine(e.ToString()); return; } int receivedDataLength = newclient.Receive(data); string stringdata = Encoding.ASCII.GetString(data, 0, receivedDataLength); Console.WriteLine(stringdata); while (true) { string input = Console.ReadLine(); if (input == " exit ") break; newclient.Send(Encoding.ASCII.GetBytes(input)); data = new byte[1024]; receivedDataLength = newclient.Receive(data); stringdata = Encoding.ASCII.GetString(data, 0, receivedDataLength); Console.WriteLine(stringdata); } Console.WriteLine(" disconnect from sercer "); newclient.Shutdown(SocketShutdown.Both); newclient.Close(); } }}
上面的服務端和客戶端都是控制臺應用程序, 想辦法做一個窗體類型的, 思路就是另起一個線程, 這個線程專門負責兩端建立連接. 如果不采用另起線程的方法, 當等待連接而沒有連接上, 或者主動連接, 服務端還沒有相應時, 程序就會出現沒有響應的假死狀態.
當這個線程將兩個端口連接成功后, 就讓程序進入一個死循環, 這個死循環負責不斷的接收是否有消息傳來, 傳來的話就在txtGetMsg中顯示出來:
while (true) // 用死循環來不斷的獲取信息{ data = new byte[1024]; recv = newclient.Receive(data); uiContext.Send(new SendOrPostCallback( state => { int txtGetMsgLength = txtGetMsg.Text.Length; string recMsg = "Friend: " + System.DateTime.Now.ToString() + "/n " +Encoding.Unicode.GetString(data, 0, recv) + "/n"; txtGetMsg.AppendText(recMsg); txtGetMsg.Select(txtGetMsgLength, recMsg.Length - Encoding.Unicode.GetString(data, 0, recv).Length - 1); txtGetMsg.SelectionColor = Color.Red; }), null);}
如果按下發送消息的按鈕, 則發送txtSendMsg中的文本, 我寫的是用Unicode編碼, 所以可以發送中文字符.
private void btnSendMsg_Click(object sender, EventArgs e){ string input = txtSendMsg.Text; if (input == "") { MessageBox.Show("消息不能為空!", "發送消息出錯"); txtSendMsg.Focus(); } else { if (meIsClient) { newclient.Send(Encoding.Unicode.GetBytes(input)); string showText = "Me: " + System.DateTime.Now.ToString() + "/n " + input + "/n"; int txtGetMsgLength = txtGetMsg.Text.Length; txtGetMsg.AppendText(showText); txtGetMsg.Select(txtGetMsgLength, showText.Length - 1 - input.Length); txtGetMsg.SelectionColor = Color.Blue; txtSendMsg.Text = ""; } else { client.Send(Encoding.Unicode.GetBytes(input)); string showText = "Me " + System.DateTime.Now.ToString() + "/n " + input + "/n"; int txtGetMsgLength = txtGetMsg.Text.Length; txtGetMsg.AppendText(showText); txtGetMsg.Select(txtGetMsgLength, showText.Length - 1 - input.Length); txtGetMsg.SelectionColor = Color.Blue; txtSendMsg.Text = ""; } }}
程序的運行效果:
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。
新聞熱點
疑難解答