Socket是什么?
Socket是应用层与TCP/IP协议通信的中间软件抽象层,其实Socket就是一组接口。其实Socket就是把TCP/IP协议族隐藏在Socket接口后面,对程序员来说,可能你不懂这些复杂的TCP/IP协议族,那么实现接口就可以进行简单的Socket通信了,Socket会去组织数据,以符合指定的协议。
一个生活中的场景:你需要和一个朋友进行通话,你就要拨号,通过运营商接通到你朋友的手机,你朋友听到电话铃声并接听电话,这样你和你的朋友就建立了连接,你们就可以进行通信了。其实Socket通信原理也是如此。我们先看下图:
这有一个服务器端和一个客户端,服务器端就相当于你,客户端就相当与你的朋友。服务器端初始化Socket,然后与端口绑定(bind),然后对端口监听(listen),调用accept进行阻塞,等待客户端的连接。当你的朋友接通电话时,就相当与客户端初始化了一个Socket,然后连接(connect)服务器,如果连接成功,这是客户端与服务器端就建立的连接。客户端发送发送了数据,服务器端接受并处理请求,然后把回应数据返还个客户端,客户端读取数据后,然后关闭连接,一次连接就结束了。
C#中的Socket编程
常用的类和方法
在进行编写代码之前,我们先看几个常用的类和方法,这会使我们的学习事半功倍。
IPAddress类
这是一个IP地址操作类,构造一个IP地址对象IPAddress的构造函数是 IPAddress(long adress),但是通常情况下我们使用该类中的Parse( )方法,可以把点分的十进制IP表示转化成IPAddress类。如:IPAddress address = IPAddress.Parse(“127.0.0.1”);
另外还有一种初始化IP地址的方法,该类中提供的只读字段IPAddress.Any: 使用你机器上热河一个可用ip来初始化这个IP地址对象。IPAddress中还提供了另外3个只读字段:
IPAddress.Broadcase //用于代表本地网络的IP广播地址
IPAddress.Loopback 用于代表系统的回送地址
IPAddress.None 用于代表系统上没有网络接口
- IPEndPoint类
IPEndPoint其实就是一个IP地址和端口的绑定,可以代表一个服务,用来Socket通讯。
我们可以通过2种构造方法来创建IPEndPoint类:
IPEndPoint(long address, int port)
IPEndPoint(IPAddress address, int port)
- DNS相关类
DNS类有四个静态方法,来获取主机DNS相关信息,
- GetHostName()
通过Dns.GetHostName()可以获得本地计算机的主机名
- GetHostByName()
根据主机名称,返回一个IPHostEntry 对象:
IPHostEntry GetHostByName(string hostName)
其中IPHostEntry把一个DNS主机名与一个别名和IP地址的数组相关联,包含三个属性:
AddressList:一个IPAddress对象的数组
Aliases:一个字符串对象数组
HostName:一个用于主机名的字符串对象
GetHostByAddress()
类似于GetHostByName(),只不过这里的参数是IP地址,而不是主机名,也返回一个IPHostEntry对象。IPHostEntry GetHostByAddress(IPAddress address)
IPHostEntry GetHostByAddress(string address)Resolve()
当我们不知道输入的远程主机的地址是哪种格式(主机名或IP地址)时,用以上二种方法来实现,我们可能还要通过判断客户输入的格式,才能正确使用,但dns类提供一更简单的方法Resolve(),该方法可以接受主机名格式或IP地址格式的任何一种地址,并返回IPHostEntry对象。
Socket编程
服务器端
就如我们上面所说,服务器端创建Socket连接的步骤有:初始化Socket,然后与端口绑定(bind),然后对端口监听(listen),调用accept进行阻塞,等待客户端的连接。
namespace Server
{
class Server
{
static void Main ( string[] args )
{
int port = 6001;
string host = "127.0.0.1";
IPAddress ipAddress = IPAddress.Parse ( host );
//创建IPEndPoint实例,用于Scoket监听
IPEndPoint ipep = new IPEndPoint ( ipAddress, port );
//创建套接字实例
//这里使用ProtocolType.Tcp,表示建立一个面向连接的Scoket.如果要用UDP协议要用SocketType.Dgram
Socket serverSocket = new Socket ( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp );
//将所创建的套接字与IPEndPoint绑定
serverSocket.Bind ( ipep );
//设置套接字为监听模式,并且挂起队列最长为10
serverSocket.Listen ( 10 );
Console.WriteLine ( "等待客户端连接" );
//接收到client连接,为此链接创建新的socket,并接收信息
Socket tempSocket = serverSocket.Accept ( );
Console.WriteLine ( "建立连接");
while (tempSocket.Connected)
{
string recvString = "";
byte[] recvByte = new byte[1024];
int byteCount;
byteCount = tempSocket.Receive ( recvByte, recvByte.Length, 0 );
recvString += Encoding.ASCII.GetString ( recvByte, 0, byteCount );//将字节编码解释为字符串
//给client返回信息
Console.WriteLine ( "服务器得到的信息:{0}", recvString );
string sendString = "got message";
byte[] bytesSend = Encoding.ASCII.GetBytes ( sendString );
tempSocket.Send ( bytesSend, bytesSend.Length, 0 );//返回客户端信息
}
tempSocket.Close ( );
serverSocket.Close ( );
Console.ReadLine ( );
}
}
}
通过serverSocket.Accept()来接收客户Socket的连接请求,在这里用循环可以实现该线程实时侦听,而不是只侦听一次。当程序运行serverSocket.Accept()时,会等待,直到有客户端Socket发起连接请求时,获取该客户Socket。
客户端
同样的客户端初始化一个Socket,然后连接(connect)服务器,如果连接成功,这是客户端与服务器端就建立的连接。客户端发送发送了数据,服务器端接受并处理请求,然后把回应数据返还个客户端,客户端读取数据。
namespace Client
{
class Client
{
static void Main ( string[] args )
{
try
{
int port = 6001;
string host = "127.0.0.1";
IPAddress ip = IPAddress.Parse ( host );
IPEndPoint ipe = new IPEndPoint ( ip, port );
//创建socket并连接服务器
Socket clientSocket = new Socket ( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp );
Console.WriteLine ( "connect..." );
clientSocket.Connect ( ipe );
int count = 0;
do
{
//向服务器发送信息
string sendString = Console.ReadLine ( );//"this is a test";
byte[] sendByte = Encoding.ASCII.GetBytes ( sendString );
Console.WriteLine ( "send message" );
clientSocket.Send ( sendByte, sendByte.Length, 0 );
//接受服务器返回的信息
string recvString = "";
byte[] recvByte = new Byte[1024];
int bytesCount;
bytesCount = clientSocket.Receive ( recvByte, recvByte.Length, 0 );
recvString += Encoding.ASCII.GetString ( recvByte, 0, bytesCount );
Console.WriteLine ( "client get : {0}", recvString );
count ++;
}while(count < 10);
clientSocket.Close ( );
}
catch (ArgumentNullException e)
{
Console.WriteLine ( "argumentNullException:{0}", e );
}
catch (SocketException e) {
Console.WriteLine ("SocketException:{0}",e);
}
Console.ReadLine ( );
}
}
}