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 WinsockiResult = 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 portiResult = 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 serverListenSocket = 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 socketiResult = 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 socketClientSocket = 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 socketclosesocket(ListenSocket);// Receive until the peer shuts down the connectiondo {iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);if (iResult > 0) {printf("Bytes received: %d\n", iResult);// Echo the buffer back to the senderiSendResult = 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 doneiResult = shutdown(ClientSocket, SD_SEND);if (iResult == SOCKET_ERROR) {printf("shutdown failed with error: %d\n", WSAGetLastError());closesocket(ClientSocket);WSACleanup();return 1;}// cleanupclosesocket(ClientSocket);WSACleanup();return 0;}
本文档是关于Winsock的入门学习笔记,主要涵盖如何在Windows环境下进行网络编程,包括初始化Winsock、创建套接字、绑定、监听、接受连接、收发数据以及断开连接等基本步骤。
13

被折叠的 条评论
为什么被折叠?



