3 监听套接字
在绑定了套接字之后,接下来使用listen()函数将套接字设置为监听状态,等待客户端的连接。listen()函数的格式为
int WSAAPI listen( SOCKET s, int backlog );
其中,参数s表示要设置监听状态的套接字;如果有多个客户端连接处于监听状态的服务端套接字,则服务端只能逐一进行处理。在处理某个连接时,其它的客户端连接只能在队列中等待,backlog指定了队列中等待处理的客户端的数量。当服务端接受了某一客户端的连接后,等待的客户端将会被从队列中取出来进行后续处理。如果backlog的值设置为SOMAXCONN,则表示等待的客户端数量为合理范围内的最大值。这个最大值由网络协议的供应商决定。如果函数调用成功,则listen()函数返回值是0,否则返回SOCKET_ERROR值。
4 接受客户端的连接
一旦套接字处于监听模式,接下来就要时刻准备着接受客户端的连接了。
可以通过accept()函数接受客户端的连接,即处理“3 监听套接字”中提到的等待队列中的第一个客户端连接。该函数的格式为
SOCKET WSAAPI accept(
SOCKET s
, sockaddr *addr
, int *addrlen
);
其中,参数s是处于监听状态的套接字;addr是sockaddr结构的指针,用于保存连接服务端的客户端的信息;addrlen是一个整型指针,该指针指向的内容是addr的大小。如果成功接受了客户端的连接,accept()函数的返回值是一个新创建的套接字,这个新创建的套接字用于服务端与该客户端的通信,否则,返回值是INVALID_SOCKET。需要注意的是,accept()函数返回的新套接字,一般用于发送和接收数据,而不能通过listen()函数将其设置为监听模式。
默认情况下,通过socket()函数创建的套接字都是阻塞的。对于阻塞模式的套接字,如果没有客户端连接服务端,即等待队列中没有客户端连接,服务端中的accept()函数就会一直阻塞,直到有客户端连接后,accept()函数才会返回。
5 相关代码
#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib,"ws2_32.lib")
int main()
{
WSADATA wsaData;
SOCKET ListeningSocket;
SOCKET NewConnection;
SOCKADDR_IN ServerAddr;
SOCKADDR_IN ClientAddr;
int Port = 5150;
WSAStartup(MAKEWORD(2, 2), &wsaData);
ListeningSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (ListeningSocket != INVALID_SOCKET)
{
printf("创建套接字成功。\n");
}
else
{
printf("创建套接字失败。\n");
}
ServerAddr.sin_family = AF_INET;
ServerAddr.sin_port = htons(Port);
ServerAddr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(ListeningSocket, (SOCKADDR *)&ServerAddr,
sizeof(ServerAddr)) == 0)
{
printf("绑定套接字成功。\n");
}
else
{
printf("绑定套接字失败。\n");
}
if (listen(ListeningSocket, 5) == 0)
{
printf("套接字监听模式设置成功,等待客户端连接。。。\n");
}
else
{
printf("套接字监听模式设置失败。\n");
}
int ClientAddrLen = sizeof(SOCKADDR);
NewConnection = accept(ListeningSocket, (SOCKADDR *)
&ClientAddr, &ClientAddrLen);
if (NewConnection != INVALID_SOCKET)
{
printf("有客户端连入,接受连接成功。\n");
}
else
{
printf("有客户端连入,接受连接失败。\n");
}
closesocket(NewConnection);
closesocket(ListeningSocket);
WSACleanup();
return 0;
}