淺談C# Socket編程及C#如何使用多線程

來源:互聯網
上載者:User

去年暑假學習了幾個月asp.net 最後幾個星期弄了下C# socket .也算知道了個一知半解了, 好久沒動C#了, 雖然這語言進階的讓我對他沒興趣, 不過還是回憶回憶, 忘了一乾二淨就不好了.

C# Socket:

建議初學C# socket的菜鳥朋友不要使用TcpListenner, TcpClient這些MS封裝好的類庫, 這些封裝好的類用起來的確方便, 但你用完了你又學到了什麼了? 那該用什麼了, 只用Socket這一個類. 不錯,這樣會麻煩點的,

但是, 在C#裡面, 就連Socket, MS都進行了一翻封裝,使得Socket使用起來也是十分的簡單, 我剛學的時候寫過一個很菜的TCP聊天程式, 兩人對聊的. 大家可以去嘗試下一S多C的聊天程式,TCP會了可以去做個UDP的.

UDP會了,學學SMTP, 哎, SMTP也是封裝的太厲害, 都成傻瓜式的了, 然後大家可以看下MultiThread,也就是多線程, 這些都差不多了, 可以取嘗試寫個Proxy. 我當時就是這樣學的, 呵呵, 不過我只是個菜鳥, 現在搞asm/c/C++去了,就把這些忘了差不多.

首先必須包含的兩個命名空間:

Using System.Net;

Using System.Net.Sockets;

幾個常用的類:(這些東西,查下MSDN就很詳細了)

IPHostEntry, Dns,IPAddress,IPEndPoint,還有最重要的Socket

IPEndPoint: 這個是網路終結點,很好理解,就是網路上一個固定的地址:一個IP與一個連接埠的組合.

下面我還是以我以前寫的一個很簡單的聊天程式做樣本吧, (很短代碼的)

Form1.cs

//說明下, 這個是集Server與Client與一體的.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;   //這個和上面的是使用Socket必須的.
using System.IO;      
using System.Threading;      //這個是使用多線程必需的.

namespace OnlySocket
{
    public partial class Form1 : Form           //partial表示這塊代碼只是Form1類的部分, Form1類繼承自Form類
    {
        public Form1()
        {
            InitializeComponent();    //建構函式, 初始化容器.
        }
        Socket sock;          //定義一個Socket類的對象 (預設為protected)
        Thread th;             //定義一個Thread類的對象
        //

        public static IPAddress GetServerIP()        //靜態函數, 無需執行個體化即可調用.
        {
            IPHostEntry ieh = Dns.GetHostByName(Dns.GetHostName()); //不多說了, Dns類的兩個靜態函數

             //或用DNS.Resolve()代替GetHostName()
            return ieh.AddressList[0];                  //返回Address類的一個執行個體. 這裡AddressList是數組並不奇怪,一個Server有N個IP都有可能
        }
      

        private void BeginListen()               //Socket監聽函數, 等下作為建立新線程的參數
        {
            IPAddress serverIp = GetServerIP();         //調用本類靜態函數GetServerIP得到本機IPAddress.
            IPEndPoint iep = new IPEndPoint(serverIp, Convert.ToInt32(tbPort.Text));    //本地終結點
            sock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);   //執行個體化內成員sock
            Byte[] byteMessage = new Byte[100]; //存放訊息的位元組數組緩衝區, 注意數組表示方法,和C不同的.
            this.lbIep.Text = iep.ToString();         
            sock.Bind(iep);                                  //Socket類的一個重要函數, 綁定一個IP,
            while (true)                     //這裡弄了個死迴圈來監聽連接埠, 有人會問死迴圈了,那程式不卡住了, 注意這隻是個類, 這裡還沒有main函數呢.
            {
                try
                {
                    sock.Listen(5);             //好了,sock綁定了本地終結點就可以開始監聽了,5表示最大串連數為5
                    Socket newSock = sock.Accept();     //這裡又有Socket類的一個重要的方法:Accept, 該方法接受來自外面的Socket串連請求, 並返回一個Socket通訊端, 這個通訊端就開始處理這一個client與Server之間的對話
                    newSock.Receive(byteMessage); //接受client發送過來的資料儲存到緩衝區.
                    string msg = "From [" + newSock.RemoteEndPoint.ToString() + "]:" +System.Text.Encoding.UTF8.GetString(byteMessage)+"\n";   //GetString()函數將byte數群組轉換為string類型.
                    rtbTalk.AppendText(msg+"\n");        //顯示在文本控制項裡
                }
                catch (SocketException se)              //捕捉異常,
                {
                    lbState.Text = se.ToString();       //將其顯示出來, 在此亦可以自訂錯誤.
                }
            }
        }

        private void btConnect_Click(object sender, EventArgs e)   //連線按鍵觸發的事件: 串連Server
        {
            btConnect.Enabled = false;
            btStopConnect.Enabled = true;
            try
            {
                th = new Thread(new ThreadStart(BeginListen));          //建立一個新的線程專門用於處理監聽,這句話可以分開寫的,比如: ThreadStart ts=new ThreadStart(BeginListen); th=new Thread (ts); 不過要注意, ThreadStart的建構函式的參數一定要是無參數的函數. 在此函數名其實就是其指標, 這裡是委託嗎?
                th.Start();                            //啟動線程
                lbState.Text = "Listenning...";
            }
            catch (SocketException se)           //處理異常
            {
                MessageBox.Show(se.Message, "出現問題", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            catch (ArgumentNullException ae)   //參數為空白異常
            {
                lbState.Text = "參數錯誤";
                MessageBox.Show(ae.Message, "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }

        }

        private void btStopConnect_Click(object sender, EventArgs e)  //停止監聽
        {
            btStopConnect.Enabled = false;
            btConnect.Enabled = true;
            sock.Close();                     //關閉通訊端
            th.Abort();                         //終止監聽線程
           
           lbState.Text = "Listenning stopped";
        }

        private void btExit_Click(object sender, EventArgs e)       
        {
            sock.Close();
            th.Abort();
            Dispose();             //清理資源,就是釋放記憶體
            this.Close();          //關閉對話方塊, 退出程式
        }

        private void btSend_Click(object sender, EventArgs e)
        {
            try
            {
                IPAddress clientIp = IPAddress.Parse(tbTargetIp.Text);    //類IPAddress的靜態函數Parse() :將Text轉化為IPAddress的一個執行個體.
                int clientPort = Convert.ToInt32(tbPort.Text);                 //C#的這些轉化函數很方便的,不像C++那樣麻煩
                IPEndPoint clientIep = new IPEndPoint(clientIp, clientPort);     //這裡用client表示不是很好...., 
                Byte[] byte_Message;
                Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);            執行個體化的時候還有很多參數的, 這個是Tcp的. Tcp的SocketType是Stream:資料流, 如果協議類型是UDP, 則是資料包傳送, QQ就是用的UDP.
                socket.Connect(clientIep); //Socket的又一個函數Connect(IPEndPoint) .串連遠程通訊端
                byte_Message = System.Text.Encoding.UTF8.GetBytes(rtbWords.Text); //發現UTF8可支援中文,就用之
                socket.Send(byte_Message);
                rtbTalk.AppendText("\n"+"My words:" + rtbWords.Text + "\n");
                socket.Shutdown(SocketShutdown.Both);
                socket.Close();
            }
            catch (ArgumentNullException ae)
            {
                MessageBox.Show(ae.Message,"參數為空白",MessageBoxButtons.OKCancel,MessageBoxIcon.Information);
            }
            catch (SocketException se)
            {
                MessageBox.Show(se.Message, "出現問題", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }
       
    }
}

Program.cs

using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace OnlySocket
{
    static class Program
    {
        /// <summary>
        /// 應用程式的主進入點。
        /// </summary>
        [STAThread]
        static void Main()        //這兒才是main函數

        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }
}

寫了半天了, 夠累的了, 雖然都是很基礎的東西, 我自己寫的時候也複習了一邊 , 呵呵.

其實多線程我自己也不是很熟練, 記得去年暑假寫過一個多線程掃描器, 也不知為啥, 線程開到50以上就異常, 很鬱悶的. 其實當時我就是用的new Thread=Thread(new ThreadStart(Fun))實現的, 方法感覺很笨拙,呵呵.

大致代碼好像是這樣的吧:

先寫個Scan類:

public class Scan

{

try{ public Scan(){   ...Init...   }

            public void Scan{ ..task迴圈掃描... } //task結構體裡面有IP, 連接埠, 是否已掃描標記fLag}

catch{}

}

然後主函數裡面可以這樣搞:

Scan[] scanner = new Scan[XX]

Thread[] thread = new Thread[XX];
            for (int i = 0; i < XX;i++)
            {
                scanner[i] = new Scan(this, i);
                thread[i] = new Thread(new ThreadStart(scanner[i].StartScan));
                thread[i].Start();

            }

其實這樣就可以簡單的實現多線程了.

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.