1.目的:實(shí)現(xiàn)客戶端向服務(wù)器發(fā)送數(shù)據(jù)
? 原理:
2.建立兩個控制臺應(yīng)用,一個為服務(wù)器,用于接收數(shù)據(jù)。一個為客戶端,用于發(fā)送數(shù)據(jù)。
? 關(guān)鍵類與對應(yīng)方法:
? 1)類IPEndPoint:
? 1.是抽象類EndPoint的實(shí)現(xiàn)類
? 2.Socket對象的RemoteEndPoint、 LocalEndPoint都是這個類型
? 3.屬性Address: 使用IPv4表示的地址
? 4.屬性Port:使用int表示的端口
? 2)類Socket:
? 這個類即可以用于作服務(wù)器端的開發(fā),又可以作客戶端的開發(fā)
? 構(gòu)造方法:
? 參數(shù) AddressFamily:指定使用IPv4的地址InterNetwork
? 參數(shù)SocketType:指定使用流式傳輸Stream
? 參數(shù)ProtocolType:指定協(xié)議類型Tcp
? 1.方法Bind()E 綁定IP與端口,這樣就成為了服務(wù)器,可以監(jiān)聽指定IP的特定端口
? 2.方法Listen(); 置于監(jiān)聽狀態(tài),參數(shù)是最大的掛起數(shù)
? 3.方法Accept(): 接收客戶端連接,返回Socket對象, 這個方法會阻塞當(dāng)前線程,建議開啟新線程執(zhí)行些方法,結(jié)合尾遞歸,這樣就可以接收多個客戶端
? 4.方法Receive():
接收客戶端發(fā)送過來的消息,以字節(jié)為單位進(jìn)行操作,此方法會阻塞當(dāng)前線程,建議開啟新線程執(zhí)行此方法,結(jié)合尾遞歸,就可以持續(xù)接收多條信息
? 5. 方法Send(): 發(fā)送消息,以字節(jié)為單位
3.具體實(shí)現(xiàn)
? ? ?其他內(nèi)容不做過多解釋了,備注做的超詳細(xì),應(yīng)該只有笨笨的人才寫這么多備注吧。。
? 1.服務(wù)器
? ? ?主函數(shù):
using System; using System.Collections.Generic; using System.Linq; using
System.Text;using System.Threading.Tasks; namespace ServerTest { class Program {
static void Main(string[] args) { // 調(diào)用構(gòu)造函數(shù),使用Start方法 ServerControl server = new
ServerControl(); server.Start(); Console.ReadKey(); } } }
? ? ?ServerControl類:
using System; using System.Collections.Generic; using System.Linq; using
System.Net;using System.Net.Sockets; using System.Text; using System.Threading;
using System.Threading.Tasks; namespace ServerTest { public class ServerControl
{// 聲明變量(使用Socket需using System.Net.Sockets;) private Socket serverSocket; //
自定義有參構(gòu)造方法(IP地址,流程傳輸方式,TCP協(xié)議) public ServerControl() { serverSocket = new
Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); }//
創(chuàng)建啟動方法(IPEndPoint用于指定地址及端口初始化,需using System.Net;) public void Start() { // 服務(wù)器啟動
// 綁定IP地址(為任意IP)與端口(設(shè)置為12345) serverSocket.Bind(new IPEndPoint(IPAddress.Any,
12345)); serverSocket.Listen(10); Console.WriteLine("服務(wù)器啟動成功"); //
開啟線程:目的實(shí)現(xiàn)服務(wù)器和客戶端一對多連接 Thread threadAccept = new Thread(Accept);
threadAccept.IsBackground= true; threadAccept.Start(); } // Accept方法測試:接收客戶端連接
private void Accept() { // 接收客戶端方法,會掛起當(dāng)前線程(.RemoteEndPoint表示遠(yuǎn)程地址) Socket client
= serverSocket.Accept(); IPEndPoint point = client.RemoteEndPoint as
IPEndPoint; Console.WriteLine(point.Address+ "[" + point.Port + "] 連接成功!"); //
開啟一個新線程線程,實(shí)現(xiàn)消息多次接收 Thread threadReceive = new Thread(Receive);
threadReceive.IsBackground= true; threadReceive.Start(client); // 尾遞歸
Accept(); }// Receive方法的使用測試 // 接收客戶端發(fā)送過來的消息,以字節(jié)為單位進(jìn)行操作 //
該方法會阻塞當(dāng)前線程,所以適合開啟新的線程使用該方法//
Accept()中將Receive作為線程傳遞對象,所以要注意一點(diǎn),使用線程傳遞對象只能是object類型的??! private void Receive(
object obj) { // 將object類型強(qiáng)行轉(zhuǎn)換成socket Socket client = obj as Socket; IPEndPoint
point= client.RemoteEndPoint as IPEndPoint; // 此處的異常拋出主要針對客戶端異常的問題 //
比如,客戶端關(guān)閉或者連接中斷// 程序會停留在int msgLen = client.Receive(msg);這段代碼,而導(dǎo)致無法繼續(xù)往下走 try {
byte[] msg = new byte[1024]; //
實(shí)際接收到字節(jié)數(shù)組長度,該方法會阻塞當(dāng)前線程,即(client.Receive(msg)開始掛起)// 同時,這里還是尾遞歸掛起處 int msgLen =
client.Receive(msg);// 將msg裝換成字符串 Console.WriteLine(point.Address + "[" +
point.Port +"]:" + Encoding.UTF8.GetString(msg, 0, msgLen)); //
此處實(shí)現(xiàn)服務(wù)器自動向客戶端返回一條消息// 因?yàn)镾end發(fā)送信息是以字節(jié)為單位發(fā)送的 //
所以下面(Encoding.UTF8.GetString(msg,0,msgLen)+" yes.boy")這一塊是把這一部分均搞成string//
后使用Encoding.UTF8.GetBytes統(tǒng)一轉(zhuǎn)化成字節(jié)傳遞//
這里呢,已經(jīng)實(shí)現(xiàn)服務(wù)器向客戶端發(fā)送消息了,客戶端只需要receive一下,格式一轉(zhuǎn)就可視化了
client.Send(Encoding.UTF8.GetBytes(Encoding.UTF8.GetString(msg,0,msgLen)+"
yes.boy")); // 尾遞歸實(shí)現(xiàn)多條消息的接收;和while同理。 Receive(client); } catch {
Console.WriteLine(point.Address+ "[" + point.Port + "]積極斷開"); } } } }
2.客戶端:
? ?主函數(shù):
client.Connect("127.0.0.1",12345);
修改IP可實(shí)現(xiàn)不同計(jì)算機(jī)之間的連接。 using System; using System.Collections.Generic; using
System.Linq;using System.Text; using System.Threading.Tasks; namespace
ClientTest {class Program { static void Main(string[] args) { // 調(diào)用構(gòu)造函數(shù)
ClientControl client =new ClientControl(); // 輸入本機(jī)IP與端口號 client.Connect("
127.0.0.1",12345); // 提示操作方法 Console.WriteLine("請輸入發(fā)送至服務(wù)器的內(nèi)容或者輸入quit退出"); //
輸入內(nèi)容 string msg = Console.ReadLine(); // 非退出情況下操作方式,使用while可以持續(xù)不斷的接收用戶輸入 while
(msg !="quit") { client.Send(msg); msg = Console.ReadLine(); }
Console.ReadKey(); } } }
? ?ClientControl類:
using System; using System.Collections.Generic; using System.Linq; using
System.Net.Sockets;using System.Text; using System.Threading; using
System.Threading.Tasks;namespace ClientTest { public class ClientControl { //
聲明變量 private Socket clientSocket; // 自定義有參構(gòu)造方法((IP地址,流程傳輸方式,TCP協(xié)議)) public
ClientControl() { clientSocket= new
Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp); }//
創(chuàng)建通過IP與端口號連接的方法 public void Connect(string ip,int port) {
clientSocket.Connect(ip, port); Console.WriteLine("連接服務(wù)器成功"); // 客戶端接收服務(wù)器消息的線程
Thread threadReceive =new Thread(Receive); threadReceive.IsBackground = true;
threadReceive.Start(); }// 用于測試服務(wù)器向客戶端返回一條消息 private void Receive() { while(true
) {try { // 用于接收服務(wù)器的回復(fù)信息 byte[] msg = new byte[1024]; int msgLen =
clientSocket.Receive(msg); Console.WriteLine("服務(wù)器:"+Encoding.UTF8.GetString(msg,
0,msgLen)); } // 異常處理方法 catch { Console.WriteLine("服務(wù)器積極拒絕!!"); // 退出while循環(huán)
break; } } } // Send方法測試:即發(fā)送消息,以字節(jié)為單位 public void Send(string msg) { //
將字符創(chuàng)傳化為字節(jié)數(shù)組 clientSocket.Send(Encoding.UTF8.GetBytes(msg)); } } }
4.實(shí)現(xiàn)
本地連接:IP為127.0.0.1
遠(yuǎn)程連接:因?yàn)槲抑挥幸慌_電腦,所以用騰訊云服務(wù)器作為我的服務(wù)器,我本地的PC作為客戶端,實(shí)現(xiàn)連接。
1.修改客戶端主程序里面的IP為我的騰訊云IP
2.使用遠(yuǎn)程桌面連接
3.連接成功
熱門工具 換一換