Socket簡介 |
TCP/IP協議 |
既然是TCP/IP協議族隱藏在Socket接口後面,那麼,什麼是TCP/IP協議呢?
- TCP/IP(Transmission Control Protocol/Internet Protocol)即傳輸控制協議/網間協議,不是一個協議,而是包含衆多協議的協議集,它是爲廣域網(WANS)設計的。
- UDP(User Data Protocol,用戶數據報協議)是與TCP相對應的協議。它是屬於TCP/IP協議族中的一種。與TCP協議位於同一層,主要作用:爲了在給定的主機上能識別多個目的地址,同時允許多個應用程序在同一臺主機上工作並能獨立地進行數據包的發送和接收,設計用戶數據報協議UDP。UDP使用底層的互聯網協議來傳送報文,同IP一樣提供不可靠的無連接數據包傳輸服務。它不提供報文到達確認、排序、及流量控制等功能。
Socket原理 |
當A給B打電話,A先撥號,接通後進行對話,對話結束,掛掉電話!這也是Socket的實現原理。A撥號就相當於輸入IP找到對方主機,在通過端口號找到應用程序,找到主機也找到也找到端口號就可以實現通信了。(見下圖)
具體實現 |
//命名空間
using System.Net;
using System.Net.Sockets;
//服務端實現
public partial class frmRecive : Form
{
public frmRecive()
{
InitializeComponent();
Control.CheckForIllegalCrossThreadCalls = false;
}
IPAddress IP;//接收IP地址變量
TcpListener listener;//建立監聽器對象,負責監聽客戶端是否發來連接請求
TcpClient tcpClient;//主要通過帶入主機地址,然後調用Connect進行和服務器點對點的連接,連接成功後通過GetStream方法返回NetworkStream對象
public void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e)//BackgroundWorker控件是用來執行多線程任務的控件,如果想在後臺執行一些耗時的操作,監聽操作進度並在操作完成時發出信號可以使用BackgroundWorker控件。
{
IP = IPAddress.Parse(this.txtIP.Text);//獲取自定義ip地址
listener = new TcpListener(IP, Convert.ToInt32(this.txtPort.Text));//獲取自定義端口號
listener.Start();//開始監聽
this.txtBoxInfo.Text = "服務器啓動-" + DateTime.Now.ToLongTimeString() + "\r\n" + this.txtBoxInfo.Text;//提示文字
tcpClient = listener.AcceptTcpClient(); //負責接收客戶端發來的連接請求操作
this.txtBoxInfo.Text = "連接成功-" + DateTime.Now.ToLongTimeString() + "\r\n" + this.txtBoxInfo.Text;//提示連接成功
NetworkStream stream = tcpClient.GetStream();//客戶端接收傳輸的流
byte[] byteArray = new byte[1024];//實例化字節數組,用來存放傳輸的字節,長度爲1024
while (true)
{
int length = stream.Read(byteArray, 0, 1024);//讀取流裏面的字節數組,並放到byteArray裏面,length就是客戶端發送的字節數組長度
string receiveMessage = Encoding.Unicode.GetString(byteArray, 0, length);//將從客戶端接收到的字節數組轉化爲字符串
this.txtBoxInfo.Text = "接收到:" + receiveMessage + "-" + DateTime.Now.ToLongTimeString() + "\r\n" + this.txtBoxInfo.Text;
}//提示接收信息
}
public void BtnSend_Click(object sender, EventArgs e)//能實現服務器端向顧客端發送消息
{
if (txtSend.Text!="")
{
string message = this.txtSend.Text;
this.txtBoxInfo.Text = "發送“" + message + "”-" + DateTime.Now.ToLongTimeString() + "\r\n" + this.txtBoxInfo.Text;
NetworkStream stream = tcpClient.GetStream();//客戶端接收傳輸的流
byte[] byteArray = Encoding.Unicode.GetBytes(message);//將服務器端發送的消息轉爲字節數組
stream.Write(byteArray, 0, byteArray.Length);//發送了字節數組
txtSend.Text = "";
}
}
public void FrmRecive_Load(object sender, EventArgs e)
{
if (frmCallOper.message!="")
{
MessageBox.Show("有顧客呼叫您,請及時查看!");
}
this.backgroundWorker1.RunWorkerAsync();//讓backgroundworker開始執行後臺操作
}
}
//客戶端實現
public partial class frmCallOper : DockContent
{
public frmCallOper()
{
InitializeComponent();
Control.CheckForIllegalCrossThreadCalls = false;
}
private void FrmCallOper_Load(object sender, EventArgs e)
{
txtBoxInput.Focus();
}
TcpClient tcpClient;//建立客戶端變量
public static string message;//靜態變量,用於存放消息內容
private void BtnSend_Click(object sender, EventArgs e)
{
if (txtBoxInput.Text!="")
{
message = this.txtBoxInput.Text;
this.txtBoxInfo.Text = "發送“" + message + "”-" + DateTime.Now.ToLongTimeString() + "\r\n" + this.txtBoxInfo.Text;
NetworkStream stream = tcpClient.GetStream();//客戶端接收傳輸的流
byte[] byteArray = Encoding.Unicode.GetBytes(message);//將發送的消息轉化爲字節數組
stream.Write(byteArray, 0, byteArray.Length);//發送了字節數組
txtBoxInput.Text = "";
}
}
private void PicCall_Click(object sender, EventArgs e)
{
tcpClient = new TcpClient();//實例化了客戶端
try
{
tcpClient.Connect("192.168.96.98", 7890);//設立連接的ip與端口號
this.txtBoxInfo.Text = "呼叫成功-" + DateTime.Now.ToLongTimeString() + "\r\n" + this.txtBoxInfo.Text;
this.backgroundWorker1.RunWorkerAsync();//讓backgroundworker開始執行操作
}
catch (Exception ex)
{
MessageBox.Show("呼叫失敗-" + ex.Message);
}
}
private void PicCall_MouseEnter(object sender, EventArgs e)
{
toolTip1.SetToolTip(picCall, "呼叫網管");
}
private void TxtBoxInput_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)//回車鍵觸發發送按鈕
{
this.BtnSend_Click(sender, e);
}
}
private void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
NetworkStream stream = tcpClient.GetStream();//用來接收網絡中傳輸的流
byte[] byteArray = new byte[1024];//實例化字節數組
while (true)
{
int length = stream.Read(byteArray, 0, 1024);//會把流裏面的字節數組 放到byteArray裏面,length就是客戶端發送的字節數組長度
string receiveMessage = Encoding.Unicode.GetString(byteArray, 0, length);//將字節數組轉化爲字符串格式
this.txtBoxInfo.Text = "接收到:" + receiveMessage + "-" + DateTime.Now.ToLongTimeString() + "\r\n" + this.txtBoxInfo.Text;
}
}
}
解析 |
-
TcpListener類
此類也是微軟基於Tcp封裝類,用於監聽服務端或客戶端的連接請求,一旦有連接請求信息,立刻交給TcpClient的AcceptTcpClient方法捕獲,Start方法用於開始監聽。
-
TcpClient類
此類是微軟基於Tcp封裝類,用於簡化Tcp客戶端的開發,主要通過構造帶入主機地址或IPEndPonint對象,然後調用Connect進行和服務器點對點的連接,連接成功後通過GetStream方法返NetworkStream對象。
-
NetworkStream類
NetworkStream:顧名思義,在網絡中傳輸的流,那既然是在網絡中傳輸的,一定有協議去約束它,也就是TCP/IP協議。如果服務器和客戶端是基於TCP連接的 ,他們之間能夠依靠穩定的字節相互傳輸信息,這就是NetworkStream的作用。
Tips:
- NetworkStream只能用在具有Tcp/Ip協議之中,如果用在UDP中編譯不報錯,會報異常。
- NetworkStream是面向連接的
- 在網絡中利用流的形式傳遞信息
- 必須藉助Socket,或使用一些返回的返回值,例如TcpClient的GetStream方法
- 用法和普通流方法一樣,但具有特殊性
TCP/UDP協議區別 |
- TCP:
1 TCP是面向連接的通信協議,通過三次握手建立連接
2 TCP提供的是一種可靠的數據流服務,採用“帶重傳的肯定確認”技術來實現傳輸的可靠性
- UDP:
1 UDP是面向無連接的通訊協議,UDP數據包括目的端口號和源端口號信息,由於通訊不需要連接,所以可以實現廣播發送
2 UDP通訊時不需要接收方確認,屬於不可靠的傳輸,可能會出丟包現象,實際應用中要求在程序員編程驗證
3 由於上述2點的關係,UDP傳輸速度更快,但是安全性比較差,很容易發生未知的錯誤,所以NetworkStream無法使用在UDP的功能上