• 《网络多人游戏架构与编程》之伯克利套接字


    一、创建、销毁、绑定、发送、接收
     
      SOCKET socket(int af,int type,int protocol);     
     
    af:  协议簇
    含义
    AF_UNSPEC
    未指定
    AF_TNET
    IPV4
    AF_IPX
    分组交换
    AF_APPLETALK
    Appletalk协议
    AF_INET6
    IPV6
    type:  通过socket发送和接收分组的形式
    含义
    SOCK_STREAM
    有序的、可靠的数据流分段
    SOCK_DGRAM
    离散的报文
    SOCK_RAW
    包头可以由应用层自定义
    SOCK_SEQPACKET
    类似SOCK_STREAM,但要整体读取数据包
    protocol:socket应使用的协议,包括传输层协议、各种实用网络层协议。
    需要的类型
    含义
    IPPROTO_UDP
    SOCK_DGRAM
    UDP数据报
    IPPROTO_TCP
    SOCK_STREAM
    TCP报文段
    IPPRPTO_IP/0
    Any
    实用默认协议
     
    ///////////////创建SOCKET/////////////////
    创建1个IPV4 UDP socket : SOCKET udpSocket = socket( AF_INET, SOCK_DGRAM, 0);
    创建1个TCP socket:SOCKET tcpSocket = socket( AF_INET, SOCK_STREAM, 0);
     
    ///////////停止传输和接收SOCKET//////////
    int shutdown(SOCKET sock, int how);
    ————————————
    how: SD_SEND         停止发送;产生FIN数据包,所有数据发送后发送这个数据包,通知另一端关闭socket,然后另一端会回一个FIN数据包,我们就可以关闭socket了。
             SD_RECEIVE     停止接收
             SD_BOTH         停止发送和接收
     
    ///////////////关闭SOCKET/////////////////
    关闭不考虑类型:int closesocket (SOCKET sock);
    ————————————
    Tips:关闭前保证所有发送的和接收的数据都已经传输和确认。
     
    ///////////////绑定SOCKET/////////////////
    返回值:0 成功 ; -1 错误
     
    ///////////////UDP发送数据/////////////////
    int sendto(SOCKET sock, const char *buf, int len, int flags, const sockaddr *to, int tolen);
    buf:指向待发送数据起始地址的指针;
    len:待发送数据的大小;避免发送大于1300字节的数据包。
    flags:对发送标志进行按位或运算的结果,游戏中通常是0。
    to:接收端的sockaddr。
    tolen:to指向的sockaddr的大小。对于IPV4,等于sizeof(sockaddr_in)。
    返回值:成功:等待发送数据长度; 失败:-1。
     
    ///////////////UDP接收数据/////////////////
    int recvfrom(SOCKET sock, const char *buf, int len, int flags, const sockaddr *from, int *fromlen);
    buf:接收的数据包的缓冲区;
    len:buf可以存储的最大字节数。
    flags:对接收标志进行按位或运算的结果,游戏中通常是0。
    from:指向sockaddr的指针。
    fromlen:from指向的sockaddr的大小。
    返回值:成功:复制到buf的字节数; 失败:-1。
     
    ///////////////TCP启动监听/////////////////
    int listen(SOCKET sock, int backlog);
    backlog:允许传入的最大连接数。SOMAXCONN表示默认值。
    返回值:成功 0;错误 -1。
     
    ///////////////TCP接收连接/////////////////
    SOCKET accept(SOCKET sock, sockaddr *addr, int *addrlen);
     
    ///////////////TCP发起链接/////////////////
    SOCKET connect(SOCKET sock, const sockaddr *addr, int *addrlen);
     
    ///////////////TCP发送数据/////////////////
    int send(SOCKET sock, const char *buf, int len, int flags);
     
    ///////////////TCP接收数据/////////////////
    int recv(SOCKET sock, char *buf, int len, int flags);
     
     
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    二、阻塞和非阻塞I/O
     
    三种解决线程阻塞的办法:
    1. 多线程
      • 目的:给每一个可能的阻塞调用生成一个线程:每个客户端1个,监听1个...
      • 缺点:每个客户端需要1个线程,不利于扩展和管理。
    2. 非阻塞I/O
      • 使用ioctlsocket设置socket为非阻塞模式:int ioctlsocket(SOCKET sock, long cmd, u_long *argp);  //cmd是控制参数;argp是该参数的取值,0阻止开启非阻塞,其他值开启。
      • 优点:每帧检查是否有准备好的待接收数据,有,先处理第一个挂起;没有,进行到下一帧,没有等待。
      • 缺点:轮询的socket数量很大时,效率很低。
    3. select函数
      • 优点:可以同时检查多个socket,只要有1个准备好了就开始执行
     
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    三、平台差异
     
    ///////////////Windows头文件/////////////////
    Windows平台使用头文件 WinSock2.h ,包含了socket相关的函数声明和数据类型。
    ————————————
    Windows.h是旧版本,和Winsock2.h一起用会造成命名冲突,想要避免冲突就要在Windows.h之前引用Winsock2.h
     
    ///////////////Windows激活socket/////////////////
    使用WSAStartup激活Windows上的socket库: int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData); 
    返回值:0 或 错误代码。
    必须先成功启动WSAStartup, 才能正确运行Winsock2中的函数。
    ————————————
    wVersionRequested 是两字节的WORD,低字节表示主版本号,高字节表示Winsock实现的最低版本。
    lpWSAData 指向Windows特定的数据结构。
    WSAStartup填入被激活的socket库的信息。
     
    ///////////////Windows关闭socket/////////////////
    int WSACleanup();      //结束所有未完成的socket操作,释放所有socket资源
    返回值:错误代码,通常返回-1,要找到错误来源要在返回-1后调用WSAGetLastError()。
    ————————————
    int WSAGetLastError();    返回当前运行线程最近的错误代码
     
    ///////////////Windows数据包地址/////////////////
    存放地址信息的数据类型:struct  sockaddr  {
                                                    uint16_t    sa_family;        //指定地址类型,应与af一致。
                                                    char          sa_data[14];    //存储真正的地址。
                                            };
     
    创建1个IPV4数据包的地址:struct  sockaddr_in  {
                                                    short                 sin_family;      //指定地址类型,应与af一致。
                                                    uint16_t            sin_port;         //存储地址中16位端口    
                                                    struct  in_addr  sin_addr;        //存储4字节IPV4地址;in_addr 在不同平台有差异。
                                                    char                  sin_zero[8];    //存储真正的地址。
                                            };
     
    IP地址从字符串表示转换为 in_addr  表示:inet_pton POSIX系统,InetPton Windows系统
    src:存储.分隔的地址
    dst:指向待赋值的sin_addr字段
    返回值:1 成功;0 源字符串错误;-1 其他
     
    将域名解析为IP地址:
     
    ///////////////socket字节序/////////////////
    原因:TCP/IP和主机可能存在多字节数的字节序上采用不同标准
    解决:多字节数赋值必须将 主机字节序 转换为 网络字节序
    方法:uint16_t  htons( uint16_t hostshort);  
            uint16_t  htonl( uint16_t hostlong);
     
    网络字节序 转换成 主机字节序:
    uint16_t  ntohs( uint16_t networkshort);  
    uint16_t  ntohl( uint16_t networklong);
  • 相关阅读:
    蓝桥杯如何训练?(附VIP题库)
    scratch2.0的教材视频,王木头系列
    out文件 dev c++
    MongoDB 学习笔记
    golang 学习笔记 -- struct interface的使用
    goang学习笔记---struct
    golang 学习笔记 ---JSON
    golang学习笔记 ---rand
    golang学习笔记 --go test
    golang学习笔记---string && strconv
  • 原文地址:https://www.cnblogs.com/tomatokely/p/16316849.html
Copyright © 2020-2023  润新知