socket 接口已普遍存在于古代操作系统中
  • Windows 下的 socket 编程接口与 Linux 中简直雷同
  • 不同之处

    • 返回类型不同(句柄类型)
    • 句柄不是文件描述符,Window 中并不是所有接文件 (因而 windows 下对于 socket 无奈应用 send、recv)

Windows 下 socket() 的用法

SOCKET s = {0};s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);  // IPPROTO_TCP 明确指明创立 TCP 协定的套接字if (s == INVALID) {  // 创立套接字时出错,返回 INVALID_SOCKET    ERROR("...");    return -1;}

Windows 网络编程接口

#include <winsock2.h>

函数原型性能形容
SOCKET socket(int af, int type, int protocal);创立套接字,为网络连接做筹备
int connect(SOCKET s, const struct sockaddr *addr, int len);连贯指定地址的近程设施
int send(SOCKET s, const char *buf, int len, int flags);发送数据到近程设施
int recv(SOCKET s char *buf, int len, int flags);接管近程设施发回的数据
int closesocket(SOCKET s);敞开连贯,销毁套接字
int bind(SOCKET s, const struct sockaddr *addr, int len);将套接字与指定地址进行关联
int listen(SOCKET s, int backlog);将套接字推入监听状态,期待连贯
SOCKET accept(SOCKET s, struct sockaddr *addr, int len);接管客户端连贯
int shutdown(SOCKET s, int howto);敞开连贯,进行发送和接管
closes 与 shutdown(Linux 下也存在) 的区别:shoutdown 不开释 socket 资源

几点细微差别

  • 通过 WSAStartup() 初始化零碎环境(最先调用)
  • socket(), accept() 谬误返回 INVALID_SOCKET (不可默认为 -1)
  • bind(), listen() 谬误返回 SOCKET_ERROR (不可默认为 -1)
  • connect(), send(),recv() 谬误返回 SOCKET_ERROR (不可默认为 -1)
  • 通过 WSACleanup() 革除零碎环境(最初调用)

Windows 网络编程的非凡阐明

  • 在工程属性中设置链接 ws2_32.lib
  • 定义变量 WSADATA wd
  • 抉择 socket 版本并初始化 WSAStartup(MAKEWORD(2, 2), &wd)

    • Windows 中存在多个 socket 版本

      • MAKEWORD(1, 2) // 主版本为1, 副版本为2,返回0x0201
      • MAKEWORD(2, 2) // 主版本为2, 副版本为2,返回0x0202

编程试验:Windows 网络编程示例

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;}
问题:select() 是 Linux 零碎特有的吗?

Windows 下的 select() 函数

  • Windows 中同样提供 select() 函数,并且参数与Linux的版本完全相同
  • 留神:Windows 中 select() 函数,第一个参数没有任何意义(仅为了兼容)
#include <winsock2.h>int select(int nfds, fd_set *readfds,                     fd_set *writefds,                     fd_set *excepfds,                     const struct timeval *timeout);

一个细微差别:Windows 中的 select() 专门为套接字设计

  • fd_count 用于记录趣味的 socket 数量
  • fd_array 用于记录感兴趣的 socket 句柄
typedef struct fd_set {    u_int fd_count;    SOCKET fd_array[FD_SETSIZE];}fd_set;

Windows 中 select() 函数应用示例

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

编程试验: 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;}
思考:如何编写能够跨平台编译运行的网络程序?