添加using指令集:
using System.Net.Sockets;using System.Threading;using System.Net;
添加定義(IP地址 IP介面 berkeley通訊端介面):
private IPAddress serverIP = IPAddress.Parse("127.0.00.1");private IPEndPoint serverFullAddr;private Socket sock;
中斷連線:
//中斷連線 private void btnClose_Click(object sender, EventArgs e) { sock.Close(); btnConn.Enabled = true ; }
串連伺服器:
//串連伺服器端 private void btnConn_Click(object sender, EventArgs e) { btnConn.Enabled = false; serverIP = IPAddress.Parse(tbxIP.Text); try { serverFullAddr = new IPEndPoint(serverIP, int.Parse(tbxPort.Text));//設定IP,連接埠 sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //指定本地主機地址和連接埠號碼 sock.Connect(serverFullAddr); //btnConn.Enabled = false; lblError.Text = "串連伺服器成功。。。。"; btnClose.Enabled = true; sock.Close(); } catch (Exception ee) { btnConn.Enabled = true; lblError.Text = "串連伺服器失敗。。。請仔細檢查伺服器是否開啟"+ ee; } }
發送訊息:
//發送訊息 private void btnSend_Click(object sender, EventArgs e) { serverFullAddr = new IPEndPoint(serverIP, int.Parse(tbxPort.Text));//設定IP,連接埠 sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //指定本地主機地址和連接埠號碼 sock.Connect(serverFullAddr); byte[] byteSend = System.Text.Encoding.Default.GetBytes(this.tbxMessage.Text); byte[] message = new byte[1024]; string mess = ""; int bytes = 0; try { //發送資料 sock.Send(byteSend); bytes = sock.Receive(message);//接收資料 mess = mess + Encoding.Default.GetString(message,0,bytes);//編碼(當接收的位元組大於1024的時候 這應該是迴圈接收,測試就沒有那樣寫了) //do //{ // bytes = newSocket.Receive(message, message.Length, 0); // mess = mess + Encoding.ASCII.GetString(message, 0, bytes); //} //while (bytes > 0); tbxMessage.Text = mess; } catch (Exception ex) { lblError.Text = "出現錯誤,請聯絡管理員"+ex; } sock.Close(); }
清空訊息:
//清空訊息 private void btnClean_Click(object sender, EventArgs e) { tbxMessage.Text = ""; }
接收資料(由於多線程的原因,稍微麻煩一點):
1.開啟線程
Thread myThead = null;
2.線程函數,類似於指定中斷服務函數
myThead = new Thread(new ThreadStart(BeginListen)); myThead.Start();
3.編寫函數(這一部分理解比較麻煩,涉及委託,因為要是用的顯示資料的控制項是在主線程裡面開啟的,而控制項的預設值是不可以跨線程使用,所以需要委託。)
a.需要好好理解Invoke函數:他是在擁有該控制項的基礎視窗控制代碼的線程上,用指定的參數列表執行指定的委託。通俗一點就是,我委託擁有這個控制項的線程來幫我執行這個操作。
b.為什麼使用accept函數。為了新的串連創造一個新的socket呢。理解為:sock這個socket已經在監聽了,而且隊列中允許的傳入串連數有多個,所以新建立一個socket,當隊列中有忙碌的傳入串連時,將地址給新的socket來recieve資料。高效,穩定一些。
private void BeginListen() { serverI2.xianP = IPAddress.Parse(tbxIP.Text); //IP serverFullAddr = new IPEndPoint(serverIP, int.Parse(tbxPort.Text));//設定IP,連接埠 sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //指定本地主機地址和連接埠號碼 sock.Bind(serverFullAddr); lbxMessage.Invoke(new SetTextCallback(SetText), "啟動成功 時間:" + DateTime.Now, 1); byte[] message = new byte[1024]; string mess = ""; while (true) { try { sock.Listen(5);//backlog 參數指定隊列中最多可容納的等待接受的傳入串連數。 Socket newSocket = sock.Accept();//為建立串連建立新的socket。sock這個socket是用來監聽的,當他有串連請求的時候,將地址給新的socket來接收,這樣不影響他繼續監聽原本的socket。 int bytes = newSocket.Receive(message);//用剛才chuangjian接收資料 mess = Encoding.Default.GetString(message, 0, bytes);//對接收位元組編碼(S與C 兩端編碼格式必須一致不然中文亂碼)(當接收的位元組大於1024的時候 這應該是迴圈接收,測試就沒有那樣寫了) lbxMessage.Invoke(new SetTextCallback(SetText), mess, 1);//子線程操作主線程UI控制項 //擷取用戶端的IP和連接埠 string ip11 = newSocket.RemoteEndPoint.AddressFamily.ToString(); mess = "已接收資料: "+ mess +" 來自:" +ip11+ " 目前時間為:" + DateTime.Now; //處理資料 newSocket.Send(Encoding.Default.GetBytes(mess));//向用戶端發送資料 } catch (SocketException se) { lbxMessage.Invoke(new SetTextCallback(SetText), mess + se, 1); } } } #region//聲名委託 //生命delegate對象,可以理解成函數指標 delegate void SetTextCallback(string text, int num); //欲傳遞的方法,她與CompareDelegate具有相同的參數和傳回值類型 private void SetText(string text, int num) { lbxMessage.Items.Add(text); } #endregion
一個參考的完整的Demo:
using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Windows.Forms; using System.Net;using System.Net.Sockets;using System.IO;using System.Threading; namespace TcpClientTest{ public partial class FormMain : Form { public FormMain() { InitializeComponent(); } private void FormMain_Load(object sender, EventArgs e) { //初始化控制項 txtSendMssg.Text = "測試資料"; //開啟Listener開始監聽 Thread thrListener = new Thread(new ThreadStart(Listen)); thrListener.Start(); } private void FormMain_FormClosing(object sender, FormClosingEventArgs e) { //強制關閉程式(強行終止Listener) Environment.Exit(0); } //發送資料 private void btnSend_Click(object sender, EventArgs e) { TcpClient tcpClient = new TcpClient(); //tcpClient.Connect(IPAddress.Parse("170.0.0.78"), 2014); tcpClient.Connect(IPAddress.Parse("127.0.0.1"), 2014); NetworkStream ntwStream = tcpClient.GetStream(); if (ntwStream.CanWrite) { Byte[] bytSend = Encoding.UTF8.GetBytes(txtSendMssg.Text); ntwStream.Write(bytSend, 0, bytSend.Length); } else { MessageBox.Show("無法寫入資料流"); ntwStream.Close(); tcpClient.Close(); return; } ntwStream.Close(); tcpClient.Close(); } //監聽資料 private void Listen() { Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); listener.Bind(new IPEndPoint(IPAddress.Any, 2014)); //不斷監聽連接埠 while (true) { listener.Listen(0); Socket socket = listener.Accept(); NetworkStream ntwStream = new NetworkStream(socket); StreamReader strmReader = new StreamReader(ntwStream); Invoke(new PrintRecvMssgDelegate(PrintRecvMssg), new object[] { strmReader.ReadToEnd() }); socket.Close(); } //程式的listener一直不關閉 //listener.Close(); } //線程內向文字框txtRecvMssg中添加字串及委託 private delegate void PrintRecvMssgDelegate(string s); private void PrintRecvMssg(string info) { txtRecvMssg.Text += string.Format("[{0}]:{1}\r\n", DateTime.Now.ToLongTimeString(), info); } }}