使用Winfrom寫一個聊天軟體
- 效果圖
- 原理
- 代碼實作
- 服務端
- 用戶端
- 注意事項
效果圖
原理
代碼實作
服務端
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.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ScoketServerTest
{
public partial class Server : Form
{
public Server()
{
InitializeComponent();
}
/// <summary>
/// socket套接字
/// </summary>
Socket socketServer = null;
/// <summary>
/// 用戶端
/// </summary>
Socket socketClienCon = null;
/// <summary>
/// 用戶端資訊
/// </summary>
string clientIpMsg = string.Empty;
/// <summary>
/// 伺服器是否關閉 false:開啟 true:已關閉
/// </summary>
bool isClose = false;
private void Server_Load(object sender, EventArgs e)
{
// 擷取目前主機名
this.textBox1.Text = System.Net.Dns.GetHostName();
// 預設端口設定為33333
this.portTex.Text = "33333";
// 根據主機名擷取ip位址
//注意擷取的是IPV4的位址,有些電腦如果第一個就是IPV4可以把索引'1'更換成'0'
this.ipTex.Text = System.Net.Dns.GetHostEntry(this.textBox1.Text).AddressList[1].ToString();
}
private void btnOpen_Click(object sender, EventArgs e)
{
try
{
// 擷取輸入框的ip位址
IPAddress ip = IPAddress.Parse(this.ipTex.Text.ToString().Trim());
// 擷取端口号
int port = int.Parse(this.portTex.Text.ToString().Trim());
IPEndPoint ipe = new IPEndPoint(ip,port);
socketServer = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// 綁定網絡終結點
socketServer.Bind(ipe);
// 設定監聽最大連接配接數
socketServer.Listen(10);
isClose = false;
this.lstRev.Items.Add(string.Format("伺服器已開啟,開始監聽消息"));
this.lstRev.Items.Add(string.Format("目前主機名:{0}", this.textBox1.Text));
this.lstRev.Items.Add(string.Format("目前IP:{0}", System.Net.Dns.GetHostEntry(this.textBox1.Text).AddressList[0].ToString()));
Thread clientThread = new Thread(ListenTh); //建立一個線程,用于監聽用戶端連接配接
clientThread.IsBackground = true; //設定為背景線程
clientThread.Start(); //開啟線程
Thread revThread = new Thread(RevTh); //建立一個線程,用于監聽用戶端發來的消息
revThread.IsBackground = true;//設定為背景線程
revThread.Start(); //開啟線程
}
catch (Exception ex)
{
string errorMsg = string.Format("伺服器開啟失敗,失敗原因:{0}", ex.Message.ToString());
this.lstRev.Items.Add(errorMsg);
}
}
/// <summary>
/// 監聽用戶端
/// </summary>
private void ListenTh()
{
while (!isClose)
{
try
{
socketClienCon = socketServer.Accept(); //為監聽到的用戶端建立新的套接字
IPAddress clientIp = (socketClienCon.RemoteEndPoint as IPEndPoint).Address; //擷取連接配接的用戶端的IP位址
int clientPort = (socketClienCon.RemoteEndPoint as IPEndPoint).Port; //擷取連接配接的用戶端的端口号
clientIpMsg = string.Format("用戶端IP:{0},端口号:{1}", clientIp, clientPort);
OnlineClient(clientIpMsg); //線上使用者并列印到線上使用者視窗
}
catch (Exception ex)
{
string error = string.Format("監聽用戶端資訊異常,錯誤原因:{0}", ex.Message.ToString());
Recevice(error);
break;
}
}
}
delegate void OnlineClientDele(string clientMsg); //建立線上使用者委托方法
/// <summary>
/// 線上使用者
/// </summary>
/// <param name="clientMsg">線上使用者資訊</param>
private void OnlineClient(string clientMsg)
{
if (this.InvokeRequired)
{
OnlineClientDele oel = new OnlineClientDele(OnlineClient);
this.Invoke(oel, new object[] { clientMsg });
}
else
{
this.lstClient.TopIndex = this.lstClient.Items.Count - 1;
this.lstClient.Items.Add(clientMsg);
}
}
/// <summary>
/// 接收用戶端消息
/// </summary>
private void RevTh()
{
while (!isClose)
{
if (socketClienCon != null)
{
try
{
byte[] clientMsg = new byte[1024]; //建立接收消息緩存數組
int msgLen = socketClienCon.Receive(clientMsg); //接收從用戶端發來的資料
if (msgLen == 0)
{
break;
}
else
{
string msg = string.Format("{0}【收到 ← 來自:{1}】:\n{2}", DateTime.Now, clientIpMsg, Encoding.UTF8.GetString(clientMsg, 0, msgLen));
Recevice(msg); //接收消息并列印到對話視窗
}
}
catch (Exception ex)
{
string error = string.Format("接收用戶端消息異常,錯誤原因:{0}", ex.Message.ToString());
Recevice(error);
break;
}
}
}
}
delegate void ReceviceDele(string msgStr); //建立接收消息委托方法
/// <summary>
/// 接收消息
/// </summary>
/// <param name="msg">用戶端發來的消息</param>
private void Recevice(string msg)
{
if (this.InvokeRequired)
{
ReceviceDele rel = new ReceviceDele(Recevice);
this.Invoke(rel, new object[] { msg });
}
else
{
this.lstRev.TopIndex = this.lstRev.Items.Count - 1;
this.lstRev.Items.Add(msg);
}
}
/// <summary>
/// 發送消息
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSend_Click(object sender, EventArgs e)
{
string msg = this.txtSend.Text.ToString().Trim();
byte[] msgByt = Encoding.UTF8.GetBytes(msg);
socketClienCon.Send(msgByt);
this.lstRev.Items.Add(string.Format("{0}【發送 → 至 {1}】:\n{2}", DateTime.Now, clientIpMsg, msg));
this.txtSend.Text = "";
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
isClose = true;
socketServer.Close();
}
private void btnClose_Click(object sender, EventArgs e)
{
isClose = true;
socketServer.Close();
}
}
}
用戶端
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.Net.WebSockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WebspcketClientTest
{
public partial class Client : Form
{
public Client()
{
InitializeComponent();
}
/// <summary>
/// socket套接字
/// </summary>
Socket cilentSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
bool isClose = true;
private void Client_Load(object sender, EventArgs e)
{
this.portTex.Text = "33333";
}
private void btnOpen_Click(object sender, EventArgs e)
{
//擷取ip
IPAddress ip = null;
if (!string.IsNullOrEmpty(this.ipTex.Text.ToString().Trim()))
{
ip = IPAddress.Parse(this.ipTex.Text.ToString().Trim());
}
else if(!string.IsNullOrEmpty(this.texHostName.Text.ToString().Trim()))
{
IPAddress[] addressList = Dns.GetHostEntry(this.texHostName.Text.ToString().Trim()).AddressList;
// 根據HostName 擷取IPV4位址
string ipStr = addressList[0].ToString();
if (ipStr.Length > 17)
{
ipStr = addressList[1].ToString();
}
// IP位址
ip = IPAddress.Parse(ipStr);
this.ipTex.Text = ip.ToString();
}
else
{
MessageBox.Show("無法擷取主機位址!");
this.richTextBox1.Text += "無法擷取主機位址!\n";
return;
}
//擷取端口号
IPEndPoint ep = new IPEndPoint(ip, int.Parse(this.portTex.Text));
//ws = new WebSocketSharp.WebSocket("ws://" + this.texHostName.Text + ":" + this.portTex.Text);
try
{
//ws.Connect();
//建立連接配接
cilentSocket.Connect(ep);
this.richTextBox1.Text += "連接配接成功!\n";
isClose = false;
Thread th = new Thread(StartMsg);
th.IsBackground = true;
th.Start();
}
catch (Exception ex)
{
this.richTextBox1.Text += string.Format("連接配接失敗,原因{0}1\n", ex.ToString());
}
}
// 監聽伺服器資訊
public void StartMsg()
{
while (!isClose)
{
try
{
//發送消息的大小
var bytes = new byte[1024 * 1024];
var mlength = cilentSocket.Receive(bytes);
//将位元組轉換為字元串
string msg = Encoding.UTF8.GetString(bytes, 0, mlength);
this.Invoke(new Action(() =>
{
this.richTextBox1.Text += string.Format("【收到 ← 來自:{0}】:\n{1}\n", DateTime.Now, msg);
}));
}
catch (Exception ex)
{
this.Invoke(new Action(() =>
{
this.richTextBox1.Text += string.Format("連接配接異常{0}\n", ex.ToString());
isClose = true;
}));
}
}
}
private void btnSend_Click(object sender, EventArgs e)
{
//把要發送的字元串轉化為位元組
var bytes = Encoding.UTF8.GetBytes(this.txtSend.Text.Trim());
cilentSocket.Send(bytes);
this.richTextBox1.Text += string.Format("{0}【發送 → 至 {1}】:\n{2}\n", DateTime.Now,this.ipTex.Text, this.txtSend.Text.ToString().Trim());
this.txtSend.Text = "";
}
private void button1_Click(object sender, EventArgs e)
{
isClose = true;
}
}
}
注意事項
- 代碼是直接可以直接使用的,但是注意這是兩個項目的代碼,是以需要建立兩個項目,一個用戶端一個服務端;
- 需要為按鈕綁定點選事件才可以使事件生效;
- 設定ip位址的時候要符合自己網段的ip,否則無法開啟伺服器,另外也可以使用
.127.0.0.1
- 在本項目中用戶端與服務端發送消息使用的是位元組發送,注意發出的編碼與接收的編碼要一緻,否則無法識别.