套接字有三种类型:流式套接字, 数据包套接字和原始套接字。其中流式套接字定义了一种可靠的面向连接的服务,实现了无差错无重复的顺序数据传输。数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证可靠,无差错。原始套接字允许对底层协议如ip和ICMP直接访问,主要用于新的网络协议实现测试等。
服务器API函数:
“服务器”其实就是一个进程, 它需要等待任意数量的客户端连接,以便为他们的请求提供服务。对服务器监听的连接来说,“服务器”必须在一个已知的名字上。在TCP/IP中, 这个名字就是本地接口的IP地址, 并加上一个端口号。每一种协议都有一套不同的定址方案,所以会有不同的命名方法。在Winsock中, 第一步是将指定的套接字绑定到它已知的名字上。这个过程是通过API调用bind来完成。下一步是这个套接字设置为监听模式。这时,用API函数listen来完成的。最后,若一个客户试图建立连接,服务器必须通过accept或者WSAAccept调用接受连接。
1.bind函数:
int bind(
SOCKET s,
const struct sockaddr FAR* name,
int namelen
)
s 代表我们希望在上面等待客户端连接的套接字。
name 它的 是一个指向struct sockaddr 类型的指针。sockaddr中包含了名字的地址和端口信息,以及使用的协议等。
namelen 这个参数表示的是 sockaddr 的大小。
示例代码:
SOCKET s;
struct sockaddr_in tcpaddr;
int port = 5150;
int nSockErr;
s = socket(AF_INET, SOCK_SREAM, IPPROTO_TCP);
tcpaddr.sin_family = AF_INET;
tcpaddr.sin_port = htons(port);
tcpaddr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(s, (SOCKADDR*)&tcpaddr, sizeof(tcpaddr)) == SOCKE_ERROR)
{
nSockErr = WSAGetLastError();
}
这个例子中创建了一个流套接字。随后, 设置了TCP/IP地址结构, 一边在它上面接受客户端连接。在这种情况下套接字绑定到端口5150上的默认IP接口。
一旦出错。bind就会返回SOCKET_ERROR。对bind来说最常见的错误是WSAEADDRINUSE。如果使用的是TCP/IP,那么WSAEADDRINUSE就代表另一个进程已经同本地IP接口和端口绑定到一起。或者那个IP或者端口号正处于TIME_WAIT状态。假如在调用bind的时候套接字已经绑定过了,便会返回WSAEFFAULT错误。
2.listen函数
int listen(
SOCKET s,
int backlog
);
第一个参数同样是绑定过的套接字。
第二个参数指定了正在等待连接的最大队列长度。这个参数非常重要,因为完全可能同时出现几个客户端连接请求。如果backlog为2,如果有3个客户端同时发出请求,那么头两个会被放在等待处理的队列中,以便应用程序能依次处理。而第三个请求连接会得到WSAECONNREFUSED错误。注意,一旦服务器接收了一个连接,那个连接就会从等待队列中删除掉,以便能接收继续接收请求。backlog本身的就存在着限制,这个限制是有基层的协议提供者决定的。如果出现非法值,会用最近进合法值取代。除此之外,对于如何知道实际的bakclog,其实并不存在一个标准手段。
listen的错误:listen的错误常见的有WSAEINVAL。这个错误意味着忘记在listen之前调用bind。
3.Accept和WSAAccept
SOCKET accept(
SOCKET s,
struct FAR* addr,
int FAR* addrlen
);
第一个参数 是一个已经处于监听状态的套接字。
第二个参数 是一个指向 sockaddr或者sockaddr_in 类型的指针, 这个结构体用来保存客户端的信息。
第三个参数 表示sockaddr或者sockaddr_in 结构的大小
通过调用accept函数,可以为上述等待队列中的第一个连接请求提供服务。accept函数返回后,addr结构中包含发出请求的客户端的ip地址信息。此外accept还会返回一个新的套接字描述符,它对应这已经接受连接的客户端连接。对于该客户端之后的所有操作都应该使用这个套接字。至于原来的监听套接字仍然处于监听状态。如果INVALID_SOCKET,这是我们需要调用WSAGetLastError已得到更多的错误细节。
SOCKET sServSock;
sockaddr_in addr;
int nSockErr;
int nNumConns[5];
SOCKET sConns[5];
sockaddr Connaddrs[5];
int nAddrLen = sizeof(sockaddr);
sServSock = socket(AF_INET, SOCK_SREAM, 0);
addr.sin_family = AF_INET;
addr.sin_port = hton(5050);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(sServSock, (LPSOCKADDR)&addr, sizeof(addr)) == SOCKE_ERROR)
{
nSockErr = WSAGetLastError();
}
if(listen(sServSock, 2) == SOCKET_ERROR)
{
nSockErr = WSAGetLastError();
}
while( (nNumconns<5))
{
sConns[nNumConns] = accept(sServSock, ConnAddrs[nNumConns],&nAddrLen);
if(sConns[nNumConns] == INVALID_SOCKET)
{
nSockErr = GetLastError();
}
else
{
nNumConns++;
}
}