The socket interface has become ubiquitous in modern operating systems
  • The socket programming interface under Windows is almost the same as in Linux
  • the difference

    • The return type is different (handle type)
    • The handle is not a file descriptor, and not all files are connected in Window (so send and recv cannot be used for sockets under Windows)

Usage of socket() under Windows

 SOCKET s = {0};

s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);  // IPPROTO_TCP 明确指明创建 TCP 协议的套接字

if (s == INVALID) {  // 创建套接字时出错,返回 INVALID_SOCKET
    ERROR("...");
    return -1;
}

Windows Network Programming Interface

#include <winsock2.h>

function prototype Function description
SOCKET socket(int af, int type, int protocal); Create a socket in preparation for a network connection
int connect(SOCKET s, const struct sockaddr *addr, int len); Connect to a remote device at the specified address
int send(SOCKET s, const char *buf, int len, int flags); Send data to remote device
int recv(SOCKET s char *buf, int len, int flags); Receive data from remote devices
int closesocket(SOCKET s); close the connection, destroy the socket
int bind(SOCKET s, const struct sockaddr *addr, int len); Associate the socket with the specified address
int listen(SOCKET s, int backlog); Push socket into listening state, waiting for connection
SOCKET accept(SOCKET s, struct sockaddr *addr, int len); Receive client connections
int shutdown(SOCKET s, int howto); Close the connection, stop sending and receiving
 closes 与 shutdown(Linux 下也存在) 的区别:shoutdown 不释放 socket 资源

a few nuances

  • Initialize the system environment through WSAStartup() (first call)
  • socket(), accept() error returns INVALID_SOCKET (cannot default to -1)
  • bind(), listen() error returns SOCKET_ERROR (cannot default to -1)
  • connect(), send(), recv() error returns SOCKET_ERROR (cannot default to -1)
  • Clean up the system environment via WSACleanup() (last call)

Special Instructions for Windows Network Programming

  • Set the link ws2_32.lib in the project properties
  • Define the variable WSADATA wd
  • Select socket version and initialize WSAStartup(MAKEWORD(2, 2), &wd)

    • Multiple socket versions exist in Windows

      • MAKEWORD(1, 2) // major version is 1, minor version is 2, return 0x0201
      • MAKEWORD(2, 2) // major version is 2, minor version is 2, returns 0x0202

Programming Lab: Windows Network Programming Examples

client.c
 #include "stdafx.h"
#include <winsock2.h>

int _tmain(int argc, _TCHAR* argv[])
{
    SOCKET sock = 0;  // 注意 socket 类型
    struct sockaddr_in addr = {0};
    int len = 0;
    char buf[128] = {0};
    char input[32] = {0};
    int r = 0;

    WSADATA wd = {0};  // 注意变量定义

    if( WSAStartup(MAKEWORD(2, 2), &wd) != 0 )  // 注意初始化系统环境
    {
        printf("startup error\n");
        return -1;
    }

    sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

    if( sock == INVALID_SOCKET )  // 注意返回值类型
    {
        printf("socket error\n");
        return -1;
    }

    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    addr.sin_port = htons(8888);

    if( connect(sock, (struct sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR )  // 注意返回值类型
    {
        printf("connect error\n");
        return -1;
    }

    printf("connect success\n");

    while( 1 )
    {
        printf("Input: ");

        scanf("%s", input);

        len = send(sock, input, strlen(input) + 1, 0);

        r = recv(sock, buf, sizeof(buf), 0);

        if( r > 0 )
        {
            printf("Receive: %s\n", buf);
        }
        else
        {
            break;
        }
    }

    closesocket(sock);  // 注意
    
    WSACleanup();  // 注意清除系统环境

    return 0;
}
server.c
 // Server.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <winsock2.h>

int _tmain(int argc, _TCHAR* argv[])
{
    SOCKET server = 0;
    struct sockaddr_in saddr = {0};
    SOCKET client = 0;
    struct sockaddr_in caddr = {0};
    int asize = 0;
    int len = 0;
    char buf[32] = {0};
    int r = 0;

    WSADATA wd = {0};

    if( WSAStartup(MAKEWORD(2, 2), &wd) != 0 )
    {
        printf("startup error\n");
        return -1;
    }

    server = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

    if( server == INVALID_SOCKET )
    {
        printf("server socket error\n");
        return -1;
    }

    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = htonl(INADDR_ANY);
    saddr.sin_port = htons(8888);

    if( bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == SOCKET_ERROR )
    {
        printf("server bind error\n");
        return -1;
    }

    if( listen(server, 1) == SOCKET_ERROR )
    {
        printf("server bind error\n");
        return -1;
    }

    printf("server start success\n");

    while( 1 )
    {
        asize = sizeof(caddr);

        client = accept(server, (struct sockaddr*)&caddr, &asize);

        if( client == INVALID_SOCKET )
        {
            printf("client accept error\n");
            return -1;
        }

        printf("client: %d\n", client);

        do
        {
            r = recv(client, buf, sizeof(buf), 0);

            if( r > 0 )
            {
                printf("Receive: %s\n", buf);

                if( strcmp(buf, "quit") != 0 )
                {
                    len = send(client, buf, r, 0);
                }
                else
                {
                    break;
                }
            }

        } while ( r > 0 );

        closesocket(client);
    }
    
    closesocket(server);

    WSACleanup();

    return 0;
}
Question: Is select() specific to Linux systems?

select() function under Windows

  • The select() function is also provided in Windows, and the parameters are exactly the same as the Linux version
  • Note: In the select() function on Windows, the first parameter has no meaning (just for compatibility)
 #include <winsock2.h>

int select(int nfds, fd_set *readfds,
                     fd_set *writefds,
                     fd_set *excepfds,
                     const struct timeval *timeout);

One nuance: select() in Windows is designed specifically for sockets

  • fd_count is used to record the number of interested sockets
  • fd_array is used to record interested socket handles
 typedef struct fd_set {
    u_int fd_count;
    SOCKET fd_array[FD_SETSIZE];
}fd_set;

Example of using the select() function in Windows

 temps = reads;

timeout.tv_sec = 0;
timeout.tv_usec = 10000;

num = select(0, &temps, 0, 0, &timeout);

if (num > 0) {
    unsigned int i = 0;

    for (i=0; i <reads.fd_fount; ++i) {
        if (FD_ISSET(reads.fd_array[i], &temps)) {
            if (reads.fd_array[i] == server) {
            
            }
            else {
    
            }
        }
    }
}

Programming Lab: Multiplexing Server in Windows

 // Select-Server.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <winsock2.h>

SOCKET server_handler(SOCKET server)
{
    struct sockaddr_in addr = {0};
    int asize = sizeof(addr);

    return accept(server, (struct sockaddr*)&addr, &asize);
}

int client_handler(SOCKET client)
{
    char buf[32] = {0};
    int ret = recv(client, buf, sizeof(buf)-1, 0);

    if( ret > 0 )
    {
        buf[ret] = 0;

        printf("Receive: %s\n", buf);

        if( strcmp(buf, "quit") != 0 )
        {
            ret = send(client, buf, ret, 0);
        }
        else
        {
            ret = -1;
        }
    }

    return ret;
}

int _tmain(int argc, _TCHAR* argv[])
{
    SOCKET server = 0;
    struct sockaddr_in saddr = {0};
    // unsigned int max = 0;
    int num = 0;
    fd_set reads = {0};
    fd_set temps = {0};
    struct timeval timeout = {0};

    WSADATA wd = {0};

    if( WSAStartup(MAKEWORD(2, 2), &wd) != 0 )
    {
        printf("startup error\n");
        return -1;
    }


    server = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

    if( server == INVALID_SOCKET )
    {
        printf("server socket error\n");
        return -1;
    }

    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = htonl(INADDR_ANY);
    saddr.sin_port = htons(8888);

    if( bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == SOCKET_ERROR )
    {
        printf("server bind error\n");
        return -1;
    }

    if( listen(server, 1) == SOCKET_ERROR )
    {
        printf("server listen error\n");
        return -1;
    }

    printf("server start success\n");

    FD_ZERO(&reads);
    FD_SET(server, &reads);

    // max = server;

    while( 1 )
    {
        temps = reads;

        timeout.tv_sec = 0;
        timeout.tv_usec = 10000;

        // num = select(max+1, &temps, 0, 0, &timeout);
        num = select(0, &temps, 0, 0, &timeout);

        if( num > 0 )
        {
            unsigned int i = 0;

            for(i=0; i<reads.fd_count; i++)  // 此处相对 Linux 的优势,只需遍历实际监听的 socket 句柄数量!!
            {
                SOCKET sock = reads.fd_array[i];

                if( FD_ISSET(sock, &temps) )
                {
                    if( sock == server )
                    {
                        SOCKET client = server_handler(sock);

                        if( client != INVALID_SOCKET )
                        {
                            FD_SET(client, &reads);

                            // max = (client > max) ? client : max;

                            printf("accept client: %d\n", client); 
                        }
                    }
                    else
                    {
                        int r = client_handler(sock);

                        if( r == -1 )
                        {
                            FD_CLR(sock, &reads);

                            closesocket(sock);
                        }
                    }
                }
            }
        }
    }
    
    closesocket(server);

    WSACleanup();

    return 0;
}
Thinking: How to write a network program that can be compiled and run across platforms?

TianSong
737 声望139 粉丝

阿里山神木的种子在3000年前已经埋下,今天不过是看到当年注定的结果,为了未来的自己,今天就埋下一颗好种子吧