去年暑假學習了幾個月asp.net 最後幾個星期弄了下C# socket .也算知道了個只知其一;不知其二了, 很久沒動C#了, 雖然這語言高級的讓我對他沒興趣, 不過仍是回憶回憶, 忘了一乾二淨就很差了.html
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++去了,就把這些忘了差很少.asp.net
首先必須包含的兩個命名空間:socket
Using System.Net;ide
Using System.Net.Sockets;函數
幾個經常使用的類:(這些東西,查下MSDN就很詳細了)學習
IPHostEntry, Dns,IPAddress,IPEndPoint,還有最重要的Socketthis
IPEndPoint: 這個是網絡終結點,很好理解,就是網絡上一個固定的地址:一個IP與一個端口的組合.
下面我仍是以我之前寫的一個很簡單的聊天程序作示例吧, (很短代碼的)
Form1.cs
//說明下, 這個是集Server與Client與一體的.
usingSystem;
usingSystem.Collections.Generic;
usingSystem.ComponentModel;
usingSystem.Data;
usingSystem.Drawing;
usingSystem.Text;
usingSystem.Windows.Forms;
usingSystem.Net;
usingSystem.Net.Sockets; //這個和上面的是使用Socket必須的.
usingSystem.IO;
usingSystem.Threading; //這個是使用多線程必需的.
namespaceOnlySocket
{
publicpartialclassForm1 : Form //partial表示這塊代碼只是Form1類的部分, Form1類繼承自Form類
{
publicForm1()
{
InitializeComponent(); //構造函數, 初始化容器.
}
Socket sock; //定義一個Socket類的對象 (默認爲protected)
Thread th; //定義一個Thread類的對象
//
publicstaticIPAddress GetServerIP() //靜態函數, 無需實例化便可調用.
{
IPHostEntry ieh =Dns.GetHostByName(Dns.GetHostName()); //很少說了, Dns類的兩個靜態函數
//或用DNS.Resolve()代替GetHostName()
returnieh.AddressList[0]; //返回Address類的一個實例. 這裏AddressList是數組並不奇怪,一個Server有N個IP都有可能
}
privatevoidBeginListen() //Socket監聽函數, 等下做爲建立新線程的參數
{
IPAddress serverIp =GetServerIP(); //調用本類靜態函數GetServerIP獲得本機IPAddress.
IPEndPoint iep =newIPEndPoint(serverIp, Convert.ToInt32(tbPort.Text)); //本地終結點
sock =newSocket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); //實例化內成員sock
Byte[] byteMessage =newByte[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發送過來的數據保存到緩衝區.
stringmsg ="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(); //將其顯示出來, 在此亦能夠自定義錯誤.
}
}
}
privatevoidbtConnect_Click(objectsender, EventArgs e) //鏈接按鈕觸發的事件: 鏈接Server
{
btConnect.Enabled =false;
btStopConnect.Enabled =true;
try
{
th =newThread(newThreadStart(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);
}
}
privatevoidbtStopConnect_Click(objectsender, EventArgs e) //中止監聽
{
btStopConnect.Enabled =false;
btConnect.Enabled =true;
sock.Close(); //關閉套接字
th.Abort(); //終止監聽線程
lbState.Text ="Listenning stopped";
}
privatevoidbtExit_Click(objectsender, EventArgs e)
{
sock.Close();
th.Abort();
Dispose(); //清理資源,就是釋放內存
this.Close(); //關閉對話框, 退出程序
}
privatevoidbtSend_Click(objectsender, EventArgs e)
{
try
{
IPAddress clientIp =IPAddress.Parse(tbTargetIp.Text); //類IPAddress的靜態函數Parse() :將Text轉化爲IPAddress的一個實例.
intclientPort =Convert.ToInt32(tbPort.Text); //C#的這些轉化函數很方便的,不像C++那樣麻煩
IPEndPoint clientIep =newIPEndPoint(clientIp, clientPort); //這裏用client表示不是很好....,
Byte[] byte_Message;
Socket socket =newSocket(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
usingSystem;
usingSystem.Collections.Generic;
usingSystem.Windows.Forms;
namespaceOnlySocket
{
staticclassProgram
{
///<summary>
///應用程序的主入口點。
///</summary>
[STAThread]
staticvoidMain() //這兒纔是main函數
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(newForm1());
}
}
}
寫了半天了, 夠累的了, 雖然都是很基礎的東西, 我本身寫的時候也複習了一邊 , 呵呵.
其實多線程我本身也不是很熟練, 記得去年暑假寫過一個多線程掃描器, 也不知爲啥, 線程開到50以上就異常, 很鬱悶的. 其實當時我就是用的new Thread=Thread(new ThreadStart(Fun))實現的, 方法感受很笨拙,呵呵.
大體代碼好像是這樣的吧:
先寫個Scan類:
publicclassScan
{
try{ publicScan(){ ...Init... }
publicvoidScan{ ..task循環掃描... } //task結構體裏面有IP, 端口, 是否已掃描標記fLag
}
catch{}
}
而後主函數裏面能夠這樣搞:
Scan[] scanner =newScan[XX]
Thread[] thread =newThread[XX];
for(inti =0; i <XX;i++)
{
scanner[i] =newScan(this, i);
thread[i] =newThread(newThreadStart(scanner[i].StartScan));
thread[i].Start();
}
其實這樣就能夠簡單的實現多線程了.