7-3-3 Socket类
在前面的例子中,我们还使用了Socket类,即套接字类。套接字是支持TCP/IP协议的网络通信的基本操作单元。可以将套接字看作不同主机间的进程进行双向通信的端点(EndPoint)。在通信时,至少需要一对套接字,其中一个运行于客户端,另一个运行于服务器端。一个Socket实例包含了一个本地或者一个远程端点的套接字信息,Socket类的主要成员,如下表7-8所示:
方法 |
说明 |
Connect |
建立与远程主机的连接 |
|
关闭 Socket 连接并释放所有关联的资源。 |
|
将 Socket 置于侦听状态。 |
|
接收来自绑定的 Socket 的数据。 |
|
将数据发送到连接的 Socket。 |
属性 |
说明 |
|
获取 Socket 的地址族。 |
|
获取已经从网络接收且可供读取的数据量。 |
Connected |
获取一个值,该值表明套接字是否与最后完成发送或接收操作的远程设备得到连接 |
|
获取 Socket 的协议类型。 |
|
获取远程终结点。 |
|
获取或设置一个值,该值指定之后同步 Send 调用将超时的时间长度。 |
|
获取 Socket 的类型。 |
|
获取或设置一个值,它指定 Socket 接收缓冲区的大小。 |
表7-8 Socket类
Socket类的构造函数为:
public Socket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType);其中几个参数均为枚举类型,可按需要选择不同类型。在网络连接时,某些方法均提供了相应的异步方法,可避免等待过程较长的情况无响应。
1. 案例学习:使用套接字的通信程序
本次实验目标是掌握Socket类的基本方法,同样需要建立两个应用程序,第一个是服务端,启动监听;第二个是客户端,向服务端请求连接。
u 实验步骤(1):
创建服务端控制台应用程序Server,运行中显示连接的个数,对方地址及端口号。
u 实验步骤(2):添加功能源代码:
static void Main(string[] args)
{
int recvLength;//用于表示客户端发送的信息长度
byte[] data=new byte[1024];//用于缓存客户端所发送的信息,通过socket传递的信息必须为字节数组
IPEndPoint iend=new IPEndPoint(IPAddress.Any,9999);//本机预使用的IP和端口
Socket newsock=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
newsock.Bind(iend);
newsock.Listen(10);
Console.WriteLine("waiting for a client ");
Socket client=newsock.Accept();
//当有可用的客户端连接尝试时执行,并返回一个新的socket,用于与客户端之间的通信
IPEndPoint clientAdd=(IPEndPoint)client.RemoteEndPoint;
Console.WriteLine("连接:"+clientAdd.Address+" 端口:"+clientAdd.Port);
string welcome="welcome here!";
data=Encoding.ASCII.GetBytes(welcome);
client.Send(data,data.Length,SocketFlags.None);//发送信息
while(true)
{
data=new byte[1024];
recvLength=client.Receive(data);
if (recvLength==0)//当信息长度为0,说明客户端连接断开
break;
Console.WriteLine(Encoding.ASCII.GetString(data,0,recvLength));
client.Send(data,recvLength,SocketFlags.None);
}
Console.WriteLine("断开连接"+clientAdd.Address);
client.Close();
newsock.Close();
}
图7-6 使用Socket通信(服务端)
u实验步骤(3):
创建客户端控制台应用程序Client,输入向服务端发送的数据:
static void Main(string[] args)
{
byte[] data=new byte[1024];
Socket newclient=new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);
Console.Write("输入服务器地址:");
string ipadd=Console.ReadLine();
Console.WriteLine();
Console.Write("输入服务器端口:");
int port=Convert.ToInt32(Console.ReadLine());
IPEndPoint ie=new IPEndPoint(IPAddress.Parse(ipadd),port);//服务器的IP和端口
try
{
//因为客户端只是用来向特定的服务器发送信息,所以不需要绑定本机的IP和端口。不需要监听。
newclient.Connect(ie);
}
catch(SocketException e)
{
Console.WriteLine("不能连接");
Console.WriteLine(e.ToString());
return;
}
int recv = newclient.Receive(data);
string stringdata=Encoding.ASCII.GetString(data,0,recv);
Console.WriteLine(stringdata);
while(true)
{
string input=Console.ReadLine();
if(input=="exit")
break;
newclient.Send(Encoding.ASCII.GetBytes(input));
data=new byte[1024];
recv=newclient.Receive(data);
stringdata=Encoding.ASCII.GetString(data,0,recv);
Console.WriteLine(stringdata);
}
Console.WriteLine("断开连接 ");
newclient.Shutdown(SocketShutdown.Both);
newclient.Close();
}
图7-7 使用Socket通信(客户端)
这里的运行结果与前面类似,区别在于内部实现完全使用套接字进行通信而没有使用Tcp相关类。在这个例子中,启动监听时设定了最大连接数:sock.Listen(10);可以启动多个连接。
问题讨论:
1、While(true){}?
不断接收客户端的数据流实现消息的自动接收,直到网络流中没有数据则退出循环。