winsock学习笔记(三)

本文档是关于Winsock的入门学习笔记,主要涵盖如何在Windows环境下进行网络编程,包括初始化Winsock、创建套接字、绑定、监听、接受连接、收发数据以及断开连接等基本步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;
}

**代码基本跟创建客户端socket的时候一样,唯一值得注意的是,hints.ai_flags设置为AI_PASSIVE,当这个位被置位时候,通常在getaddrinfo函数中的第一个参数,也即是地址名设置为NULL,这样就会返回一个通配地址。网上也有资料说系统会将主机自己的地址填充进去。像我自己这样测试之后打印出来是0.0.0.0。
**这里要拓展一下,返回的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

SOCKET ClientSocket;

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();

ClientSocket = INVALID_SOCKET;

// 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"

int
__cdecl main(void)
{
    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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值