這節課我們要建立用戶端,讓用戶端去連接伺服器端。
結合前一章的伺服器端(TcpListener)與本節的用戶端(TcpClient),一步步實作出一套 TCP 聊天工具。
觀念區分
- 伺服器端:TcpListener
綁定固定 IP + 固定連接埠(9500),持續被動監聽,等候用戶端連入,可以同時接納多個用戶端。就好比店家,守在原地等待顧客上門。 - 用戶端:TcpClient
不需要手動指定連接埠;作業系統會自動分配隨機的暫時連接埠,主動發起連線,去連接伺服器固定的 9500 連接埠。
通訊雙方的連接埠並不對等
用戶端隨機連接埠 — 伺服器固定 9500
主控台用戶端
using System;
using System.Net.Sockets;
namespace TalkClient
{
class Program
{
static void Main(string[] args)
{
// 建立 TCP 用戶端物件
TcpClient tcpClient = new TcpClient();
// 發起連線,連接本機 127.0.0.1:9500 的伺服器。若是其他電腦,請填入目標電腦 IP,伺服器與用戶端不一定要在同一台機器。
tcpClient.Connect("127.0.0.1", 9500);
// LocalEndPoint:本機用戶端位址與連接埠
// RemoteEndPoint:遠端伺服器位址與連接埠
Console.WriteLine("連線成功,本機連接埠 {0},伺服器連接埠{1}",
tcpClient.Client.LocalEndPoint,
tcpClient.Client.RemoteEndPoint);
Console.ReadLine();
tcpClient.Close();
}
}
}Code language: JavaScript (javascript)
WinForms 用戶端程式碼
介面控制項:
textBoxIP:伺服器 IPtextBoxPort:伺服器連接埠buttonStart:【連線】按鈕textBoxInfo:訊息顯示區textBoxMsg:待傳送訊息輸入欄buttonSend:傳送按鈕
using System;
using System.Net.Sockets;
using System.Windows.Forms;
namespace SocketChatClient
{
public partial class Form1 : Form
{
TcpClient tcpClient;
public Form1()
{
InitializeComponent();
}
// 連線按鈕點擊事件
private void buttonStart_Click(object sender, EventArgs e)
{
tcpClient = new TcpClient();
try
{
string ip = textBoxIP.Text;
int port = Convert.ToInt32(textBoxPort.Text);
tcpClient.Connect(ip, port);
textBoxInfo.AppendText($"連線成功!本機端點:{tcpClient.Client.LocalEndPoint}\r\n");
}
catch (Exception ex)
{
MessageBox.Show("連線失敗‑" + ex.Message);
}
}
}
}Code language: PHP (php)
伺服器端 + 用戶端配合流程
- 先啟動伺服器程式,點擊啟動,呼叫
TcpListener.Start()開始監聽 9500 連接埠 - 接著啟動用戶端程式,點擊連線,透過
TcpClient.Connect()完成 TCP 三次交握 - 連線建立完成後,雙向透過
NetworkStream收發訊息(下一單元說明)
注意事項
- 連線失敗常見原因
- 執行順序錯誤:沒有先開啟伺服器
- IP / 連接埠設定不一致
- 防火牆阻擋;跨電腦測試時不要用
127.0.0.1,改用區域網路 IP
- 連接埠相關知識
- 伺服器固定使用 9500 連接埠,可同時處理多個用戶端連線
- 用戶端連接埠由作業系統隨機指派,每次執行都會不一樣,無須手動設定
- 阻塞警告
表單程式直接呼叫.Connect()會卡住 UI 執行緒。正式專案建議改用非同步的ConnectAsync,避免視窗凍結。
回顧:伺服器端 WinForms(上一節)
using System;
using System.Net;
using System.Net.Sockets;
using System.Windows.Forms;
namespace SocketChatServer
{
public partial class Form1 : Form
{
IPAddress ip;
TcpListener listener;
public Form1()
{
InitializeComponent();
}
private void buttonStart_Click(object sender, EventArgs e)
{
ip = IPAddress.Parse(textBoxIP.Text);
int port = Convert.ToInt32(textBoxPort.Text);
listener = new TcpListener(ip, port);
listener.Start();
textBoxInfo.Text = $"伺服器已啟動 {DateTime.Now.ToShortTimeString()}\r\n" + textBoxInfo.Text;
}
}
}Code language: C# (cs)
用戶端