1,关于Unix Socket
1.1 什么是Socket?
Socket是网络文件描述符。在基于Socket的编程技术中,用户不直接访问发送和接收包的网络接口设备,而是建立一个中间文件描述符来处理编程接口到网络的操作。
1.2 Socket包括哪些内容?
一个特殊的通信域,比如一个网络连接
一个特殊的通信类型,比如流或者数据报
一个特殊的协议,比如TCP或者UDP
1.3 Unix的Socket() C函数详细说明:
int socket(int domain, int type, int protocol)
domain的取值:
PF_UNIX Unix IPC通信
PF_INET IPV4通信
PF_INET6 IPV6
PF_IPX Novell IPX
PF_NETLINK Kernel用户接口驱动程序
PF_X25 X.25
PF_AX25
PF_ATMPVC ATM PVC
PF_APPLETALK AppleTalk协议
PF_PACKET 低级包接口
type的取值:
SOCK_STREAM 使用面向连接的通信包
SOCK_DGRAM 使用无连接的通信包
SOCK_SEQPACKET 使用有固定最大长度的面向连接的通信包
SOCK_RAW 使用原IP包
SOCK_RDM 使用不保证次序的可靠数据报
Protocol:
一般使用与type对应的默认协议,用0表示。
例如: int newsocket = socket(PF_INET, SOCK_STREAM, 0 );//使用TCP
1.4 使用面向连接的套接字
IP领域只有两种类型: connection-oriented, connectionless
使用面向连接的套接字,服务器和客户端需要如下通信方式:
Server Client
socket() socket()
bind()
listen()
accept() <---- connect()
recv() <---- send()
send() ----> recv()
close() <---> close()
1.5 使用无连接的套接字
SOCK_DGRAM使用UDP协议。通信方式如下:
UDP Server UDP Client
socket() socket()
bind()
recvform() <--- sendto()
sendto() ---> recvform()
close() close()
1.6 使用无阻塞的I/O方法
什么是阻塞?
比如使用recv(),如果函数接受不到数据,就会阻塞程序的继续执行。
如何防止阻塞?
使用fcntl()函数,把套接字设置为无阻塞模式。
int newsocket;
newsocket = socket(PF_INET, SOCK_STREAM, 0 );
fcntl( newsocket, F_SETEL, O_NONBLOCK );
以后使用recv()就不会阻塞了。
另一种方式是使用多路套接字select()
2 WinSock技术
总的说来,就是模仿Unix socket的实现。
2.1 WinSock下的函数和流程
Server Client
WSAstartup() WSAStartup()
WSASocket() WASSocket()
bind()
listen()
WSAAccept() <-- WSAConnect()
WSARecv() <-- WSASend()
WSASend() --> WSARecv()
close() <-> close()
WSACleanup() WSACleanup()
与Unix socket的最主要区别就是在最上面加了WSAStartup()函数,最后加了WSACleanup()函数。中间都是一样的。
int WSAStartup(WORD wversion, LPWSADATA lpWSAData)
第一个参数是版本,要求2.2 还是1.1.
函数成功以后,lpWSAData指向一个结构体,包括win sock的一些信息。
WSACleanup()函数用来释放winsocket库,这个函数之后,再调用任何socket函数,都会出错。
2.2 WinSock
2.2.1 采用类似Unix的方法
使用ioctlsocket()把socket设置为无堵塞的。
使用select()多路传输多个套接字。
2.2.2 使用WSAAsyncSelect()函数
WSAAsynSelect()扩展了Unix的select()函数,它允许Windows进行查询套接字的操作。创建的WSAAsynSelect()方法包括要监视的套接字和一个Windows消息值,当某个套接字出现的时候,该消息就会送到窗口。
int WSAAsynSelect(Socket s, HWND hwnd, unsigned int wMsg, long lEvent)
Event包括如下事件类型:
FD_ACCEPT 用套接字建立一个新的连接
FD_ADDRESS_LIST_CHANGE 为套接字的协议族而改变的本地地址列表
FD_CLOSE 一个现有的连接被关闭
FD_CONNECT 已经完成与远程主机连接的套接字
FD_GROUP_QOS 套接字组的QOS已经改变
FD_OOB 套接字受到带外的数据
FD_QOS 套接字的QOS已经改变
FD_READ 套接字有准备读的数据
FD_ROUTING_INTERFACE_CHANGE 套接字的路由接口已经改变到一个特殊的目的地
FD_WRITE 套接字准备写数据
Example:
WSAAsynSelect( sock, hwnd, WM_SOCKET, FD_READ | FD_CLOSE );
2.2.3 使用WSAEventSelect()函数
为了适应一些应用程序例如精灵程序或者某些没有用户界面的服务程序(因此不使用窗口句柄),Windows Socket 2提供了WSAEventSelect()函数和WSAEnumNetworkEvents()函数。WSAEventSelect()函数和WSAAyncSelect()函数很类似,区别仅在于当一个FD_XXX网络事件发生时,WSAEventSelect()函数将导致一个应用程序指定的事件对象被设置,而WSAAyncSelect()将导致一条Windows消息被发送(例如FD_READ,FD_WRITE等等)。
C#网络编程实际就是对WinSock进行了进一步的包装。
1. IPAddress,IPEndPoint, SocketAddress类
IPAddress就是一个IP 地址类, IPEndPoint就是IPAddress+Port,对应的就是sockaddr_in这个struct. SocketAddress类可以存储IPEndPoint类序列化以后的信息。
1.1 这两个类常用和注意的一些地方:¼
1) IPAddress ip = IPAddress.Parse("192.168.123.1");
2) IPAddress.Any is 0.0.0.0, 这个用在当系统中有多个网络接口,而用户不想把套接字绑定在任何接口上的时候。
3) IPAddress.None is 255.255.255.255, 它经常用来创建一个伪套接字。
4) 得到本机IP地址的一个方法: Dns.GetHostByName(Dns.GetHostName()).AddressList[0]
2, C#中的套接字类System.Net.Sockets.Socket
public Socket(
AddressFamily addressFamily,
SocketType socketType,
ProtocolType protocolType
);
在上面的构造函数中,SocketType与ProtocolType要自己对应。对应关系如下:
Dgram (UDP) 无连接通信
Stream (TCP) 面向连接的通信
Raw (ICMP) ICMP协议
Raw (Raw) 简单IP包
Socket t = new Socket( AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp );
2.1 套接字Socket类常用属性和函数
Blocking 用来设置socket是否堵塞
Connected 指示 Socket 是否已连接到远程主机. 即使 Connected 返回 true,也无法保证 Socket 仍处于 Connected 状态。true 值仅意味着在上次 I/O 操作时 Socket 处于连接状态.
SetSocketOption()方法,将指定的 Socket 选项设置为指定值,看起来就是设置协议包头的一些值。(选项太多,看看MSDN,用google查查,可以找到很多例子)
其他的方法,例如bind(),listen(),send(),receive()和WinSocket本质都一样。(略)
2.2 Socket异常, SocketException
2.3为了简化Socket操作,C#提供了3个套接字助手:
TcpClient, TcpListener, UdpClient