• Linux高性能server编程——Linux网络基础API及应用


    

    Linux网络编程基础API

    具体介绍了socket地址意义极其API,在介绍数据读写API部分引入一个有关带外数据发送和接收的程序,最后还介绍了其它一些辅助API

    socket地址API

    主机字节序和网络字节序

    字节序分为大端字节序和小端字节序。小端字节序又被称为主机字节序,大端字节序被称为网络字节序。大端字节序是指一个整数的高位字节存储在内存的低地址处,低位字节存储在内存的高地址处。小端字节序则相反。

    Linux提供例如以下四个函数完毕主机字节序与网络字节序之间的转换:

    #include<arpa/inet.h>

    uint32_thtonl(uint32_t hostlong);

    uint16_thtons(uint16_t hostshort);

    uint32_tntohl(uint32_t netlong);

    uint16_tntohs(uint16_t netshort);

    它们的含义明白,比方htonl将长整型(32bit)的主机字节序转化为网络字节序。这四个函数中,长整型用来转化IP地址,短整型用来转换port号。

    通用socket地址

    #include <bits/socket.h>

    Struct sockaddr

    {

            Sa_family_tsa_family;

            charsa_data[14];

    };

    sa_family是地址族类型(sa_family_t)的变量。地址族通常与协议族类型相应,常见的地址族和相应的协议族例如以下表所看到的:

    协议族

    地址族

    描写叙述

    PF_UNIX

    AF_UNIX

    UNIX本地域协议族

    PF_INET

    AF_INET

    TCP/IPv4协议族

    PF_INET6

    AF_INET6

    TCP/IPv6协议族

    专用socket地址

    UNIX本地域协议族专用socket地址结构体:

    #include <sys/un.h>

    struct sockaddr_un

    {

            sa_family+tsin_family;            //地址族:AF_UNIX

            charsun_path[108]         //文件路径名

    };

    TCP/IP协议族有sockaddr_insockaddr_in6两个专用socket地址结构体,他们分别用于IPv4IPv6:

    struct sockaddr_in

    {

            sa_family_tsin_family;            //地址族:AF_INET

            u_int16_tsin_port;                   //端口号,要用网络字节序表示

            structin_addr sin_addr;          //IPv4地址结构体

    };

    Struct in_addr

    {

            u_int32_ts_addr;                     //IPv4地址,要用网络字节序表示

    };

    Struct sockaddr_in6

    {

            sa_family_tsin6_family;         //地址族:AF_INET6

            u_int16_tsin6_port;                //端口号,要用网路字节序表示

            u_int32_tsin6_flowinfo;//流信息,应设置为0

            structin6_addr;                         //IPv6地址结构体

            u_int32_tsin6_scope_id;        //scope ID,尚处实验阶段

    };

    Struct in6_addr

    {

            Unsignedchar sa_addr[16];   //IPv6地址,要用网络字节序表示

    };

    全部socket地址类型的变量在实际使用时都须要转化为通用socket地址类型sockaddr(强制转换就可以),由于全部socket编程接口使用的地址參数的类型都是sockarrd

    IP地址转换函数

    #include <sys/socket.h>

    #include <netinet/in.h>

    #include <arpa/inet.h>

    int inet_aton(const char *cp, structin_addr *inp);        //将点分十进制转化为网络字节序

    in_addr_t inet_addr(const char *cp);     //同上,但将结果放入cp所指地址

    char *inet_ntoa(struct in_addr in);                  //将网络字节序转化为点分十进制

    上面三个函数用于点分十进制字符串表示的IPv4地址和用网络字节序整数(二进制数)表示的IPv4地址之间的转换。

    当中inet_aton内部用一个静态变量存储转化结果,所以函数不可重入,演示样例

    #include <stdio.h>
    #include <sys/un.h>
    #include <arpa/inet.h>
    int main()
    {
            struct in_addr in1, in2;
            in1.s_addr = inet_addr("1.2.3.4");
            in2.s_addr = inet_addr("10.194.71.60");
            char* szValue1 = inet_ntoa(in1);
            char* szValue2 = inet_ntoa(in2);
            printf("address1:%s
    ",szValue1);
            printf("address2:%s
    ",szValue2);
            return 0;
    }
    

    运行结果:

    address1:10.194.71.60

    address2:10.194.71.60

     

    以下这对函数也能完毕上面3个函数的功能

    #include <arpa/inet.h>

    int inet_pton(int af, const char *src, void*dst);

    const char *inet_ntop(int af, const void*src, char *dst, socklen_t size);

     

    socket相关函数

    #include <sys/types.h>         /* See NOTES */

    #include <sys/socket.h>

    int socket(int domain, int type, intprotocol);         //创建socket,指定协议族和服务类型

    int bind(int sockfd, const struct sockaddr*addr, socklen_t addrlen);   //绑定地址

    int listen(int sockfd, int backlog);   //server创建监听队列以存放处理的客户连接

    int accept(int sockfd, struct sockaddr*addr, socklen_t *addrlen);/从监听队列接收一个连接

    int connect(int sockfd, const structsockaddr *addr, socklen_t addrlen);//客户与server建立连接

     

     

    #include <unistd.h>

    int close(int fd);       //关闭连接

     

    #include <sys/socket.h>

    int shutdown(int sockfd, int how);          //关闭连接

    数据读写

    #include <sys/types.h>

    #include <sys/socket.h>

    ssize_t recv(int sockfd, void *buf, size_tlen, int flags);

    ssize_t recvfrom(int sockfd, void *buf,size_t len, int flags,

    struct sockaddr *src_addr, socklen_t *addrlen);

    ssize_t recvmsg(int sockfd, struct msghdr*msg, int flags);

     

    ssize_t send(int sockfd, const void *buf,size_t len, int flags);

    ssize_t sendto(int sockfd, const void *buf,size_t len, int flags,

                         const struct sockaddr*dest_addr, socklen_t addrlen);

    ssize_t sendmsg(int sockfd, const structmsghdr *msg, int flags);

     

    演示样例:

    带外数据的发送和接收:关于带外数据见http://blog.csdn.net/walkerkalr/article/details/35258523

    发送带外数据:

    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <assert.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <string.h>
    #include <stdlib.h>
    
    int main( int argc, char* argv[] )
    {
        if( argc <= 2 )
        {
            printf( "usage: %s ip_address port_number
    ", basename( argv[0] ) );
            return 1;
        }
        const char* ip = argv[1];
        int port = atoi( argv[2] );
    
        struct sockaddr_in server_address;
        bzero( &server_address, sizeof( server_address ) );
        server_address.sin_family = AF_INET;
        inet_pton( AF_INET, ip, &server_address.sin_addr );
        server_address.sin_port = htons( port );
    
        int sockfd = socket( PF_INET, SOCK_STREAM, 0 );
        assert( sockfd >= 0 );
        if ( connect( sockfd, ( struct sockaddr* )&server_address, sizeof( server_address ) ) < 0 )
        {
            printf( "connection failed
    " );
        }
        else
        {
            printf( "send oob data out
    " );
            const char* oob_data = "abc";
            const char* normal_data = "123";
            send( sockfd, normal_data, strlen( normal_data ), 0 );
            send( sockfd, oob_data, strlen( oob_data ), MSG_OOB );
            send( sockfd, normal_data, strlen( normal_data ), 0 );
        }
    
        close( sockfd );
    

     

    接收带外数据

    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <assert.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <string.h>
    
    #define BUF_SIZE 1024
    
    int main( int argc, char* argv[] )
    {
        if( argc <= 2 )
        {
            printf( "usage: %s ip_address port_number
    ", basename( argv[0] ) );
            return 1;
        }
        const char* ip = argv[1];
        int port = atoi( argv[2] );
    
        struct sockaddr_in address;
        bzero( &address, sizeof( address ) );
        address.sin_family = AF_INET;
        inet_pton( AF_INET, ip, &address.sin_addr );
        address.sin_port = htons( port );
    
        int sock = socket( PF_INET, SOCK_STREAM, 0 );
        assert( sock >= 0 );
    
        int ret = bind( sock, ( struct sockaddr* )&address, sizeof( address ) );
        assert( ret != -1 );
    
        ret = listen( sock, 5 );
        assert( ret != -1 );
    
        struct sockaddr_in client;
        socklen_t client_addrlength = sizeof( client );
        int connfd = accept( sock, ( struct sockaddr* )&client, &client_addrlength );
        if ( connfd < 0 )
        {
            printf( "errno is: %d
    ", errno );
        }
        else
        {
            char buffer[ BUF_SIZE ];
    
            memset( buffer, '', BUF_SIZE );
            ret = recv( connfd, buffer, BUF_SIZE-1, 0 );
            printf( "got %d bytes of normal data '%s'
    ", ret, buffer );
    
            memset( buffer, '', BUF_SIZE );
            ret = recv( connfd, buffer, BUF_SIZE-1, MSG_OOB );
            printf( "got %d bytes of oob data '%s'
    ", ret, buffer );
    
            memset( buffer, '', BUF_SIZE );
            ret = recv( connfd, buffer, BUF_SIZE-1, 0 );
            printf( "got %d bytes of normal data '%s'
    ", ret, buffer );
    
            close( connfd );
        }
    
        close( sock );
        return 0;
    }
    

    同一时候在chen123上启动发送带外数据程序,在li123上启动接收带外数据程序,同一时候用tcpdump抓取这一过程中client和server交换的TCP报文。

    详细操作例如以下

    li123@ubuntu:/$ sudo tcpdump -ntx -i eth0 port 54321

    li123@ubuntu:/$./a.out 192.168.73.130 54321

    chen123@ubuntu:/$ ./a.out 192.168.73.130 54321

    server输出例如以下:

    got 5 bytes of normal data '123ab'

    got 1 bytes of oob data 'c'

    got 3 bytes of normal data '123'

    client发给server的3字节的带外数据abc中,仅有最后一个字符c被server当成真正的带外数据。而且,server对正常数据的接收将被带外数据截断,即前一部分正常数据123ab和兴许正常数据123是不能被一个recv调用所有读出的。

       Tcpdump的输出内容中,和带外数据相关的输出例如以下:

    IP 192.168.73.129.40643 > 192.168.73.130.54321:Flags [P.U], seq 4:7, ack 1, win 229, urg 3, options [nop,nop,TS val 4574025ecr 4573780], length 3

       能够看到tcpdump输出标志U,这表示TCP报文段的头部被设置了紧急标志。urg 3是紧急偏移,塔指针带外数据在字节流中的位置的下一字节位置是73+4,当中4是报文段的序号值相对于初试序号值得偏移)。

     

    其它基础API

    #include <sys/socket.h>

    int sockatmark(int sockfd);     //推断是否处于带外标记

    int getsockname(int sockfd, struct sockaddr*addr, socklen_t *addrlen);    //获取本端sockfd相应的本端socket地址

    int getpeername(int sockfd, struct sockaddr*addr, socklen_t *addrlen);    //获取sockfd相应的远端socket地址

     

    以下两个函数用来读取和设置socket文件描写叙述符属性的方法

    int getsockopt(int sockfd, int level, intoptname, void *optval, socklen_t *optlen);      

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

     

    以下两个函数用来依据主机名称获取主机的完整信息和依据IP地址获取主机的完整信息

    #include <netdb.h>

    struct hostent *gethostbyname(const char*name);

    #include <sys/socket.h>      /* for AF_INET */

    struct hostent *gethostbyaddr(const void*addr, socklen_t len, int type);

     

    以下两个函数依据名称获取某个服务的完整信息和依据port获取某个服务的完整信息

    #include <netdb.h>

    struct servent *getservbyname(const char*name, const char *proto);

    struct servent *getservbyport(int port,const char *proto);

     

    以下这个函数能通过主机名获得IP地址,也能通过server获得port号。

    #include <netdb.h>

    int getaddrinfo(const char *node, constchar *service, const struct addrinfo *hints,

    struct addrinfo **res);

     

    以下这个函数能通过socket地址同一时候获得以字符串标书的主机名和服务名。

    #include <netdb.h>

    int getnameinfo(const struct sockaddr *sa,socklen_t salen, char *host, size_t hostlen,

                          char *serv, size_tservlen, int flags);

  • 相关阅读:
    通过三个DEMO学会SignalR的三种实现方式
    [Asp.net]SignalR实现实时日志监控
    SignalR -- server push 利器
    Microsoft ASP.NET SignalR
    在 Asp.NET MVC 中使用 SignalR 实现推送功能
    asp.net signalR 专题—— 第一篇 你需要好好掌握的实时通讯利器
    asp.net signalR 专题—— 第二篇 对PersistentConnection持久连接的快速讲解
    后台管理UI推荐
    ASP.NET通用权限系统快速开发框架
    .NET通用基本权限系统框架源代码
  • 原文地址:https://www.cnblogs.com/zfyouxi/p/4084487.html
Copyright © 2020-2023  润新知