• 基本函数与结构


    IPv4套接字结构

    套接字结构,定义IP地址与端口号,仅用于IPv4,不适用IPv6

    #include <netinet/in.h>
    struct sockaddr_in{
        uint8_t sin_len; //本结构长度,16字节
        sa_family_t sin_family; // AF_INET
        in_port_t sin_port; //16位端口号
        struct in_addr sin_addr; //32位IP地址
        char sin_zero[8]; //未使用的字符数组
    };
     
    //32位IP地址结构,sockaddr_in的第四个成员
    struct in_addr{
        in_addr_t s_addr;
    };
    

    通用套接字结构

    由于IPv4,IPv6,域套接字的结构不一样,为了兼容性于是有了通用套接字结构
    sockaddr通常用于函数的参数转换,结构大小随协议的不同而改变,在实际使用时,函数会要求传入一个结构大小的参数

    struct sockaddr{
        uint8_t sa_len;
        sa_family_t sa_family;
        char sa_data[14];
    };
    

    socket函数

    socket函数,用来说明协议和返回一个套接字描述符

    #include <sys/socket.h>
    int socket(int family, int type, int protocol);
    

    family: AF_INET表示IPv4,AF_INET6表示IPv6,AF_LOCOL表示Unix域协议
    type: SOCK_STREAM表示字节流,SOCK_DGRAM表示数据报,另外还有SOCK_RAW和SOCK_SEQPACKET
    protocol: 一般填0由系统根据前两个参数自动选择. 可选项有IPPROTO_TCP,IPPROTO_UDP,IPPROTO_SCTP
    返回值: 称为套接字描述符,与文件描述符类似

    connect函数

    客户端使用connect函数向服务端发起连接请求

    #include <sys/socket.h>
    int connect(int sockfd,const struct sockaddr *servaddr, socklen_t addrlen);
    

    sockfd: 由socket函数返回的套接字描述符
    servaddr: 需要通用套接字地址,IPv4或IPv6套接字地址都需要在此转换一下,servaddr对应的套接字地址信息是服务器的地址和端口号
    addrlen: 前一个参数的长度

    bind函数

    服务器端绑定端口用

    #include <sys/socket.h>
    int bind(int sockfd,const struct sockaddr *myaddr,socklen_t addrlen);
    

    与connect参数类似,不同的是bind中myaddr的套接字地址信息是本地的地址和端口号

    listen函数

    服务器端在绑定端口后,listen函数将端口设置为可连接状态,同时listen也可用于设置最多客户端连接数量

    int listen(int sockfd,int backlog);
    

    backlog用于设置客户端连接数量上限

    accept函数

    如果当前没有客户端连接上来,函数使进程投入睡眠.当客户端正确完成连接后,进程被唤醒开始处理事务

    #include <sys/socket.h>
    int accept(int sockfd,struct sockaddr *cliaddr,socklen_t *addrlen);
    

    sockfd为socket函数的返回值
    cliaddr是一个传出参数,记录着客户端的IP和端口号
    addrlen参数既是传入也是传出,函数开始执行时作为传入参数,其值是cliaddr结构的大小,由我们通过sizeof获取,当函数执行完毕时,addrlen被作为传出参数,其值是实际客户端结构的大小
    便于理解的例子: nread=read(fd,buff,nleft),我们要从fd中读取nleft个字节到buff中,read函数的返回值是实际读取的字节数,而accept函数相当于把nleft和nread合并在一起,当我们开始read时从nleft得知要读取的字节数,读取完毕后将返回值写到nleft所在的内存上

    close函数

    关闭套接字描述符

    #include <unistd.h>
    int close(int sockfd);
    

    转换函数

    主机端字节序与网络字节序转换,s表示short,l虽然表示long但只有32位

    #include <netinet/in.h>
    uint16_t htons(uint16_t host16bitvalue);
    uint16_t ntohs(uint16_t host16bitvalue);
    uint32_t htonl(uint32_t host32bitvalue);
    uint32_t ntohl(uint32_t host32bitvalue);
    

    IP地址的字符表示与网络字节表示转换

    #include <arpa/inet.h>
    //老式,用于v4
    inet_aton(const char *strptr,struct in_addr *addrptr);
    in_addr_t inet_addr(const char *strptr);
     
    char *inet_ntoa(struct in_addr inaddr);
     
    //新式,兼容v4和v6
    int inet_pton(int family,const char *strptr,void *addrptr);
    const char *inet_ntop(int family,const void *addrptr,char *strptr,size_t len);
    

    获取套接字ip

    #include <sys/socket.h>
    int getsockname(int sockfd,struct sockaddr *localaddr,socklen_t *addrlen);
    int getpeername(int sockfd,struct sockaddr *peeraddr,socklen_t *addrlen);
    

    getsockname: 获取sockfd套接字本地端的ip地址, 存储在localaddr中, addrlen作输入输出
    getpeername: 获取sockfd套接字另一端的ip地址, 后两个参数作用同上

    例子1.字节序转换

    #include <stdlib.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
     
     
    /* test host byteorder and net byteorder */
    int main(int argc, const char *argv[])
    {
        unsigned int num=0x12345678;
        unsigned char *p=(unsigned char *)&num;
        printf("%0x %0x %0x %0x
    ",p[0],p[1],p[2],p[3]);
        unsigned int x=htonl(num);
        p=(unsigned char *)&x;
        printf("%0x %0x %0x %0x
    ",p[0],p[1],p[2],p[3]);
     
        char *ipstr="192.168.1.1";
     
        /* old function */
        struct in_addr addr;
        unsigned int netip=inet_addr(ipstr);
        printf("netip=%u
    ",netip);
        //addr.s_addr=netip;
        inet_aton(ipstr,&addr);
        printf("ipstr=%s
    ",inet_ntoa(addr));
     
        /* new function */
        struct in_addr addr2;
        inet_pton(AF_INET,ipstr,&addr2);
        printf("netip=%u
    ",addr.s_addr);
        char ipstrbuf[INET_ADDRSTRLEN];
        inet_ntop(AF_INET,&addr2,ipstrbuf,sizeof(ipstrbuf));
        printf("ipstrbuf=%s
    ",ipstrbuf);
        return 0;
    }
    

    例子2.获取socket连接的本地和对端ip地址及端口

        /* client.c */
        //connect(sockfd,(struct sockaddr *)&servaddr,len);
        struct sockaddr_in localaddr;
        socklen_t len=sizeof(localaddr);
        /* getsockname getpeername 参数均一致,只能换个名字即可 */
        /* server端调用时要放到accept之后 */
        if(getsockname(sockfd,(struct sockaddr *)&localaddr,&len) < 0)
            err_quit("getsockname");
        printf("local addr=%s,local port=%d
    ",inet_ntoa(localaddr.sin_addr),ntohs(localaddr.sin_port));
    
  • 相关阅读:
    AngularJS启动过程分析
    mongodb 基本用法大全
    bitbucket工程改名导致 repository does not exist. fatal: Could not read from remote repository.
    分散的配置文件VS集中的注册表
    让browserify接收命令行参数,在打包时parse yml配置文件
    vscode下ts-node传入cli参数
    d3 .each()
    d3选择全部子节点,不知道class和id
    d3 parse字符串形式的xml svg and append to element
    在浏览器端用es6,babel+browserify打包
  • 原文地址:https://www.cnblogs.com/cfans1993/p/5883366.html
Copyright © 2020-2023  润新知