簡單的C#TCP協議收發數據示例


參考:http://www.cnblogs.com/jzxx/p/5630516.html

一、原作者的這段話很好,先引用一下:

Socket的Send方法,並非大家想象中的從一個端口發送消息到另一個端口,它僅僅是拷貝數據到基礎系統的發送緩沖區,然后由基礎系統將發送緩沖區的數據到連接的另一端口。值得一說的是,這里的拷貝數據與異步發送消息的拷貝是不一樣的,同步發送的拷貝,是直接拷貝數據到基礎系統緩沖區,拷貝完成后返回,在拷貝的過程中,執行線程會IO等待, 此種拷貝與Socket自帶的Buffer空間無關,但異步發送消息的拷貝,是將Socket自帶的Buffer空間內的所有數據,拷貝到基礎系統發送緩沖區,並立即返回,執行線程無需IO等待,所以異步發送在發送前必須執行SetBuffer方法,拷貝完成后,會觸發你自定義回調函數ProcessSend,在ProcessSend方法中,調用SetBuffer方法,重新初始化Buffer空間。

二、代碼如下:

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);
        }
    }
}

需要在項目上加兩個TextBox名字分別為 txtSendMssg、txtRecvMssg和一個Button(名字為 btnSend

三、運行效果(效果圖片見原作者文章)

在發送數據的文本框中分別輸入“千山鳥飛絕”、“萬徑人蹤滅”、“孤舟蓑笠翁”、“獨釣寒江雪”四句話,輸完一句話,單擊一次“發送數據”按鈕,就可以在接收數據里看到這四句話了。上面代碼中,信息的發送時通過TcpClient連接到127.0.0.1的2014端口,信息的接收是通過Listen函數不斷監聽本機的2014端口實現的。從自己創建的線程中修改控件信息,用到了委托。

四、補充

1、運行時提示:由於目標計算機積極拒絕,無法連接。 127.0.0.1:2014

用 System.Net.Dns.GetHostAddresses("localhost")[1].ToString(); 取得的也是127.0.0.1

參考:https://blog.csdn.net/u010784236/article/details/51820284 也與我的情況不一樣。

想想我是在局域網中,用ipconfig /all 找到自己的ip  192.168.3.5 替換  127.0.0.1 添加防火牆規則,仍不行。關閉防火牆,還不行。

(如何獲得IP,還可參考:https://blog.csdn.net/fwj380891124/article/details/18214145)

正准備放棄時,看到 https://blog.csdn.net/fengzheng22/article/details/17266105 其中有一句:

需要你用tcpclient訪問的IP的端口正在被監聽,否則就會顯示積極拒絕,不是看他是否被占用,要看他是否在監聽

想想我是直接整體復制的代碼,不是雙擊窗體后單獨寫的formMain_load代碼。而服務端應該是在formMain_load時開始監聽。

於是重新修改代碼,使得formMain_load時先運行服務端監聽的代碼。重新生成並運行,正常。這個錯誤太低級。

如果在局域網兩台電腦上分別運行客戶端和服務端,要確保能ping通,檢查防火牆規則。參考這里:https://jingyan.baidu.com/article/a65957f4f557cb24e67f9ba6.html

2、另外,這里還有個例子:https://www.jb51.net/article/130148.htm

3、參考:https://www.cnblogs.com/straight/articles/7660889.html

socket起源於Unix,而Unix/Linux基本哲學之一就是“一切皆文件”,都可以用“打開open –> 讀寫write/read –> 關閉close”模式來操作。我的理解就是Socket就是該模式的一個實現,socket即是一種特殊的文件,一些socket函數就是對其進行的操作(讀/寫IO、打開、關閉)。 

如果作為一個服務器,在調用socket()、bind()之后就會調用listen()來監聽這個socket,如果客戶端這時調用connect()發出連接請求,服務器端就會接收到這個請求。

 isten函數的第一個參數即為要監聽的socket描述字,第二個參數為相應socket可以排隊的最大連接個數。socket()函數創建的socket默認是一個主動類型的,listen函數將socket變為被動類型的,等待客戶的連接請求。

TCP服務器端依次調用socket()、bind()、listen()之后,就會監聽指定的socket地址了。TCP客戶端依次調用socket()、connect()之后就向TCP服務器發送了一個連接請求。TCP服務器監聽到這個請求之后,就會調用accept()函數取接收請求,這樣連接就建立好了。之后就可以開始網絡I/O操作了,即類同於普通文件的讀寫I/O操作。

 4、TCP協議三次握手過程分析 參考:http://www.cnblogs.com/rootq/articles/1377355.html

5、C# TCP多線程服務器示例 參考:https://www.cnblogs.com/zhangxiaoyong/p/6486311.html

6. C# socket端口復用-多主機頭綁定 參考:https://www.cnblogs.com/viewcozy/p/4666137.html

7、定時執行、一對多  參考:  http://www.cnblogs.com/chenxizhang/archive/2011/09/10/2172994.html

8、在同步模式中,在服務器上使用Accept方法接入連接請求,而在客戶端則使用Connect方法來連接服務器。相對地,在異步模式下,服務器可以使用BeginAccept方法和EndAccept方法來完成連接到客戶端的任務,在客戶端則通過BeginConnect方法和EndConnect方法來實現與服務器的連接。  參考:http://www.cnblogs.com/sunev/archive/2012/08/07/2625688.html

9、C#的IPAddress IPEndPoint  參考 https://www.cnblogs.com/2Yous/p/5797592.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM