C#.NET网络程序开发的基本类(二)
一、Socket类
套接字是支持TCP/IP网络通信的基本操作单元。在一个套接字的实例中既保存了本机IP地址和端口,也保存了对方主机的IP地址和端口,同时还有双方通信的协议信息。
C#命名空间System.Net.Socket提供了Socket类,一个Socket实例包含了一个本地或者一个远程的套接字信息。
Socket可以向Stream流一样被视为数据通信,这个通道存在于服务器和客户端之间,数据的发送和接收均对于这个通道进行。在应用程序创建Socket对象之后就可以用Send/SendTo方法将数据发送到连接的Socket中,或者使用Receive/ReceiveFrom方法接收连接的Socket数据。
二、套接字的类型与使用方法
1、Socket类的类型
套接
字有3种类型,即流套接字、数据报套接字和原始套接字。
(1)流套接字。用来实现TCP通信,提供了面向连接的、可靠的、数据无错且无重复的数据传输服务,并且发送和接受数据的顺序是相同的。
(2)数据报套接字。用来实现UDP通信,提供了面向无连接的服务,它以独立的数据报形式发送数据(数据包的长度不能大于32kB),不提供正确性检查,也不保证各数据包的发送和接收顺序,所以可能出现数据重发、丢失等现象。
(3)原始套接字。原始套接字用来实现IP数据包通信,用于直接访问协议的较低层,常用于侦听及分析数据包,广泛应用于高级网络编程,也是黑客经常使用的一种手段。
Socket的构造函数:
public Socket(AddressFamily addressFamily,SocketType socketType,ProtocolType protocolType);
功能:新实例初始化 Socket 类使用指定的地址族、 套接字类型和协议。
参数含义:
- addressFamily:addressFamily指网络类型,该参数使用AddressFamily枚举指定Socket使用的寻址方案,其中,
AddressFamily.InterNetWork表示IPv4的地址,AddressFamily.InterNetWorkV6表示IPv6的地址.
- socketType和protocolType:这两个枚举类型的参数必须对应,共同指明Socket使用哪种协议的哪种套接字。
套接字类型与协议对应关系:创建套接字实例:Socket socket=new Socket(AddressFamily.InterNetWork, SocketType.stream, ProtocolType.Tcp);
表示创建基于TCP协议的IPv4流套接字。
2、Socket类的常用属性
AddressFamily 获取的地址族 Socket。 Available 获取已经从网络接收且可供读取的数据量。 Blocking 获取或设置一个值,该值指示是否 Socket 处于阻塞模式。 Connected 获取一个值,该值指示是否 Socket 连接到远程主机从上次以来 Send 或 Receive 操作。 LocalEndPoint 获取本地终结点。 ProtocolType 获取的协议类型 Socket。 RemoteEndPoint 获取远程终结点。 SocketType 获取 Socket 的类型。3、Socket类的常用方法
(1)void Connect(IPEndPoint remoteIcp)
该方法客户机独有,通过远程设备的套接字建立与远程设备的连接。
(2)int Send()/int Receive()
这两个方法在完成客户端的连接后将数据发送到连接到的Socket上以及将数据从连接的Socket上接收到缓冲区的指定位置。
当Receive方法没有可读的数据时,将一直处于阻止状态。
(3)void Bind(IPEndPoint remoteIcp)
使Socket与本地IP地址和端口号关联。
(4)void Listen(int backlog)
该方法用于等待客户端发出连接请求,其中的backlog为用户的最大连接数,超过该数值的其他客户不能与服务器进一步通信。
(5)Socket Accept()
该方法创建新的Socket以处理连接请求。当程序执行到该方法时会处于阻塞状态,直到有新的客户机请求连接为止。该方法返回包含客户端信息的套接字句柄。
(6)void ShutDown()
该方法在通信完成后负责把连接释放,并关闭Socket对象。
(7)void Close()
该方法关闭远程主机连接,并释放所有与Socket相关联的资源。关闭后,Connected属性将被设置为false。对于面向
连接的协议,先调用Shutdown,然后调用Close方法,以确保在已连接的套接字关闭之前已经发送和接收该套接字上的所有数据。
实例1、编写控制台程序,利用同步的面向连接Socket实现客户端和服务器的消息通信。
(1)编写服务器端程序:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net.Sockets; //添加Socket类空间 using System.Net; namespace Server_test { class Program { private static byte[] result = new Byte[1024]; private static int myport = 8012; static Socket serverSocket; static void Main(string[] args) { //服务器IP地址 IPAddress ip = IPAddress.Parse("127.0.0.1"); serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); serverSocket.Bind(new IPEndPoint(ip, myport)); serverSocket.Listen(10); Console.WriteLine("启动监听{0}", serverSocket.LocalEndPoint.ToString()); //通过clientsocket发送数据 string sendMessage = "server send Message Hello"; Socket clientsocket = serverSocket.Accept(); clientsocket.Send(Encoding.ASCII.GetBytes(sendMessage)); Console.WriteLine("向客户端发送消息:{0}", sendMessage); //通过clientsocket接收数据 int receiveNumber = clientsocket.Receive(result); Console.WriteLine("接收客户端{0}消息{1}", clientsocket.RemoteEndPoint.ToString(), Encoding.ASCII.GetString(result, 0, receiveNumber)); clientsocket.Shutdown(SocketShutdown.Both); clientsocket.Close(); Console.ReadLine(); } } }
(2)编写客户端程序:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net.Sockets; //添加Socket类空间 using System.Net; namespace Client_test { class Program { private static byte[] result = new Byte[1024]; static void Main(string[] args) { //服务器IP地址 IPAddress ip = IPAddress.Parse("127.0.0.1"); Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); try { clientSocket.Connect(new IPEndPoint(ip, 8012)); Console.WriteLine("连接服务器成功"); } catch { Console.WriteLine("连接服务失败,请按回车键退出"); return; } //通过clientSocket接收数据 int receiveLength = clientSocket.Receive(result); Console.WriteLine("接收服务器消息:{0}", Encoding.ASCII.GetString(result, 0, receiveLength)); //通过clientSocket发送数据 string sendMessage = "client send Message Hello"; clientSocket.Send(Encoding.ASCII.GetBytes(sendMessage)); Console.WriteLine("向服务器发送消息:{0}", sendMessage); clientSocket.Shutdown(SocketShutdown.Both); clientSocket.Close(); Console.ReadLine(); } } }
程序运行结果:
示例2、无连接套接字
无连接的套接字使用UDP协议,不需要像面向连接的套接字那样发送连接消息,即没有使用Connect方法进行连接的步骤,发送进程直接使用SendTo方法进行数据发送;但是如果一个进程是等待远程设备的信息,则套接字必须用Bind方法绑定到一个本地“IP地址/端口”上,完成绑定后才能使用ReceiveFrom方法接收数据。
编程:接收方程序:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net; //add net namespace
using System.Net.Sockets;
namespace Recv_Test
{
class Program
{
private static int receivePort = 8012;
static void Main(string[] args)
{
IPAddress ip = IPAddress.Parse("127.0.0.1");
//接收准备
Socket receiveSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
receiveSocket.Bind(new IPEndPoint(ip, receivePort));
//接收数据
byte[] result = new Byte[1024];
EndPoint senderRemote = (EndPoint)(new IPEndPoint(IPAddress.Any, 0));
//引用类型参数为EndPoint类型,用于存放发送方的IP地址和端点
int length = receiveSocket.ReceiveFrom(result, ref senderRemote);
Console.WriteLine("接收到{0}消息:{1}", senderRemote.ToString(), Encoding.UTF8.GetString(result, 0, length).Trim());
receiveSocket.Shutdown(SocketShutdown.Receive);
Console.ReadLine();
}
}
}
发送方程序
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net; //add net namespace using System.Net.Sockets; namespace Send_Test { class Program { private static int remoteReceivePort = 8012; static void Main(string[] args) { IPAddress ip = IPAddress.Parse("127.0.0.1"); //发送方:发送数据 Socket sendSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); sendSocket.SendTo(Encoding.UTF8.GetBytes("测试数据"), new IPEndPoint(ip, remoteReceivePort)); Console.WriteLine("发送测试数据"); sendSocket.Shutdown(SocketShutdown.Send); sendSocket.Close(); Console.ReadLine(); } } }
运行结果