Winform窗體實現服務端和客戶端通信的例子
進行了一些異常處理,提示信息的補充,還有新增獲取本地IP的方法
1、通信原理
1)服務端與客戶端
啟動服務端后,服務端通過持續監聽客戶端發來的請求,一旦監聽到客戶端傳來的信息(請求),兩端便可以互發信息了.
服務端需要綁定一個IP,用於客戶端在網絡中尋找並建立連接(支持局域網內部客戶端與服務端之間的互相通信)
2)信息發送原理
將手動輸入字符串信息轉換成機器可以識別的字節數組,然后調用套接字的Send()方法將字節數組發送出去
3)信息接收原理
調用套接字的Receive()方法,獲取對端傳來的字節數組,然后將其轉換成人可以讀懂的字符串信息
2、界面設計
1)服務端
文本框類
IP地址, name:txtIP ; 本地端口,name:txtPort;
聊天信息,name:txtMsg;發送消息:txtSendMsg
按鈕類
獲取IP, name :btnGetLocalIP, 獲取本地的IP的方法
啟動服務,name:btnServerConn, 支持服務端與客戶端通信的前提
發送消息,name:btnSendMsg ,發送消息到客戶端的方法

2)客戶端
文本框類
IP地址, name:txtIP ; 本地端口,name:txtPort;
聊天信息,name:txtMsg;發送消息:txtClientSendMsg
按鈕類
連接服務端,name:btnListenServer, 客戶端連接服務端的方法
發送消息,name:btnSendMsg ,發送消息到服務端的方法

3、源碼例子
1)服務端代碼
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace TcpMsgServer
{
public partial class FrmServer : Form
{
public FrmServer()
{
InitializeComponent();
//關閉對文本框的非法線程操作檢查
TextBox.CheckForIllegalCrossThreadCalls = false;
}
Thread threadWatch = null; //負責監聽客戶端的線程
Socket socketWatch = null; //負責監聽客戶端的套接字
/// <summary>
/// 啟動服務
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnServerConn_Click(object sender, EventArgs e)
{
try
{
//定義一個套接字用於監聽客戶端發來的信息 包含3個參數(IP4尋址協議,流式連接,TCP協議)
socketWatch = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//服務端發送信息 需要1個IP地址和端口號
IPAddress ipaddress = IPAddress.Parse(this.txtIP.Text.Trim()); //獲取文本框輸入的IP地址
//將IP地址和端口號綁定到網絡節點endpoint上
IPEndPoint endpoint = new IPEndPoint(ipaddress, int.Parse(this.txtPort.Text.Trim())); //獲取文本框上輸入的端口號
//監聽綁定的網絡節點
socketWatch.Bind(endpoint);
//將套接字的監聽隊列長度限制為20
socketWatch.Listen(20);
//創建一個監聽線程
threadWatch = new Thread(WatchConnecting);
//將窗體線程設置為與后台同步
threadWatch.IsBackground = true;
//啟動線程
threadWatch.Start();
//啟動線程后 txtMsg文本框顯示相應提示
txtMsg.AppendText("開始監聽客戶端傳來的信息!" + "\r\n");
this.btnServerConn.Enabled = false;
}
catch (Exception ex) {
txtMsg.AppendText("服務端啟動服務失敗!" + "\r\n");
this.btnServerConn.Enabled = true;
}
}
//創建一個負責和客戶端通信的套接字
Socket socConnection = null;
/// <summary>
/// 監聽客戶端發來的請求
/// </summary>
private void WatchConnecting()
{
while (true) //持續不斷監聽客戶端發來的請求
{
socConnection = socketWatch.Accept();
txtMsg.AppendText("客戶端連接成功! " + "\r\n");
//創建一個通信線程
ParameterizedThreadStart pts = new ParameterizedThreadStart(ServerRecMsg);
Thread thr = new Thread(pts);
thr.IsBackground = true;
//啟動線程
thr.Start(socConnection);
}
}
/// <summary>
/// 發送信息到客戶端的方法
/// </summary>
/// <param name="sendMsg">發送的字符串信息</param>
private void ServerSendMsg(string sendMsg)
{
try
{
//將輸入的字符串轉換成 機器可以識別的字節數組
byte[] arrSendMsg = Encoding.UTF8.GetBytes(sendMsg);
//向客戶端發送字節數組信息
socConnection.Send(arrSendMsg);
//將發送的字符串信息附加到文本框txtMsg上
txtMsg.AppendText("服務器 " + GetCurrentTime() + "\r\n" + sendMsg + "\r\n");
}
catch (Exception ex) {
txtMsg.AppendText("客戶端已斷開連接,無法發送信息!" + "\r\n");
}
}
/// <summary>
/// 接收客戶端發來的信息
/// </summary>
/// <param name="socketClientPara">客戶端套接字對象</param>
private void ServerRecMsg(object socketClientPara)
{
Socket socketServer = socketClientPara as Socket;
while (true)
{
//創建一個內存緩沖區 其大小為1024*1024字節 即1M
byte[] arrServerRecMsg = new byte[1024 * 1024];
try
{
//將接收到的信息存入到內存緩沖區,並返回其字節數組的長度
int length = socketServer.Receive(arrServerRecMsg);
//將機器接受到的字節數組轉換為人可以讀懂的字符串
string strSRecMsg = Encoding.UTF8.GetString(arrServerRecMsg, 0, length);
//將發送的字符串信息附加到文本框txtMsg上
txtMsg.AppendText("天涯 " + GetCurrentTime() + "\r\n" + strSRecMsg + "\r\n");
}
catch (Exception ex) {
txtMsg.AppendText("客戶端已斷開連接!" + "\r\n");
break;
}
}
}
/// <summary>
/// 發送消息到客戶端
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSendMsg_Click(object sender, EventArgs e)
{
//調用 ServerSendMsg方法 發送信息到客戶端
ServerSendMsg(this.txtSendMsg.Text.Trim());
this.txtSendMsg.Clear();
}
/// <summary>
/// 快捷鍵 Enter 發送信息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void txtSendMsg_KeyDown(object sender, KeyEventArgs e)
{
//如果用戶按下了Enter鍵
if (e.KeyCode == Keys.Enter)
{
//則調用 服務器向客戶端發送信息的方法
ServerSendMsg(txtSendMsg.Text.Trim());
this.txtSendMsg.Clear();
}
}
/// <summary>
/// 獲取當前系統時間的方法
/// </summary>
/// <returns>當前時間</returns>
private DateTime GetCurrentTime()
{
DateTime currentTime = new DateTime();
currentTime = DateTime.Now;
return currentTime;
}
/// <summary>
/// 獲取本地IPv4地址
/// </summary>
/// <returns></returns>
public IPAddress GetLocalIPv4Address() {
IPAddress localIpv4 = null;
//獲取本機所有的IP地址列表
IPAddress[] IpList = Dns.GetHostAddresses(Dns.GetHostName());
//循環遍歷所有IP地址
foreach (IPAddress IP in IpList) {
//判斷是否是IPv4地址
if (IP.AddressFamily == AddressFamily.InterNetwork)
{
localIpv4 = IP;
}
else {
continue;
}
}
return localIpv4;
}
/// <summary>
/// 獲取本地IP事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnGetLocalIP_Click(object sender, EventArgs e)
{
//接收IPv4的地址
IPAddress localIP = GetLocalIPv4Address();
//賦值給文本框
this.txtIP.Text = localIP.ToString();
}
}
}
2)客戶端代碼
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace TcpMsgClient
{
public partial class FrmClient : Form
{
public FrmClient()
{
InitializeComponent();
//關閉對文本框的非法線程操作檢查
TextBox.CheckForIllegalCrossThreadCalls = false;
}
//創建 1個客戶端套接字 和1個負責監聽服務端請求的線程
Socket socketClient = null;
Thread threadClient = null;
/// <summary>
/// 連接服務端事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnListenServer_Click(object sender, EventArgs e)
{
//定義一個套字節監聽 包含3個參數(IP4尋址協議,流式連接,TCP協議)
socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//需要獲取文本框中的IP地址
IPAddress ipaddress = IPAddress.Parse(this.txtIP.Text.Trim());
//將獲取的ip地址和端口號綁定到網絡節點endpoint上
IPEndPoint endpoint = new IPEndPoint(ipaddress, int.Parse(this.txtPort.Text.Trim()));
//這里客戶端套接字連接到網絡節點(服務端)用的方法是Connect 而不是Bind
try
{
socketClient.Connect(endpoint);
this.txtMsg.AppendText("客戶端連接服務端成功!" + "\r\n");
this.btnListenServer.Enabled = false;
//創建一個線程 用於監聽服務端發來的消息
threadClient = new Thread(RecMsg);
//將窗體線程設置為與后台同步
threadClient.IsBackground = true;
//啟動線程
threadClient.Start();
}
catch (Exception ex) {
this.txtMsg.AppendText("遠程服務端斷開,連接失敗!" + "\r\n");
}
}
/// <summary>
/// 接收服務端發來信息的方法
/// </summary>
private void RecMsg()
{
while (true) //持續監聽服務端發來的消息
{
try
{
//定義一個1M的內存緩沖區 用於臨時性存儲接收到的信息
byte[] arrRecMsg = new byte[1024 * 1024];
//將客戶端套接字接收到的數據存入內存緩沖區, 並獲取其長度
int length = socketClient.Receive(arrRecMsg);
//將套接字獲取到的字節數組轉換為人可以看懂的字符串
string strRecMsg = Encoding.UTF8.GetString(arrRecMsg, 0, length);
//將發送的信息追加到聊天內容文本框中
txtMsg.AppendText("服務端 " + GetCurrentTime() + "\r\n" + strRecMsg + "\r\n");
}
catch (Exception ex) {
this.txtMsg.AppendText("遠程服務器已中斷連接!"+"\r\n");
this.btnListenServer.Enabled = true;
break;
}
}
}
/// <summary>
/// 發送字符串信息到服務端的方法
/// </summary>
/// <param name="sendMsg">發送的字符串信息</param>
private void ClientSendMsg(string sendMsg)
{
try {
//將輸入的內容字符串轉換為機器可以識別的字節數組
byte[] arrClientSendMsg = Encoding.UTF8.GetBytes(sendMsg);
//調用客戶端套接字發送字節數組
socketClient.Send(arrClientSendMsg);
//將發送的信息追加到聊天內容文本框中
txtMsg.AppendText("天涯 " + GetCurrentTime() + "\r\n" + sendMsg + "\r\n");
}
catch(Exception ex){
this.txtMsg.AppendText("遠程服務器已中斷連接,無法發送消息!" + "\r\n");
}
}
private void btnSendMsg_Click(object sender, EventArgs e)
{
//調用ClientSendMsg方法 將文本框中輸入的信息發送給服務端
ClientSendMsg(this.txtClientSendMsg.Text.Trim());
this.txtClientSendMsg.Clear();
}
private void txtClientSendMsg_KeyDown(object sender, KeyEventArgs e)
{
//當光標位於文本框時 如果用戶按下了鍵盤上的Enter鍵
if (e.KeyCode == Keys.Enter)
{
//則調用客戶端向服務端發送信息的方法
ClientSendMsg(this.txtClientSendMsg.Text.Trim());
this.txtClientSendMsg.Clear();
}
}
/// <summary>
/// 獲取當前系統時間的方法
/// </summary>
/// <returns>當前時間</returns>
private DateTime GetCurrentTime()
{
DateTime currentTime = new DateTime();
currentTime = DateTime.Now;
return currentTime;
}
}
}
4、程序演示
1)正常情況
運行服務端程序,輸入本地的IP地址,不知道可以點擊獲取本地IP按鈕,輸入本地端口,點擊啟動服務按鈕,
消息框會出現開始監聽客戶端信息,表示服務端啟動服務成功

運行客戶端程序,輸入服務端的IP和端口,點擊連接服務端按鈕,當服務端的消息框出現客戶端連接成功,
表示連接服務端成功


服務端啟動服務成功,客戶端連接服務端成功,可以兩端進行通信發消息

2)異常處理
原先參考的源碼未做異常處理,導致退出程序報錯,提示未友好等
當客戶端異常處理
服務端中斷開連接,自動提示
服務端中斷連接,無法發送消息

服務端異常處理
客戶端斷開連接,自動提示
客戶端斷開連接,無法發送消息


