• socket 学习笔记


    #include <sys/socket.h>
    ---------------------------------------------------------------------------------
    1. int socket(int domain, int type, int protocol)
       socket: return fd
       domain: AF_INET(IPv4), AF_INET6(IPv6)
       type: SOCK_DGRAM(udp), SOCK_STREAM(tcp), SOCK_RAM(ip)
       protocol: 0(usually)
    ---------------------------------------------------------------------------------   
    2. int shutdown(int sockfd, int how)
       how: SHUT_RD(read end) SHUT_RDWR(read end)
    ---------------------------------------------------------------------------------
    3. network/host byte order
      
       network: (TCP/IP: big-endian)
       host:    (little-endian: x86, x86-64, linux, FreeBSD, )
       
       #include <arpa/inet.h> or #include <netinet/in.h> (old)
       htonl: host to network long (32bit)
       htons: host to network short(16bit)
       ntohl: network to host long (32bit)
       ntohs: network to host short (16bit)
    ---------------------------------------------------------------------------------
    4. socket address format
    #include <netinet/in.h>
     struct sockaddr {
       sa_family-t sa_family;
       char        sa_data[];
       ...
     };
     
     struct sockaddr_in {
       sa_family_t sin_family;
       in_port_t   sin_port;
       struct in_addr sin_addr;
     };
     
     struct in_addr {
       in_addr_t s_addr;
     };
     
     example:
        struct sockaddr_in addr;
    socklen_t addrlen = sizeof(struct sockaddr_in);
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr("127.0.0.1");
    addr.sin_port = htons(dtls_local_port);
    connect(socket_fd, (struct sockaddr*)&addr, addrlen);
    ---------------------------------------------------------------------------------
    5. bind (tcp/udp server端都需要bind)
    int bind(int sockfd, const struct sockaddr *addr, socklen_t len);
     
     1) 端口号必须大于1024,否则需要超级用户权限
     2) 只有server需要bind, 客户端套接字bind没什么意义
     3) 如果调用connect/listen,但没有bind,系统会选一个地扯将其bind到socket
     
    int initserver(int type, const struct sockaddr *addr, socklen_t alen, int qlen)
    {
        int fd;
        int err = 0;
        if((fd = socket(addr->sa_family, type, 0)) <0)
            return -1;
        if(bind(fd, addr, alen) < 0) {
            err = errno;
            goto errout;
        }
        if(type == SOCK_STREAM || type == SOCK_SEQPACKET) {
            if(listen(fd, qlen) < 0) {
                err = errno;
                goto errout;
            }
        }
        return fd;
    errout:
        close(fd);
        errno = err;
        return -1;
    }
    ---------------------------------------------------------------------------------
    6.connect 面向连接的socket要用connect建立连接,无连接socket不是必须,但也可使用
     
    ***函数connect还可以用于无连接的网络服务(SOCK_DGRAM),如果在SOCK_DGRAM套接字上调用connect,
    所有发送报文的目标地址设为connect调用中所指定的地址,这样每次传送报文时就不需要提供地址,
    另外仅能接收来自指定地址的报文。
     
     1) int connect(int sockfd, const struct sockaddr *addr, socklen_t len);
     
      example:
    #define MAXSLEEP 128
    int connect_retry(int sockfd, const struct sockaddr *addr, socklen_t alen)
    {
       int nsec;
       for(nsec=1; nsec <= MAXSLEEP; nsec <<= 1) {
           if(connect(sockfd, addr, alen) == 0){
         return 0; //connection accecpt 
      }
      if(nsec <= MAXSLEEP/2)
         sleep(nsec);
        }
    return(-1);
    }
     
     2) int listen(int sockfd, int backlog)
    backlog: 表示该进程所要入队的连接请求数量
    server调用listen宣告可以接受连接请求
     
     3) accept
      int accept(int sockfd, struct sockaddr *restrict addr, socklen_t *restrict len);
      server调用accept接受连接,如果没有连接请求到来,accept会阻塞直到一个请求到来。
      如果sockfd是非阻塞模式,accept会return -1
      accept返回一个fd,这个fd与sockfd套接字类型和地址族相同,原套接字继续保持listen
    ---------------------------------------------------------------------------------
    9. send/receive data
      0) read/write data from fd
     
      1)ssize_t send(int sockfd, const void *buf, size_t nbyts, int flags);
        使用send时套接字必须已经连接, 可忽略目的地址,或者在connect时设计目的地址
      2)ssize_t sendto(int sockfd, const void *buf, size_t nbyts, int flags,
                     const struct sockaddr *destaddr, socklen-t destlen);
        对于无连接的socket,可用sendto
      3)ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
        可以指定多重缓冲区传输数据
     
      01)ssize_t recv(int sockfd, void *buf, size_t nbytes, int flags);
        面向连接
      02) ssize_t recvfrom(int sockfd, void *restrict buf, size_t len, int flags, 
                           struct sockaddr *restrict addr, socklen_t *restrict addrlen);
    可用于无连接socket, 如果addr为空,recvfrom等同于recv
      03) ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
     ---------------------------------------------------------------------------------
     10. socket option
     int setsockopt(int sockfd, int level, int option, const void *val, socklen_t len);
     int getsockopt(int sockfd, int level, int option, const void *val, socklen_t * restrict lenp);
     
     
     ---------------------------------------------------------------------------------
     
     11.异步
     fcntl(fd, F_SETFL, O_NONBLOCK);
     
     1)select
     int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
     
    缺点:
         1). nfds有限制,最大只能是1024
         2). 知道有事件到来,还要遍历到底是哪个fd触发的
         3).不能动态的修改fdset, 或者关闭某个socket
         4)、需要维护一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大
     
    优点:(select如此的老,我们还需要它吗?)
        1). 更好的兼容老的系统
        2). select 计算超时可以达到纳秒精度,而poll, epoll只能达到毫秒的精度。client/server用不着这么高,但嵌入式系统用的着。
        事实上,如果你写一个不超过200个socket程序,select和poll, epool性能上没啥区别。
     
     
     2)poll
     int poll(struct pollfd *fds,nfds_t nfds, int timeout);
     
    缺点:
         1). 知道有事件到来,还要遍历到底是哪个fd触发的
         2).不能动态的修改fdset, 或者关闭某个socket
     
    优点: 
         1)没有fd个数限制
          
    使用场景:
        1)多平台支持,不仅仅是linux,你又不想使用libevent
         2)不超过1000个sockets
         3)超过1000个socket,但是socket都是short-lived的
         4)Your application is not designed the way that it changes the events while another thread is waiting for them 
     
     
    3)epool   (epoll is Linux only)
    int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
        
        1)Your application runs a thread poll which handles many network connections by a handful of threads.
            单线程应用使用epool得不偿失,不比pool好
        2)至少1000个以上的socket,socket
        3)epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著
        提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。另一点原因就是获取事件的时候,它无须遍历整个被侦听
        的描述符集,只要遍历那些被内核IO事件异步唤醒而加入Ready队列的描述符集合就行了。
     
         epoll除了提供select/poll那种IO事件的水平触发(Level Triggered)外,还提供了边缘触发(Edge Triggered),这就使得用户空间程序有可能缓存IO状态,
        减少epoll_wait/epoll_pwait的调用,提高应用程序效率。
     
         UDP不能使用ET,否则有可能丢失数据  //Modify 2006.4.4 UDP can not use edge model, otherwise some data will miss
         LT是默认的模式,ET是“高速”模式。
         LT模式下,只要这个fd还有数据可读,每次 epoll_wait都会返回它的事件,提醒用户程序去操作,
         ET(边缘触发)模式中,它只会提示一次,直到下次再有数据流入之前都不会再提示了,无 论fd中是否还有数据可读。
         ET模式下,read一个fd的时候一定要把它的buffer读光,也就是说一直读到read的返回值小于请求值,或者 遇到EAGAIN错误,否则会丢失数据。
      
     
     
     
     
     
       
  • 相关阅读:
    设计模式:singleton模式
    设计模式:factory method模式
    设计模式:template method模式
    设计模式:Adapter模式
    设计模式:Iterator模式
    MySQL数据库---数据库备份、视图、触发器、事物、存储过程、函数和索引
    MySQL数据库---记录相关操作
    C语言经典面试题 与 C语言面试宝典
    [置顶] C语言中 || 和 &&
    linux 创建连接命令 ln -s 软连接
  • 原文地址:https://www.cnblogs.com/awiki/p/5521828.html
Copyright © 2020-2023  润新知