• socket编程备忘录


    #include <sys/socket.h>
    int socket(int domain, int type, int protocol);
    socket的三个形参搭配整理如下:
    ------------------------------------------------------------------
    TCP、UDP socket: 最常用的socket,不解释

    tcp_socket = socket(AF_INET, SOCK_STREAM, 0);
    udp_socket = socket(AF_INET, SOCK_DGRAM, 0);

    struct sockaddr_in  inet_addr;
    inet_addr.sin_family      = AF_INET;
    inet_addr.sin_addr.s_addr = inet_addr("192.168.1.1");
    inet_addr.sin_port        = htons(inet_port);

    bind(fd, (struct sockaddr *) &inet_addr, sizeof(inet_addr));

    ------------------------------------------------------------------

    UNIX Domain socket: 进程间通信常用

    unix_socket = socket(AF_LOCAL, SOCK_STREAM, 0); 或者
    unix_socket = socket(AF_LOCAL, SOCK_DGRAM, 0);

    struct sockaddr_un  un_addr;

    unlink("my_file_path");
    un_addr.sun_family = AF_LOCAL;
    strcpy(un_addr.sun_path, "my_file_path");

    bind(fd, (struct sockaddr *) &un_addr, sizeof(un_addr));

    ----------------------------------------------------------------------
    RAW socket: 用来处理IP包,它最多只能触及IP包头,再往下就搞不定了

    inet_raw_fd = socket(AF_INET, SOCK_RAW, my_protocol);
    struct sockaddr_in    inet_addr;


    inet_addr.sin_family = AF_INET;
    inet_addr.sin_addr.s_addr = inet_addr("192.168.1.1");

    int on = 1;  /*  1 用户程序处理IP头,  0 用户程序不处理IP头 */
    setsockopt(inet_raw_fd,IPPROTO_IP,IP_HDRINCL,(char *)&on,sizeof(on));

    形参 my_protocol,可选值为下面的任意组合
    IPPROTO_UDP 
    IPPROTO_TCP 
    IPPROTO_ICMP  

    IPPROTO_RAW

    ----------------------------------------------------------------------
    AF_PACKET socket: 用来处理以太网包,它能修改以太网包头

    un_cooked_fd = socket(AF_PACKET, SOCK_RAW, my_protocol); //用户程序处理 以太网头
    cooked_fd = socket(AF_PACKET, SOCK_DGRAM, my_protocol);  //用户程序不处理以太网头

    struct sockaddr_ll ll_addr;

    struct ifreq if_idx;
    strncpy(if_idx.ifr_name, "eth0", IFNAMSIZ-1);
    ioctl(fd, SIOCGIFINDEX, &if_idx);

    ll_addr.sll_family = AF_PACKET;
    ll_addr.sll_protocol = htons(ETH_P_ALL);
    ll_addr.sll_ifindex = if_idx.ifr_ifindex;

    bind(sockfd, (struct sockaddr *)&ll_addr, sizeof(ll_addr)) ;

    形参 my_protocol,可选值为下面的任意组合
    ETH_P_IP
    ETH_P_ARP
    ETH_P_RARP

    ETH_P_ALL

     pingpang_socket.rar   
    -------------------------------------------------------------------------

    NetLink socket: TODO

    ############################################################################
        
    accept 调用前连接夭折:    
          SVR4 实现:    返回错误码
          Berkeley实现: 服务器进程看不到

    服务器fork出来的子进程挂了:
        a.父进程 先前已经close了 connfd
        b.子进程 挂会导致释放 connfd
        c.connfd 描述字释放后,会导致发送 FIN 给客户端
        d.客户发 ACK
        e.客户发 数据 (引发server发RST)
        客户read此时返回(只接受到FIN,返回0,errno "server terminated prematurely" 服务器过早终止)
        f.服务 发 RST(防止主动关闭的一端经历 TIME_WAIT 状态)
        客户 read 此时返回(接受到FIN 和 RST, RST 优先级高,返回,errno "ECONNREAET" 对方复位连接)
        g.客户再发数据 (引发SIGPIPE信号)

            
    服务器主机挂了
        客户发数据
        read 返回错误 完全没响应 ETIMEOUT
        中间路由器判断服务器主机不可达,发目的地不可达的ICMP消息响应,错误 EHOSTUNREACH 或 ENETUNREACH


    服务器主机崩溃后重启
        服务器崩溃后重启,他的TCP丢失了连接信息
        客户发数据
            服务发RST
            客户read 返回错误 ECONNRESET

    daytime 服务器 accept 后 write 后马上 close ??    

      
    从进程到内核传递套接字地址结构有3个函数:bind connect sendto
    他们的形参中都有 *sockaddr 和 结构大小sizeof(serv)
        bind(sockfd,(struct sockaddr *)&serv,sizeof(serv) );

    从内核到进程传递套接字地址结构有4个函数:accept recvfrom getsocketname getpeername
    他们的形参中都有 *sockaddr 和 指向结构大小的整数的“指针”  
        struct sockaddr_un test;
        socklen_t len = sizeof(test);
        getpeername(unixfd,(* sockaddr)&test,&len);
        /* len 可能会变,这时len代表test的写后大小*/

    为何将结构大小由整数改为指向整数的指针呢?
    这样len可以被函数更新
    当函数被调用时,结构大小是一个值(此值告诉内核在写结构时不至于越界),
    当函数返回时,结构大小又是一个结果(它告诉进程,内核在此结构中确切存储了多少信息),
    这种参数类型叫做“值-结果”参数


    客户在读回任何东西之前对服务器写两次,而第一次写就引发了一个RST.(第二次写生成SIGPIPE)
    所用规则是:当一个进程(client)向接受了RST的套接字(server)进行写操作时,内核给该进程(client)
    发一个SIGPIPE信号。此信号的缺省行为就是终止进程(client),所以进程(client)必须捕获它以免被终止

    client不论是捕获了该信号并从信号处理程序返回,还是不理会该信号,client写操作都返回EPIPE错误



    网络编程时可能会遇到的三种情况:
    1 当派生子进程时,必须捕获信号
    2 当捕获信号时,必须处理被中断的系统调用
    3 SIGCHLD的信号处理程序应使用函数waitpid以免留下僵尸进程


    在客户断和服务器可以通信之前,每一方都要指定连接的套接字对:
        本地IP 和 本地端口    
            两端都可以调用bind(通常客户不调,由内核选择,内核选后由getsockname探知)
        远程IP 和 远程端口    
            客户调用connect;服务器端通过accept返回这2个值。
            若accept返回后由新程序来处理,且调用exec来执行,则在新程序中必要时
            调用getpeername来确定客户的IP地址和端口号。(前提是connfd要传给exec)


    关于异构系统socket通信时大端小端问题,常用解决方法:
    1。所有的数值数据作为文本串来传递。当然,保证通讯两主机要有相同的字符集
    2。显示定义所支持数据类型的二进制格式(位数,大端,小端)。远程过程调用(RPC)软件包常用此技术


    拒绝服务型攻击
       当一个服务器正在处理多个客户时,服务器决不能阻塞于只与单个用户相关的函数调用。如果这样的话
    服务器将悬挂并拒绝为所有其他客户提供服务,这叫拒绝服务(denial of service)型攻击。
       可能的解决方法:(a)使用非阻塞I/O模型
              (b)让每个客户由单独的控制线程提供服务(例如,创建子进程或线程来为每个客户提供服务)
              (c)对I/O操作设置超时

    有3种方法给套接字上的I/O操作设置超时
    1. 调用alarm(),这涉及到信号处理,可能与进程中其他已有的alarm调用冲突
    2. 使用select阻塞在等待I/O上,select内部有一个时间限制,以此来代替在read或write调用上的阻塞
    3. 使用新的SO_RECVTIMEO SO_SNDTIMEO套接字选项,但并不是所有实现都支持这两个套接字选项。


    ###############################################################################################

    #include <sys/socket.h>
    ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags);
    ssize_t send(int sockfd, const void *buf, size_t nbytes, int flags);

    这连个函数和标准的read和write函数很相似,前三个参数意义和read write 相同。
    只是多了一个附加的参数flags (0 或者一些宏的逻辑或)
                   recv    send
    MSG_DONTROUTE           Y
    MSG_DONTWAIT    Y       Y
    MSG_OOB         Y       Y
    MSG_PEEK        Y
    MSG_WAITTALL    Y

    参考:

    http://blog.chinaunix.net/uid-24148050-id-3161566.html

  • 相关阅读:
    团队绩效打分
    软件对标分析
    目前校园百晓生APP与CSDN软件的对比
    Alpha版
    团队项目第一阶段成果展示
    意见汇总
    团队第一阶段冲刺评价
    冲刺(十)
    【WPF学习】第五十八章 理解逻辑树和可视化树
    【WPF学习】第五十七章 使用代码创建故事板
  • 原文地址:https://www.cnblogs.com/yaozhongxiao/p/3020495.html
Copyright © 2020-2023  润新知