去年暑假學習了幾個月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();
}
其實這樣就可以簡單的實現多線程了.