浅谈C# Socket编程及C#如何使用多线程

去年暑假学习了几个月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();

            }

其实这样就可以简单的实现多线程了.

posted @ 2011-01-18 21:08  与时俱进  阅读(11285)  评论(2编辑  收藏  举报
友情链接:同里老宅院民居客栈