zoukankan      html  css  js  c++  java
  • linux socket

    linux 的 C 库路径为  /usr/include,可以直接查看源码,也可以通过 "man 头文件名" 来学习,需要查看某个函数如 bind() ,则只需要 man 2 bind 即可。

    如:<stdint.h> 定义了 int8_t、int16_t、int32_t、int64_t、uint8_t、uint16_t、uint32_t、uint64_t

    详见:/usr/include/stdint.h   或 man stdint.h

    IPv4套接口地址结构:

    struct sockaddr_in {
      short int sin_family;                      /* Address family 2个字节 */
      unsigned short int sin_port;       /* Port number 2个字节 */
      struct in_addr sin_addr;              /* Internet address 4个字节 */
      unsigned char sin_zero[8];         /* Same size as struct sockaddr 8个字节,暂不使用,一般设为0*/
      };

    sim_family 决定地址家族,IPv4 必须设置为 AF_INET,其它还有 AF_INET6 (IPv6协议)、AF_ROUTE(路由套接口)

    in_addr 这个结构体里只含有一个成员:

    struct in_addr {
        uint32_t s_addr;
        };

    sin_addr.s_addr 使用网络字节序,可以使用 inet_addr 方法将字符串格式 IP 转换成网络字节序。

    通用套接口地址结构:

    struct sockaddr {
      unsigned short sa_family;     /* address family, AF_xxx */
      char sa_data[14];                 /* 14 bytes of protocol address */
      }; 

    connect、 bind、 accept 方法使用的都是通用套接口地址结构,需要把 sockaddr_in 结构强转为 sockaddr 即可。

    不同的主机可能有不同的字节序,其中x86 为小端字节序。网络字节序规定为大端字节序。

    #include <stdio.h>
    #include <arpa/inet.h>
    
    int main()
    {
        unsigned int x = 0x12345678;
        unsigned char *p = (unsigned char*)&x;
        printf("%0x %0x %0x %0x
    ",p[0],p[1],p[2],p[3]);
        unsigned y = htonl(x);
        p = (unsigned char*)&y;
        printf("%0x %0x %0x %0x
    ",p[0],p[1],p[2],p[3]);
    }

    输出结果为:

    字节序转换函数:

    #include <arpa/inet.h>
    
    uint32_t htonl(unit32_t hostlong);
    uint16_t htons(uint16_t hostshort);
    uint32_t ntohl(uint32_t netlong);
    uint32_t ntohs(uint16_t netshort);

    地址转换函数:

    #include <netinet/in.h>
    #include <arpa/inet.h>
    
    in_addr_t inet_addr(const char *cp);  //点分十进制转换成32位整型地址
    int inet_aton(const char *cp, struct in_addr *inp); //同上
    char *inet_ntoa(struct in_addr in);  //32位整型地址转换成点分十进制地址

    套接字类型:

    SOCK_STREAM    流式套接字
    SOCK_DGRAM     数据报套接字
    SOCK_RAW       原始套接字

    int socket(int domain, int type, int protocol);

    如:socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

    也可以写成:socket(AF_INET, SOCK_STREAM, 0);    //第三个参数为0,表示由系统自动选择协议,而 AF_INET 和 SOCK_STREAM 组合一定是 IPPROTO_TCP 协议。

    指定本机地址:

    server.sin_addr.s_addr = htonl(INADDR_ANY);    //推荐
    server.sin_addr.s_addr = inet_addr("127.0.0.1");
    inet_aton("127.0.0.1",&server.sin_addr);  

    int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen); 

    在绑定之前,尽量使用 setsockopt() 来设置 SO_REUSEADDR 套接字选项,可以使得不必等待 TIME_WAIT 状态消失就可以重启服务器。如:

    int on = 1;
    setsockopt(sockfd,SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

    level:(级别): 指定选项代码的类型。
      SOL_SOCKET: 基本套接口
      IPPROTO_IP: IPv4套接口
      IPPROTO_IPV6: IPv6套接口
      IPPROTO_TCP: TCP套接口
    optname(选项名): 选项名称
    optval(选项值): 是一个指向变量的指针 类型:整形,套接口结构, 其他结构类型:linger{}, timeval{ }
    optlen(选项长度) :optval 的大小

    返回值:标志打开或关闭某个特征的二进制选项

    int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

    int connect(int sockfd, const struct sockaddr *addr,socklen_t addrlen);

    int listen(int sockfd, int backlog);

    第二个参数表示监听队列最大连接数(包括未完成连接队列和已完成连接队列的大小之和),可以使用 SOMAXCONN 这个宏。

    int accept(int listenfd, struct sockaddr* client, socklen_t *addlen);

    client 和 addlen 是用来返回客户端的套接字地址结构和对应的结构长度的。如果不关心这两个值,可以使用  accept(int sockfd, NULL, NULL);

    否则为:

    sockaddr_in client_addr;
    socklen_t client_addr_len = sizeof(client_addr);     //这时必须要有初始值,否则 accept() 失败
    accept(sockfd, (sockaddr*)&client_addr, &client_addr_len));

    使用 socket() 创建一个套接字时,默认情况下它是一个主动套接字,即将调用 connect() 发起连接的客户端套接字,对于服务器,必须调用 listen() 函数,将这个未连接的套接字转换成被动套接字,即监听套接字,负责接受每个客户的连接请求一个服务器常常只有一个监听套接字,而且会一直存在,直到服务器关闭。accept() 函数可以返回已连接套接字描述符,已连接套接字是内核为每个被接受的客户端分别创建的,负责与对应的客户端进行数据传输。当服务器完成与该客户端的数据传输时,需要关闭该已连接描述符。

    数据传输函数:

    因为套接字描述符也是一种文件描述符,所以可以使用文件读写函数 read() 和 write():

    #include <unistd.h>

    int write(int sockfd, char *buf, int len);     //出错返回 -1,成功返回大于0的整数,为发送的字节数

    int read(int sockfd, char *buf, int len);  //出错返回 -1,成功返回大于0的整数,为接收的字节数

    此外,还有 TCP套接字提供的 send() 和 recv() 函数:

    #include <sys/types.h>
    #include <sys/socket.h>

    ssize_t send(int sockfd, const void* buf, size_t len, int flags);    //前三个参数同 write() ,第四个参数为传输控制标志,一般设为0,也可以为其它值如:MSG_OOB 等

    ssize_t recv(int sockfd, void *buf, size_t len, int flags);   //前三个参数同 read() ,第四个参数一般设为0,也可以为其它值如:MSG_PEEK 等

    关闭套接字:

    #include <unistd.h>

    close(int sockfd);

    UDP的数据传输函数:

    ssize_t sendto(int sockfd, const void* buf, size_t len, int flags, const struct sockaddr *to, int addrlen); 

    ssize_t recvfrom(int sockfd, const void* buf, size_t len, int flags, const struct sockaddr *from, int addrlen);

    前四个参数完全等同 TCP 的 send 和 recv 方法,第五和第六个参数类似于 accept() 的后两个参数。这两个函数,也可以用于TCP协议,但是一般不这么使用。

    UDP的套接字不需要监听,只要 bind() 需要监听的端口就可以接收数据了。

    exit() 退出当前进程。

    父进程退出,程序结束,但子进程却没有退出,可以通过 "px aux | grep 二进制文件名" 来查看。所以需要通过信号通知的方式,来通知子进程退出。

    sysconf(_SC_NPROCESSORS_CONF)   获取服务器处理器数目(核心数)

    关于下面的写法,见于:http://www.spongeliu.com/415.html

    #define ERR_EXIT(m)
    do
    {
    perror(m);
    exit(EXIT_FAILURE);
    } while(0)

  • 相关阅读:
    51nod 1621 花钱买车牌 优先队列
    最大字段和 51nod 1049 水水水水水水水水水水水水
    大数相乘 51nod 1027 水题
    逆序数 51nod 1019 归并 分治
    最长公共子序列 LCS 递归 dp 51Nod 1006
    vc6 字体设置
    自行车维护大全(zz)
    DirectX 9.0 3D游戏开发编程基础 [书评](zz)
    二维线段树
    latex 引用文献 bib
  • 原文地址:https://www.cnblogs.com/tianyajuanke/p/3251882.html
Copyright © 2011-2022 走看看