PS:这几篇学习笔记都是在windows dev center上学习的东西,大部分是翻译。传送门:http://msdn.microsoft.com/en-us/library/windows/desktop/ms738545(v=vs.85).aspx#winsock.advanced_winsock_samples
服务端:
- Initialize Winsock.(初始化winsock)
- Create a socket.(创建套接字)
- Bind the socket.(绑定套接字)
- Listen on the socket for a client.(监听套接字,等待客户端连接)
- Accept a connection from a client.(接受客户端连接)
- Receive and send data.(接受和发送数据或消息)
- Disconnect.(断开连接)
1、Create a socket:
#define DEFAULT_PORT "27015"
struct addrinfo *result = NULL, *ptr = NULL, hints;
ZeroMemory(&hints, sizeof (hints));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
hints.ai_flags = AI_PASSIVE;
// Resolve the local address and port to be used by the server
iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
if (iResult != 0) {
printf("getaddrinfo failed: %d\n", iResult);
WSACleanup();
return 1;
}
SOCKET ListenSocket = INVALID_SOCKET;
// Create a SOCKET for the server to listen for client connections
ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (ListenSocket == INVALID_SOCKET) {
printf("Error at socket(): %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}
**这里要拓展一下,返回的result结构中一个属性是sockaddr*结构指针类型的ai_addr。它包含了两个属性,一个是AF_Family,一个是14个字节的char数组,填充了使用网络字节顺序的地址和端口号。sockaddr的类型随着AF_Family的值而变,当其值为AF_INET使,可用sockaddr_in结构强制转换。然后可以通过两个函数inet_ntoa((sockaddr_in*)ai_addr->sin_addr)和ntohs((sockaddr_in*)ai_addr->sin_port)将网络字节顺序的数据转化为机器字节顺序的输出。一般来说,编程的时候我们不使用sockaddr结构,这个是给操作系统用的。做法是使用sockaddr_in对我们使用的信息进行填充,然后强制转化为sockaddr结构,当做参数传入bind,accept等函数。
2、 Bind a socket:
C++:
// Setup the TCP listening socket
iResult = bind( ListenSocket, result->ai_addr, (int)result->ai_addrlen);
if (iResult == SOCKET_ERROR) {
printf("bind failed with error: %d\n", WSAGetLastError());
freeaddrinfo(result);
closesocket(ListenSocket);
WSACleanup();
return 1;
}
对于服务端socket来说,必须将地址和端口号和你的socket绑定起来,才能进行下一步的对其进行监听。由于绑定之后,由getaddrinfo所返回的result结构我们不再需要,所以可以用freeaddrinfo释放对应的资源。
3、 Listen to a socket:
C++:
if ( listen( ListenSocket, SOMAXCONN ) == SOCKET_ERROR ) {
printf( "Listen failed with error: %ld\n", WSAGetLastError() );
closesocket(ListenSocket);
WSACleanup();
return 1;
}
Listen函数的第二个参数比较值得研究,它是表示还没连接的在缓冲池中的客户端数。具体的情况可以看看网上一篇博客上的分析,写的很好。传送门:http://blog.csdn.net/ordeder/article/details/21551567 有时候这个参数设置得很小但是客户端连接依旧没有问题,这是因为操作系统从缓冲池中取出然后处理的时间太短,所以很少出现同时几个客户端都在连接的情况。