一、套接字(socket)概念
套接字(socket)是通信的基石,是支持TCP/IP協議的網絡通信的基本操作單元。它是網絡通信過程中端點的抽象表示,包含進行網絡通信必須的五種信息:連接使用的協議,本地主機的IP地址,本地進程的協議端口,遠地主機的IP地址,遠地進程的協議端口。
應用層通過傳輸層進行數據通信時,TCP會遇到同時為多個應用程序進程提供並發服務的問題。多個TCP連接或多個應用程序進程可能需要通過同一個 TCP協議端口傳輸數據。為了區別不同的應用程序進程和連接,許多計算機操作系統為應用程序與TCP/IP協議交互提供了套接字(Socket)接口。應 用層可以和傳輸層通過Socket接口,區分來自不同應用程序進程或網絡連接的通信,實現數據傳輸的並發服務。
二、建立socket連接
建立Socket連接至少需要一對套接字,其中一個運行於客戶端,稱為ClientSocket ,另一個運行於服務器端,稱為ServerSocket 。
套接字之間的連接過程分為三個步驟:服務器監聽,客戶端請求,連接確認。
- 服務器監聽:服務器端套接字並不定位具體的客戶端套接字,而是處於等待連接的狀態,實時監控網絡狀態,等待客戶端的連接請求
- 客戶端請求:指客戶端的套接字提出連接請求,要連接的目標是服務器端的套接字。為此,客戶端的套接字必須首先描述它要連接的服務器的套接字,指出服務器端套接字的地址和端口號,然后就向服務器端套接字提出連接請求。
- 連接確認:當服務器端套接字監聽到或者說接收到客戶端套接字的連接請求時,就響應客戶端套接字的請求,建立一個新的線程,把服務器端套接字的描述發給客戶 端,一旦客戶端確認了此描述,雙方就正式建立連接。而服務器端套接字繼續處於監聽狀態,繼續接收其他客戶端套接字的連接請求。
三、服務端與客戶端代碼
1.服務端前台XMAL
<Window x:Class="WPFAPP.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WPFAPP" mc:Ignorable="d" Title="服務端" Height="350" Width="525" Loaded="Window_Loaded" > <Grid> <Grid.RowDefinitions> <RowDefinition Height="50*"/> <RowDefinition Height="190*"/> <RowDefinition Height="81*"/> </Grid.RowDefinitions> <Grid Grid.Row="0"> <WrapPanel VerticalAlignment="Center"> <Label Padding="5">IP:</Label> <TextBox Padding="3" Width="150" Name="IP"></TextBox> <Label Padding="5">Port:</Label> <TextBox Padding="3" Width="100" Name="Port"></TextBox> <Button Padding="3" Margin="3" Click="Start_View_Click">開始監聽</Button> <Button Padding="3" Margin="3" Click="Stop_Click">停止監聽</Button> </WrapPanel> </Grid> <Grid Grid.Row="1"> <ListBox Name="ListConnet"></ListBox> </Grid> <Grid Grid.Row="2"> <Grid.ColumnDefinitions> <ColumnDefinition Width="405*"/> <ColumnDefinition Width="113*"/> </Grid.ColumnDefinitions> <TextBox Name="SentConnet"></TextBox> <Button Grid.Column="1" Click="Send_Click">發送</Button> </Grid> </Grid> </Window>
2.服務端后台代碼
using System; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Windows; namespace WPFAPP { /// <summary> /// MainWindow.xaml 的交互邏輯 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } Socket socketSend; private void Start_View_Click(object sender, RoutedEventArgs e) { try { //點擊開始監聽時 在服務端創建一個負責監聽IP和端口號的Socket Socket socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPAddress ip = IPAddress.Any; //創建對象端口 IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(Port.Text)); socketWatch.Bind(point);//綁定端口號 ShowMsg("監聽成功!"); socketWatch.Listen(10);//設置監聽 //創建監聽線程 Thread thread = new Thread(Listen); thread.IsBackground = true; thread.Start(socketWatch); } catch { } } void Listen(object o) { try { Socket socketWatch = o as Socket; while (true) { socketSend = socketWatch.Accept();//等待接收客戶端連接 ShowMsg(socketSend.RemoteEndPoint.ToString() + ":" + "連接成功!"); //開啟一個新線程,執行接收消息方法 Thread r_thread = new Thread(Received); r_thread.IsBackground = true; r_thread.Start(socketSend); } } catch { } } /// <summary> /// 服務器端不停的接收客戶端發來的消息 /// </summary> /// <param name="o"></param> void Received(object o) { try { Socket socketSend = o as Socket; while (true) { //客戶端連接服務器成功后,服務器接收客戶端發送的消息 byte[] buffer = new byte[1024 * 1024 * 3]; //實際接收到的有效字節數 int len = socketSend.Receive(buffer); if (len == 0) { break; } string str = Encoding.UTF8.GetString(buffer, 0, len); ShowMsg("接收到的客戶端數據:" + socketSend.RemoteEndPoint + ":" + str); } } catch { } } /// <summary> /// 服務器向客戶端發送消息 /// </summary> /// <param name="str"></param> void Send(string str) { byte[] buffer = Encoding.UTF8.GetBytes(str); socketSend.Send(buffer); } void ShowMsg(string msg) { Dispatcher.Invoke(() => { ListConnet.Items.Add(msg + "\r\n"); }); } private void Send_Click(object sender, RoutedEventArgs e) { Send(SentConnet.Text.Trim()); } private void Window_Loaded(object sender, RoutedEventArgs e) { } private void Stop_Click(object sender, RoutedEventArgs e) { socketSend.Close(); ShowMsg("已經停止監聽!"); } } }
3.客戶端前台XMAL
<Window x:Class="WPFClient.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WPFClient" mc:Ignorable="d" Title="客戶端" Height="350" Width="525"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="50*"/> <RowDefinition Height="50*"/> <RowDefinition Height="200 *"/> </Grid.RowDefinitions> <Grid Grid.Row="0"> <WrapPanel VerticalAlignment="Center"> <Label Padding="5">IP:</Label> <TextBox Padding="3" Width="150" Name="IP"></TextBox> <Label Padding="5">Port:</Label> <TextBox Padding="3" Width="100" Name="Port"></TextBox> <Button Padding="3" Margin="3" Click="Start_View_Click">連接</Button> <Button Padding="3" Margin="3" Click="Stop_Click">斷開連接</Button> </WrapPanel> </Grid> <Grid Grid.Row="1"> <TextBox Name="ListConnet"></TextBox> </Grid> <Grid Grid.Row="2"> <Grid.ColumnDefinitions> <ColumnDefinition Width="405*"/> <ColumnDefinition Width="113*"/> </Grid.ColumnDefinitions> <TextBox Name="SentConnet"></TextBox> <Button Grid.Column="1" Click="Send_Click">發送</Button> </Grid> </Grid> </Window>
4.客戶端后台代碼
using System; using System.Net; using System.Net.Sockets; using System.Text; using System.Threading; using System.Windows; namespace WPFClient { /// <summary> /// MainWindow.xaml 的交互邏輯 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); this.IP.Text = "127.0.0.1"; this.Port.Text = "5000"; this.SentConnet.Text= @"發送消息"; } Socket socketSend; private void Start_View_Click(object sender, RoutedEventArgs e) { try { //創建客戶端Socket,獲得遠程ip和端口號 socketSend = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); IPAddress ip = IPAddress.Parse(IP.Text); IPEndPoint point = new IPEndPoint(ip, Convert.ToInt32(Port.Text)); socketSend.Connect(point); ShowMsg("連接成功!"); //開啟新的線程,不停的接收服務器發來的消息 Thread c_thread = new Thread(Received); c_thread.IsBackground = true; c_thread.Start(); } catch (Exception) { ShowMsg("IP或者端口號錯誤..."); } } void ShowMsg(string str) { Dispatcher.Invoke(() => { ListConnet.AppendText(str + "\r\n"); }); } /// <summary> /// 接收服務端返回的消息 /// </summary> void Received() { while (true) { try { byte[] buffer = new byte[1024 * 1024 * 3]; //實際接收到的有效字節數 int len = socketSend.Receive(buffer); if (len == 0) { continue; } string str = Encoding.UTF8.GetString(buffer, 0, len); ShowMsg("接收到的服務端數據:" + socketSend.RemoteEndPoint + ":" + str); } catch { } } } private void Send_Click(object sender, RoutedEventArgs e) { try { string msg = SentConnet.Text.Trim(); byte[] buffer = new byte[1024 * 1024 * 3]; buffer = Encoding.UTF8.GetBytes(msg); socketSend.Send(buffer); } catch { } } private void Stop_Click(object sender, RoutedEventArgs e) { socketSend.Close(); ShowMsg("連接已經斷開!"); } } }