參看此鏈接http://www.cnblogs.com/longwu/archive/2011/08/25/2153636.html
在上述代碼的基礎上進行了修改,包括一些捕獲異常以及按鈕的應用,擴充了一個listbox確保服務端可以選擇和不同的客戶端進行通信
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Threading; using System.Net.Sockets; using System.Net; using System.Collections; namespace chat1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); StartPosition = FormStartPosition.CenterScreen; //關閉對文本框的非線程操作檢查 TextBox.CheckForIllegalCrossThreadCalls = false; } string RemoteEndPoint; //客戶端的網絡結點 Thread threadwatch = null;//負責監聽客戶端的線程 Socket socketwatch = null;//負責監聽客戶端的套接字 //創建一個和客戶端通信的套接字 Dictionary<string, Socket> dic = new Dictionary<string, Socket> { }; //定義一個集合,存儲客戶端信息 private void button1_Click(object sender, EventArgs e) { this.button1.Enabled = false; //定義一個套接字用於監聽客戶端發來的消息,包含三個參數(IP4尋址協議,流式連接,Tcp協議) socketwatch = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp); //服務端發送信息需要一個IP地址和端口號 IPAddress address = IPAddress.Parse(textBox1.Text.Trim());//獲取文本框輸入的IP地址 //將IP地址和端口號綁定到網絡節點point上 IPEndPoint point = new IPEndPoint(address,int.Parse(textBox2.Text.Trim()));//獲取文本框上輸入的端口號 //此端口專門用來監聽的 //監聽綁定的網絡節點 socketwatch.Bind(point); //將套接字的監聽隊列長度限制為20 socketwatch.Listen(20); //創建一個監聽線程 threadwatch = new Thread(watchconnecting); //將窗體線程設置為與后台同步,隨着主線程結束而結束 threadwatch.IsBackground = true; //啟動線程 threadwatch.Start(); //啟動線程后 textBox3文本框顯示相應提示 textBox3.AppendText("開始監聽客戶端傳來的信息!" + "\r\n"); } void OnlineList_Disp(string Info) { listBoxOnlineList.Items.Add(Info); //在線列表中顯示連接的客戶端套接字 } //監聽客戶端發來的請求 private void watchconnecting() { Socket connection = null; while (true) //持續不斷監聽客戶端發來的請求 { try { connection = socketwatch.Accept(); } catch (Exception ex) { textBox3.AppendText(ex.Message); //提示套接字監聽異常 break; } //獲取客戶端的IP和端口號 IPAddress clientIP = (connection.RemoteEndPoint as IPEndPoint).Address; int clientPort = (connection.RemoteEndPoint as IPEndPoint).Port; //讓客戶顯示"連接成功的"的信息 string sendmsg = "連接服務端成功!\r\n" + "本地IP:" + clientIP + ",本地端口" + clientPort.ToString(); byte[] arrSendMsg = Encoding.UTF8.GetBytes(sendmsg); connection.Send(arrSendMsg); RemoteEndPoint = connection.RemoteEndPoint.ToString(); //客戶端網絡結點號 textBox3.AppendText("成功與" + RemoteEndPoint + "客戶端建立連接!\t\n"); //顯示與客戶端連接情況 dic.Add(RemoteEndPoint, connection); //添加客戶端信息 OnlineList_Disp(RemoteEndPoint); //顯示在線客戶端 //IPEndPoint netpoint = new IPEndPoint(clientIP,clientPort); IPEndPoint netpoint = connection.RemoteEndPoint as IPEndPoint; //創建一個通信線程 ParameterizedThreadStart pts = new ParameterizedThreadStart(recv); Thread thread = new Thread(pts); thread.IsBackground = true;//設置為后台線程,隨着主線程退出而退出 //啟動線程 thread.Start(connection); } } /// /// 接收客戶端發來的信息 /// ///客戶端套接字對象 private void recv(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上 textBox3.AppendText("客戶端:" + socketServer.RemoteEndPoint+",time:" + GetCurrentTime() + "\r\n" + strSRecMsg + "\r\n\n"); } catch (Exception ex) { textBox3.AppendText("客戶端"+socketServer.RemoteEndPoint+"已經中斷連接"+"\r\n"); //提示套接字監聽異常 listBoxOnlineList.Items.Remove(socketServer.RemoteEndPoint.ToString());//從listbox中移除斷開連接的客戶端 socketServer.Close();//關閉之前accept出來的和客戶端進行通信的套接字 break; } } } /// /// 獲取當前系統時間的方法 /// /// 當前時間 private DateTime GetCurrentTime() { DateTime currentTime = new DateTime(); currentTime = DateTime.Now; return currentTime; } //發送信息到客戶端 private void button2_Click(object sender, EventArgs e) { string sendMsg = textBox4.Text.Trim(); //要發送的信息 byte[] bytes = System.Text.Encoding.UTF8.GetBytes(sendMsg); //將要發送的信息轉化為字節數組,因為Socket發送數據時是以字節的形式發送的 if (listBoxOnlineList.SelectedIndex == -1) { MessageBox.Show("請選擇要發送的客戶端!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Stop); } else { string selectClient = listBoxOnlineList.Text; //選擇要發送的客戶端 dic[selectClient].Send(bytes); //發送數據 textBox4.Clear(); textBox3.AppendText(label4.Text + GetCurrentTime() + "\r\n" + sendMsg + "\r\n"); } } //快捷鍵 Enter 發送信息 private void textBox4_KeyDown(object sender, KeyEventArgs e) { //如果用戶按下了Enter鍵 if (e.KeyCode == Keys.Enter) { string sendMsg = textBox4.Text.Trim(); //要發送的信息 byte[] bytes = System.Text.Encoding.UTF8.GetBytes(sendMsg); if (listBoxOnlineList.SelectedIndex == -1) { MessageBox.Show("請選擇要發送的客戶端!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Stop); } else { string selectClient = listBoxOnlineList.Text; //選擇要發送的客戶端 dic[selectClient].Send(bytes); //發送數據 textBox4.Clear(); textBox3.AppendText(label4.Text + GetCurrentTime() + "\r\n" + sendMsg + "\r\n"); } } } private void Form1_Load(object sender, EventArgs e) { } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { DialogResult result = MessageBox.Show("是否退出?選否,最小化到托盤", "操作提示", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question); if (result == DialogResult.Yes) { this.Dispose(); } else if (result == DialogResult.Cancel) { e.Cancel = true; } else { e.Cancel = true; this.WindowState = FormWindowState.Minimized; this.Visible = false; this.notifyIcon1.Visible = true; this.ShowInTaskbar = false; } } private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e) { base.Visible = true; this.notifyIcon1.Visible = false; this.ShowInTaskbar = true; //base.Show(); base.WindowState = FormWindowState.Normal; } } }
以下是客戶端的代碼:
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using System.Threading; using System.Net.Sockets; using System.Net; namespace chat2 { public partial class Form1 : Form { public Form1() { InitializeComponent(); StartPosition = FormStartPosition.CenterScreen; //關閉對文本框的非法線程操作檢查 TextBox.CheckForIllegalCrossThreadCalls = false; } //創建 1個客戶端套接字 和1個負責監聽服務端請求的線程 Thread threadclient = null; Socket socketclient = null; List<IPEndPoint> mlist =new List<IPEndPoint>(); private void button1_Click(object sender, EventArgs e) { //SocketException exception; this.button1.Enabled = false; //定義一個套接字監聽 socketclient = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp); //獲取文本框中的IP地址 IPAddress address = IPAddress.Parse(textBox1.Text.Trim()); //將獲取的IP地址和端口號綁定在網絡節點上 IPEndPoint point = new IPEndPoint(address,int.Parse(textBox2.Text.Trim())); try { //客戶端套接字連接到網絡節點上,用的是Connect socketclient.Connect(point); } catch (Exception ) { //MessageBox. MessageBox.Show("連接失敗\r\n"); this.button1.Enabled = true; return; } threadclient=new Thread(recv); threadclient.IsBackground=true; threadclient.Start(); } // 接收服務端發來信息的方法 private void recv()// { int x = 0; while (true)//持續監聽服務端發來的消息 { try { //定義一個1M的內存緩沖區,用於臨時性存儲接收到的消息 byte[] arrRecvmsg = new byte[1024 * 1024]; //將客戶端套接字接收到的數據存入內存緩沖區,並獲取長度 int length = socketclient.Receive(arrRecvmsg); //將套接字獲取到的字符數組轉換為人可以看懂的字符串 string strRevMsg = Encoding.UTF8.GetString(arrRecvmsg, 0, length); if (x == 1) { textBox3.AppendText("服務器:" + GetCurrentTime() + "\r\n" + strRevMsg + "\r\n\n"); } else { textBox3.AppendText(strRevMsg + "\r\n\n"); x = 1; } } catch(Exception ex) { textBox3.AppendText("遠程服務器已經中斷連接"+"\r\n"); this.button1.Enabled = true; break; } } } //獲取當前系統時間 private DateTime GetCurrentTime() { DateTime currentTime = new DateTime(); currentTime = DateTime.Now; return currentTime; } //發送字符信息到服務端的方法 private void ClientSendMsg(string sendMsg) { //將輸入的內容字符串轉換為機器可以識別的字節數組 byte[] arrClientSendMsg = Encoding.UTF8.GetBytes(sendMsg); //調用客戶端套接字發送字節數組 socketclient.Send(arrClientSendMsg); //將發送的信息追加到聊天內容文本框中 textBox3.AppendText(this.label4.Text+": " + GetCurrentTime() + "\r\n" + sendMsg + "\r\n\n"); } //點擊按鈕button2 向服務端發送信息 private void button2_Click(object sender, EventArgs e) { //調用ClientSendMsg方法 將文本框中輸入的信息發送給服務端 ClientSendMsg(textBox4.Text.Trim()); textBox4.Clear(); } private void textBox4_KeyDown_1(object sender, KeyEventArgs e) { //當光標位於文本框時 如果用戶按下了鍵盤上的Enter鍵 if (e.KeyCode == Keys.Enter) { //則調用客戶端向服務端發送信息的方法 ClientSendMsg(textBox4.Text.Trim()); textBox4.Clear(); } } private void Form1_Load(object sender, EventArgs e) { } private void Form1_FormClosing(object sender, FormClosingEventArgs e) { DialogResult result = MessageBox.Show("是否退出?選否,最小化到托盤", "操作提示", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question); if (result == DialogResult.Yes) { this.Dispose(); } else if (result == DialogResult.Cancel) { e.Cancel = true; } else { e.Cancel = true; this.WindowState = FormWindowState.Minimized; this.Visible = false; this.notifyIcon1.Visible = true; this.ShowInTaskbar = false; } } private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e) { base.Visible = true; this.notifyIcon1.Visible = false; this.ShowInTaskbar = true; //base.Show(); base.WindowState = FormWindowState.Normal; } } }
程序還有不足,后期還會繼續改進
客戶端在斷開連接后,應該把socket關閉;
if (result == DialogResult.Yes)
{
if (socketclient == null)
{
this.Dispose();
}
else
{
socketclient.Close();
this.Dispose();
}
}
其他錯誤,歡迎大家指出,需要完善的地方,也希望大家給出建議