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.youkuaiyun.com/ordeder/article/details/21551567 有时候这个参数设置得很小但是客户端连接依旧没有问题,这是因为操作系统从缓冲池中取出然后处理的时间太短,所以很少出现同时几个客户端都在连接的情况。
4、 Accept a socket
ClientSocket = INVALID_SOCKET;
// Accept a client socket
ClientSocket = accept(ListenSocket, NULL, NULL);
if (ClientSocket == INVALID_SOCKET) {
printf("accept failed: %d\n", WSAGetLastError());
return 1;
}
closesocket(ListenSocket);
WSACleanup();
// Accept a client socket
ClientSocket = accept(ListenSocket, NULL, NULL);
if(ClientSocket == INVALID_SOCKET) {
printf("accept failed:
%d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
接入一个客户端socket并用CLientSocket保存,因为还要往这个客户端socket写回信息。如果在更高级点的技术或者多线程接入多个客户端的话,通常accept之后,后面处理IO之类的工作会丢给工作线程去处理而自己不再管这个客户端socket,因为server socket还要继续处理listen,accept之类的工作。可用select函数或者WSAPoll来帮忙处理实现。
5、 receive and send data
6、 disconnect
上述两步跟客户端的操作基本一样,不再敖述。下面贴出完整的服务端socket代码。
#undef UNICODE
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdlib.h>
#include <stdio.h>
// Need to link with Ws2_32.lib
#pragma comment (lib, "Ws2_32.lib")
// #pragma comment (lib, "Mswsock.lib")
#define DEFAULT_BUFLEN 512
#define DEFAULT_PORT "27015"
__cdecl main(void)int
{
WSADATA wsaData;
int iResult;
SOCKET ListenSocket = INVALID_SOCKET;
SOCKET ClientSocket = INVALID_SOCKET;
struct addrinfo *result = NULL;
struct addrinfo hints;
int iSendResult;
char recvbuf[DEFAULT_BUFLEN];
int recvbuflen = DEFAULT_BUFLEN;
// Initialize Winsock
iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
if (iResult != 0) {
printf("WSAStartup failed with error: %d\n", iResult);
return 1;
}
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 server address and port
iResult = getaddrinfo(NULL, DEFAULT_PORT, &hints, &result);
if ( iResult != 0 ) {
printf("getaddrinfo failed with error: %d\n", iResult);
WSACleanup();
return 1;
}
// Create a SOCKET for connecting to server
ListenSocket = socket(result->ai_family, result->ai_socktype, result->ai_protocol);
if (ListenSocket == INVALID_SOCKET) {
printf("socket failed with error: %ld\n", WSAGetLastError());
freeaddrinfo(result);
WSACleanup();
return 1;
}
// 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;
}
freeaddrinfo(result);
iResult = listen(ListenSocket, SOMAXCONN);
if (iResult == SOCKET_ERROR) {
printf("listen failed with error: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
// Accept a client socket
ClientSocket = accept(ListenSocket, NULL, NULL);
if (ClientSocket == INVALID_SOCKET) {
printf("accept failed with error: %d\n", WSAGetLastError());
closesocket(ListenSocket);
WSACleanup();
return 1;
}
// No longer need server socket
closesocket(ListenSocket);
// Receive until the peer shuts down the connection
do {
iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
if (iResult > 0) {
printf("Bytes received: %d\n", iResult);
// Echo the buffer back to the sender
iSendResult = send( ClientSocket, recvbuf, iResult, 0 );
if (iSendResult == SOCKET_ERROR) {
printf("send failed with error: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
printf("Bytes sent: %d\n", iSendResult);
}
else if (iResult == 0)
printf("Connection closing...\n");
else {
printf("recv failed with error: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
} while (iResult > 0);
// shutdown the connection since we're done
iResult = shutdown(ClientSocket, SD_SEND);
if (iResult == SOCKET_ERROR) {
printf("shutdown failed with error: %d\n", WSAGetLastError());
closesocket(ClientSocket);
WSACleanup();
return 1;
}
// cleanup
closesocket(ClientSocket);
WSACleanup();
return 0;
}