C# Windows Sockets (Winsock) 接口 (转)

在.Net中,System.Net.Sockets 命名空间为须要严密控制网络访问的开发人员提供了 Windows Sockets (Winsock) 接口的托管实现。System.Net 命名空间中的全部其余网络访问类都创建在该套接字Socket实现之上,如TCPClient、TCPListener 和 UDPClient 类封装有关建立到 Internet 的 TCP 和 UDP 链接的详细信息;NetworkStream类则提供用于网络访问的基础数据流等,常见的许多Internet服务均可以见到Socket的踪迹,如Telnet、Http、Email、Echo等,这些服务尽管通信协议Protocol的定义不一样,可是其基础的传输都是采用的Socket。

其实,Socket能够象流Stream同样被视为一个数据通道,这个通道架设在应用程序端(客户端)和远程服务器端之间,然后,数据的读取(接收)和写入(发送)均针对这个通道来进行。


可见,在应用程序端或者服务器端建立了Socket对象以后,就可使用Send/SentTo方法将数据发送到链接的Socket,或者使用Receive/ReceiveFrom方法接收来自链接Socket的数据;

针对Socket编程,.NET 框架的 Socket 类是 Winsock32 API 提供的套接字服务的托管代码版本。其中为实现网络编程提供了大量的方法,大多数状况下,Socket 类方法只是将数据封送到它们的本机 Win32 副本中并处理任何须要的安全检查。若是你熟悉Winsock API函数,那么用Socket类编写网络程序会很是容易,固然,若是你未曾接触过,也不会太困难,跟随下面的解说,你会发觉使用Socket类开发windows 网络应用程序原来有规可寻,它们在大多数状况下遵循大体相同的步骤。

在使用以前,你须要首先建立Socket对象的实例,这能够经过Socket类的构造方法来实现:

public Socket(AddressFamily addressFamily,SocketType socketType,ProtocolType protocolType);


其中,addressFamily 参数指定 Socket 使用的寻址方案,socketType 参数指定 Socket 的类型,protocolType 参数指定 Socket 使用的协议。

下面的示例语句建立一个 Socket,它可用于在基于 TCP/IP 的网络(如 Internet)上通信。

Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);


若要使用 UDP 而不是 TCP,须要更改协议类型,以下面的示例所示:

Socket s = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);


一旦建立 Socket,在客户端,你将能够经过Connect方法链接到指定的服务器,并经过Send/SendTo方法向远程服务器发送数据,然后能够经过Receive/ReceiveFrom从服务端接收数据;而在服务器端,你须要使用Bind方法绑定所指定的接口使Socket与一个本地终结点相联,并经过Listen方法侦听该接口上的请求,当侦听到用户端的链接时,调用Accept完成链接的操做,建立新的Socket以处理传入的链接请求。使用完 Socket 后,记住使用 Shutdown 方法禁用 Socket,并使用 Close 方法关闭 Socket。其间用到的方法/函数有:

Socket.Connect方法:创建到远程设备的链接
public void Connect(EndPoint remoteEP)(有重载方法)
Socket.Send 方法:从数据中的指示位置开始将数据发送到链接的 Socket。
public int Send(byte[], int, SocketFlags);(有重载方法)
Socket.SendTo 方法 将数据发送到特定终结点。
public int SendTo(byte[], EndPoint);(有重载方法)
Socket.Receive方法:将数据从链接的 Socket 接收到接收缓冲区的特定位置。
public int Receive(byte[],int,SocketFlags);
Socket.ReceiveFrom方法:接收数据缓冲区中特定位置的数据并存储终结点。
public int ReceiveFrom(byte[], int, SocketFlags, ref EndPoint);
Socket.Bind 方法:使 Socket 与一个本地终结点相关联:
public void Bind( EndPoint localEP );
Socket.Listen方法:将 Socket 置于侦听状态。
public void Listen( int backlog );
Socket.Accept方法:建立新的 Socket 以处理传入的链接请求。
public Socket Accept();
Socket.Shutdown方法:禁用某 Socket 上的发送和接收
public void Shutdown( SocketShutdown how );
Socket.Close方法:强制 Socket 链接关闭
public void Close();


能够看出,以上许多方法包含EndPoint类型的参数,在Internet中,TCP/IP 使用一个网络地址和一个服务端口号来惟一标识设备。网络地址标识网络上的特定设备;端口号标识要链接到的该设备上的特定服务。网络地址和服务端口的组合称为终结点,在 .NET 框架中正是由 EndPoint 类表示这个终结点,它提供表示网络资源或服务的抽象,用以标志网络地址等信息。.Net同时也为每一个受支持的地址族定义了 EndPoint 的子代;对于 IP 地址族,该类为 IPEndPoint。IPEndPoint 类包含应用程序链接到主机上的服务所需的主机和端口信息,经过组合服务的主机IP地址和端口号,IPEndPoint 类造成到服务的链接点。

用到IPEndPoint类的时候就不可避免地涉及到计算机IP地址,.Net中有两种类能够获得IP地址实例:

IPAddress类:IPAddress 类包含计算机在 IP 网络上的地址。其Parse方法可将 IP 地址字符串转换为 IPAddress 实例。下面的语句建立一个 IPAddress 实例:

IPAddress myIP = IPAddress.Parse("192.168.1.2");


Dns 类:向使用 TCP/IP Internet 服务的应用程序提供域名服务。其Resolve 方法查询 DNS 服务器以将用户友好的域名(如"host.contoso.com")映射到数字形式的 Internet 地址(如 192.168.1.1)。Resolve方法 返回一个 IPHostEnty 实例,该实例包含所请求名称的地址和别名的列表。大多数状况下,可使用 AddressList 数组中返回的第一个地址。下面的代码获取一个 IPAddress 实例,该实例包含服务器 host.contoso.com 的 IP 地址。

IPHostEntry ipHostInfo = Dns.Resolve("host.contoso.com");
IPAddress ipAddress = ipHostInfo.AddressList[0];


你也可使用GetHostName方法获得IPHostEntry实例:

IPHosntEntry hostInfo=Dns.GetHostByName("host.contoso.com")


在使用以上方法时,你将可能须要处理如下几种异常:

SocketException异常:访问Socket时操做系统发生错误引起

ArgumentNullException异常:参数为空引用引起

ObjectDisposedException异常:Socket已经关闭引起

在掌握上面得知识后,下面的代码将该服务器主机( host.contoso.com的 IP 地址与端口号组合,以便为链接建立远程终结点:

IPEndPoint ipe = new IPEndPoint(ipAddress,11000);


肯定了远程设备的地址并选择了用于链接的端口后,应用程序能够尝试创建与远程设备的链接。下面的示例使用现有的 IPEndPoint 实例与远程设备链接,并捕获可能引起的异常:

try {
s.Connect(ipe);//尝试链接
}
//处理参数为空引用异常
catch(ArgumentNullException ae) {
Console.WriteLine("ArgumentNullException : {0}", ae.ToString());
}
//处理操做系统异常
catch(SocketException se) {
Console.WriteLine("SocketException : {0}", se.ToString());
}
catch(Exception e) {
Console.WriteLine("Unexpected exception : {0}", e.ToString());
}


须要知道的是:Socket 类支持两种基本模式:同步和异步。其区别在于:在同步模式中,对执行网络操做的函数(如 Send 和 Receive)的调用一直等到操做完成后才将控制返回给调用程序。在异步模式中,这些调用当即返回。

另外,不少时候,Socket编程视状况不一样须要在客户端和服务器端分别予以实现,在客户端编制应用程序向服务端指定端口发送请求,同时编制服务端应用程序处理该请求,这个过程在上面的阐述中已经说起;固然,并不是全部的Socket编程都须要你严格编写这两端程序;视应用状况不一样,你能够在客户端构造出请求字符串,服务器相应端口捕获这个请求,交由其公用服务程序进行处理。如下事例语句中的字符串就向远程主机提出页面请求:

string Get = "GET / HTTP/1.1\r\nHost: " + server + "\r\nConnection: Close\r\n\r\n";


远程主机指定端口接受到这一请求后,就可利用其公用服务程序进行处理而不须要另行编制服务器端应用程序。

综合运用以上阐述的使用Visual C#进行Socket网络程序开发的知识,下面的程序段完整地实现了Web页面下载功能。用户只需在窗体上输入远程主机名(Dns 主机名或以点分隔的四部分表示法格式的 IP 地址)和预保存的本地文件名,并利用专门提供Http服务的80端口,就能够获取远程主机页面并保存在本地机指定文件中。若是保存格式是.htm格式,你就能够在Internet浏览器中打开该页面。适当添加代码,你甚至能够实现一个简单的浏览器程序。


实现此功能的主要源代码以下:

C# code
//"开始"按钮事件 private void button1_Click(object sender, System.EventArgs e) { //取得预保存的文件名string fileName=textBox3.Text.Trim(); //远程主机 string hostName=textBox1.Text.Trim(); //端口 intport=Int32.Parse(textBox2.Text.Trim()); //获得主机信息 IPHostEntry ipInfo=Dns.GetHostByName(hostName); //取得IPAddress[] IPAddress[] ipAddr=ipInfo.AddressList; //获得ip IPAddress ip=ipAddr[0]; //组合出远程终结点 IPEndPoint hostEP=new IPEndPoint(ip,port); //建立Socket 实例 Socket socket=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp); try {//尝试链接 socket.Connect(hostEP); } catch(Exception se) { MessageBox.Show("链接错误"+se.Message,"提示信息 ,MessageBoxButtons.RetryCancel,MessageBoxIcon.Information); } //发送给远程主机的请求内容串string sendStr="GET / HTTP/1.1\r\nHost: " + hostName + "\r\nConnection: Close\r\n\r\n"; //建立bytes字节数组以转换发送串 byte[] bytesSendStr=new byte[1024]; //将发送内容字符串转换成字节byte数组bytesSendStr=Encoding.ASCII.GetBytes(sendStr); try { //向主机发送请求socket.Send(bytesSendStr,bytesSendStr.Length,0); } catch(Exception ce) { MessageBox.Show("发送错误:"+ce.Message,"提示信息 ,MessageBoxButtons.RetryCancel,MessageBoxIcon.Information); } //声明接收返回内容的字符串 string recvStr=""; //声明字节数组,一次接收数据的长度为1024字节 byte[] recvBytes=newbyte[1024]; //返回实际接收内容的字节数 int bytes=0; //循环读取,直到接收完全部数据 while(true) { bytes=socket.Receive(recvBytes,recvBytes.Length,0); //读取完成后退出循环 if(bytes<=0) break; //将读取的字节数转换为字符串 recvStr+=Encoding.ASCII.GetString(recvBytes,0,bytes); } //将所读取的字符串转换为字节数组 byte[] content=Encoding.ASCII.GetBytes(recvStr); try { //建立文件流对象实例 FileStream fs=newFileStream(fileName,FileMode.OpenOrCreate,FileAccess.ReadWrite); //写入文件fs.Write(content,0,content.Length); } catch(Exception fe) { MessageBox.Show("文件建立/写入错误:"+fe.Message,"提示信息",MessageBoxButtons.RetryCancel,MessageBoxIcon.Information); } //禁用Socket socket.Shutdown(SocketShutdown.Both); //关闭Socket socket.Close(); } }
 



程序在WindowsXP中文版、.Net Frameworkd 中文正式版、Visual Studio.Net中文正式版下调试经过编程

 

 

 

////////////////////////////////////////windows

 

 

  对于TCP的Socket编程,主要分二部分:
      1、服务端Socket侦听:
      服务端Socket侦听主要分如下几个步骤,按照如下几个步骤咱们能够很方便的创建起一个Socket侦听服务,来侦听尝试链接到该服务器的客户Socket,从而创建起链接进行相关通信。
      一、建立IPEndPoint实例,用于Socket侦听时绑定
         数组

1 IPEndPoint ipep = new IPEndPoint(IPAddress.Any, 6001);


      二、建立套接字实例浏览器

1 //建立一个套接字
2             serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

  
      这里建立的时候用ProtocolType.Tcp,表示创建一个面向链接(TCP)的Socket。

      三、将所建立的套接字与IPEndPoint绑定

安全

1  //将所建立的套接字与IPEndPoint绑定
2             serverSocket.Bind(ipep);


      四、设置套接字为收听模式
 服务器

1 //设置套接字为收听模式
2             serverSocket.Listen(10);


      以上这四步,咱们已经创建了Socket的侦听模式,下面咱们就来设置怎么样来获取客户Socket链接的实例,以及链接后的信息发送。

      五、在套接字上接收接入的链接
  网络

 1 while (true)
 2              {
 3                try
 4                {
 5                    //在套接字上接收接入的链接
 6                    clientSocket = serverSocket.Accept();
 7                    clientThread = new Thread(new ThreadStart(ReceiveData));
 8                    clientThread.Start();
 9                }
10                catch (Exception ex)
11                {
12                    MessageBox.Show("listening Error: " + ex.Message);
13                }
14            }


      经过serverSocket.Accept()来接收客户Socket的链接请求,在这里用循环能够实现该线程实时侦听,而不是只侦听一次。当程序运行serverSocket.Accept()时,会等待,直到有客户端Socket发起链接请求时,获取该客户Socket,如上面的clientSocket。在这里我用多线程来实现与多个客户端Socket的链接和通讯,一旦接收到一个链接后,就新建一个线程,执行ReceiveData功能来实现信息的发送和接收。

      六、 在套接字上接收客户端发送的信息和发送信息多线程

 1 private void ReceiveData()
 2          {
 3            bool keepalive = true;
 4            Socket s = clientSocket;
 5            Byte[] buffer = new Byte[1024];
 6
 7            //根据收听到的客户端套接字向客户端发送信息
 8            IPEndPoint clientep = (IPEndPoint)s.RemoteEndPoint;
 9            lstServer.Items.Add("Client:" + clientep.Address + "("+clientep.Port+")");
10            string welcome = "Welcome to my test sever ";
11            byte[] data = new byte[1024];
12            data = Encoding.ASCII.GetBytes(welcome);
13            s.Send(data, data.Length, SocketFlags.None);
14
15            while (keepalive)
16            {
17                //在套接字上接收客户端发送的信息
18                int bufLen = 0;
19                try
20                {
21                    bufLen = s.Available;
22
23                    s.Receive(buffer, 0, bufLen, SocketFlags.None);
24                    if (bufLen == 0)
25                        continue;
26                }
27                catch (Exception ex)
28                {
29                    MessageBox.Show("Receive Error:" + ex.Message);
30                    return;
31                }
32                clientep = (IPEndPoint)s.RemoteEndPoint;
33                string clientcommand = System.Text.Encoding.ASCII.GetString(buffer).Substring(0, bufLen);
34
35                lstServer.Items.Add(clientcommand + "("+clientep.Address + ":"+clientep.Port+")");
36
37            }
38            
39        }

 


      经过IPEndPoint clientep = (IPEndPoint)s.RemoteEndPoint;咱们能够获取链接上的远程主机的端口和IP地址,若是想查询该主机的其它属性如主机名等,可用于上一篇讲的Dns.GetHostByAddress(string ipAddress)来返回一个IPHostEntry对象,就能够获得。另外咱们要注意的是,经过Socket发送信息,必需要先把发送的信息转化成二进字进行传输,收到信息后也要把收到的二进字信息转化成字符形式,这里能够经过Encoding.ASCII.GetBytes(welcome);和Encoding.ASCII.GetString(buffer).Substring(0, bufLen);来实现。框架

      以上就是服务端Socket侦听模式的实现,只要有远程客户端Socket链接上后,就能够轻松的发送信息和接收信息了。下面咱们来看看客户端Socket是怎么链接上服务器的。

      2、客户端链接

      客户端Socket链接相对来讲比较简单了,另外说明一下,在执行客户端链接前,服务端Socket侦听必须先启动,否则会提示服务器拒绝链接的信息。

      一、建立IPEndPoint实例和套接字异步

 

1  //建立一个套接字
2             IPEndPoint ipep = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 6001);
3             clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);


      这个跟服务端Socket侦听差很少,下面一步由服务端Socket的侦听模式变成链接模式。

      二、将套接字链接到远程服务器

 1 //将套接字与远程服务器地址相连
 2             try
 3              {
 4                clientSocket.Connect(ipep);
 5            }
 6            catch (SocketException ex)
 7            {
 8                MessageBox.Show("connect error: " + ex.Message);
 9                return;
10            }


      前面已说明,若是在执行Socket链接时,服务器的Socket侦听没有开启的话,会产生一个SocketException异常,若是没有异常发生,那恭喜你,你已经与服务器链接上了,接下来就能够跟服务器通讯了。
  
      三、接收信息

 1 while (true)
 2              {
 3                //接收服务器信息
 4                int bufLen = 0;
 5                try
 6                {
 7                    bufLen = clientSocket.Available;
 8
 9                    clientSocket.Receive(data, 0, bufLen, SocketFlags.None);
10                    if (bufLen == 0)
11                    {
12                        continue;
13                    }
14                }
15                catch (Exception ex)
16                {
17                    MessageBox.Show("Receive Error:" + ex.Message);
18                    return;
19                }
20
21                string clientcommand = System.Text.Encoding.ASCII.GetString(data).Substring(0, bufLen);
22
23                lstClient.Items.Add(clientcommand);
24
25            }


      四、发送信息

1 //向服务器发送信息
2           
3             byte[] data = new byte[1024];
4             data = Encoding.ASCII.GetBytes(txtClient.Text);
5             clientSocket.Send(data, data.Length, SocketFlags.None);


      客户端的发送信息和接收信息跟服务器的接收发送是同样的,只不过一个是侦听模式而另外一个是链接模式。

      如下是程序的运行界面,这些在源码下载里均可以看到:

      一、服务端界面:
      
      
      二、客户端界面:
      

      好了,关于面向链接的Socket就讲到这里了,以实例为主,但愿对那些派得上用场的朋友可以看得明白。另外提一下,这里服务端开启侦听服务、客户端链接服务端都采用线程方式来实现,这样服务端可以跟多个客户端同时通讯,不用等候,固然还有另一种方式能够实现那就是异步socket,关于这些能够搜索博客园上的相关文章,已经有好多了。

Stop talking and create the future !

相关文章
相关标签/搜索