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等等)。
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