作者:Ernest Laurentin  翻译:高庆余
文章来源:[url]http://www.codeproject.com/KB/IP/serversocket.aspx[/url]
 
绪论:这篇文章主要介绍如何设计基于多线程的客户端/服务器套接字(socket)类。开发者/设计者可以根据具体的需要决定是否选用另外的线程。在网络上有许多其它的套接字类,但是没有一个能够向这个类一样为你的应用程序提供回调功能(事件监测)。本文设计的类能够为你提供如下的事件监测:完成建立链接、放弃链接、链接失败和接收数据(包括0字节数据包)
 
描述:
本文提供一种新的,同时支持TCPUDP通讯的套接字类,与你可能在本站或其它地方发现的套接字编程文章相比,本文设计的类有很多优点。首先,这个类没有像其它类要求提供窗口句柄的限制,假如你仅仅要一个控制应用程序的话,这个限制是不好的,所以,本文的类没有这样的限制。它也为你提供了自动线程支持功能,套接字通过类的句柄进行链接或断开链接操作,它也提供了目前为之我所见到的类所没有的其它功能。此类同时支持客户端和服务器套接字,一个服务器端套接字能被指定为一个能够接受很多链接的套接字,一个客户端套接字能够链接一个服务器端套接字。你还可以使用这个类在不建立链接的情况下在两个应用程序间进行通信。在第二种情况下,你可以创建两个UDP套接字(每个程序一个)。这个类在创建像聊天室程序,或者在两个或更多IPC(进程间通信) 程序(进程)时有助于减少编码量。同时为具有TCP/IP错误处理的双方提供可靠的通信支持。你可能会使用确定的IP地址使数据传递到能够被控制的目的地(仅仅对于UDP方式),这个类的TCP方式只能够在相等的双方进行通信。
现在还不熟悉IP套接字,下面的段落将对此类如何工作进行详细的介绍。这有是本文的目标:解释套接字对象的基本功能。

TCP/IP

TCP/IP 栈比OSI栈短。
TCP是面向连接的协议,UDP(用户数据报协议)是面向无连接的协议。

IP数据报

IP层提供了一个无连接的,不可靠的传输机制。它认为每个数据包都是相互独立的。任何数据报之间的连接必须被更高的层支持。IP层提供一种包含自身数据头的和校验。数据头包含数据报的源地址和目的地址。IP层通过网络处理路由。为了传输,它也提供将大的数据报截断为许多小数据报,并在目的端将它们重新组合的机制。

UDP

UDP是无连接的,也是不可靠的。它将数据报内容的校验和(checksum)和端口号加到IP地址上。

TCP

TCPIP上提供一个逻辑的可靠的基于连接的协议,它提供一个虚拟的环路,这样两个程序就可以进行通信。

互联网地址

为了使用一个服务,你首先要找到它。互联网使用地址规划计算机,这样计算机就能够被定位。IP地址是一个×××32位整数,它编码了一个网络ID和相当多的地址。根据网络地址的大小,网络ID被分成了很多类。

网络地址

A类网使用8位作为网络地址,余下的24位表示其他的地址。B类网使用16位作为网络地址,C类网使用24位作为网络地址,D类网使用全部的32位作为网络地址

内网地址

在互联网上,UNIX网络被分为很多子网。现在有11个,对一个子网,并利用10位处理,允许表示1024个主机。

主机地址

在一个内网里面,用8位表示主机地址。也就是在一个内网里最后可以布置256台主机。

整个IP地址

基于多线程的客户端/服务器套接字类_套接字

32位地址通常被“.”分为4段。
端口地址
一个服务存在于一个主机上,并被端口所知道。这是一个16位数,要发送一个消息给一个服务,你要发给正在运行这个服务的主机的端口,这不是位置透明。当然,很多端口我们都是知道的。例如:
tcpmux
1
TCP
echo
7
UDP
echo
7
TCP
systat
11
TCP
netstat
15
TCP
ftp-data
20
TCP File Transfer Protocol (data)
ftp
21
TCP File Transfer Protocol
smtp
25
TCP Simple Mail Transfer Protocol
time
37
TCP Time Server
time
37
UDP Time Server
name
42
UDP Name Server
whois
43
TCP nicname
domain
53
UDP
domain
53
TCP
tftp
69
UDP
rje
77
TCP
finger
79
TCP
link
87
TCP ttylink
supdup
95
TCP
hostname
101
TCP hostname
pop-2
109
TCP Post Office Protocol
uucp-path
117
TCP
nntp
119
TCP Network News Transfer Protocol
ntp
123
TCP Network Time Protocol
端口的1-255TCP/IP保留,系统可能会保留更多。我们的程序要在1023以上的端口运行,利用getservbyname函数可以得到服务已经注册的端口。

套接字:

套接字是一种操作系统用来处理网络连接的数据结构,当调用socket时创建套接字。它返回一个类似文件描述符的整数值(句柄)。事实上,在windows系统下,在使用ReadFile函数和WriteFile函数时可以使用这个句柄。
#include <sys/types.h>
#include <sys/socket.h>
int socket(int family, int type, int protocol);
这里的“family”在进行IP通信时要指定为 AF_INETprotocol可以为0,并且type要根据是TCP形式还是UDP形式而定。两个程序可以通过各自创建一个套接字,从而在网络上进行连接,这就像一个管子的两端,但事实上这个管子并不存在。

基于连接(TCP

一个程序(服务器端)通过使用套接字的bind(绑定)来监听系统。这样可以使其他的套接字发现它,然后它通过在这个套接字上进行监听,进而可以“accept”(接收)任何进入的消息。而其他的程序(客户端)通过和服务器的套接字建立连接,进而可以交换消息。双方大量的消息通过这个通道可以发送出去。
服务器端
  • Create endpoint (socket())
  • Bind address (bind())
  • Specify queue (listen())
  • Wait for connection (accept())
  • Transfer data (read()/write())
客户端
  • Create endpoint (socket())
  • Connect to server (connect())
  • Transfer data (read()/write())

无连接(UDP

在没有连接的方案中,两个套接字必须通过使用bind(绑定)使系统知道它们的存在。这是因为每个消息都被单独处理,所以客户端每发送一个消息或数据的时候都要查×××器端。每当调用bind函数,它都绑定到一个新的端口,它不能绑定到一个已经存在的端口。假如你指定端口号为0,系统将自动为你分配一个未使用的端口号。因为每次发送消息都存在这个额外的操作,所以程序不使用Read/Write函数,而使用recvfrom/sendto函数。这个函数使用远端服务器的地址作为套接字的参数,执行写入操作。
服务器端
  • Create endpoint (socket())
  • Bind address (bind())
  • Transfer data (sendto()/recvfrom())
客户端
  • Create endpoint (socket())
  • Bind address (bind()) (optional if connect is called)
  • Connect to server (connect())
  • Transfer data (sendto()/recvfrom())

使用方法:

这个类可以用来创建TCPUDP套接字。它的用法很简单。首先,这个类对服务器端来说是未完成的,这个类必须被派生。幸运的是,仅仅需要创建OnDataReceived OnEvent这两个函数。默认函数什么都不做。我们现在就创建一个服务器端套接字,并启动它,方法如下:
// To use TCP socket
// no smart addressing - we use connection oriented
m_SocketObject.SetSmartAddressing( false ); 
m_SocketObject.CreateSocket( m_strPort, AF_INET, SOCK_STREAM,0); // TCP
 
// To use UDP socket
m_SocketObject.SetSmartAddressing( true );
m_SocketObject.CreateSocket( m_strPort, 
   AF_INET, SOCK_DGRAM, SO_BROADCAST); // UDP
 
// Now you may start the server/client thread to do the work for you...
m_SocketObject.WatchComm();
创建一个客户端套接字,并启动它,方法如下:
// To use TCP socket
m_SocketObject.ConnectTo( strServer, m_strPort, AF_INET, SOCK_STREAM); // TCP
 
// To use UDP socket
m_SocketObject.ConnectTo( strServer, m_strPort, AF_INET, SOCK_DGRAM); // UDP
 
// Now you may start the server/client thread to do the work for you...
m_SocketObject.WatchComm();
 
 
备注:翻译中错误在所难免,欢迎大家批评指正,不胜感激。
         源代码见附件,也可以到[url]http://www.codeproject.com/KB/IP/serversocket.aspx[/url]下载。