linux下网络编程

 

网络编程

通信协议

TCP协议

TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议。即客户端和服务器之间在交换数据之前会先建立一个TCP连接,才能相互传输数据。并且提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。

  • TCP的优点 :可靠,稳定 ,TCP的可靠体现在TCP在传递数据之前,会有三次握手来建立连接,而且在数据传递时,有确认、窗口、重传、拥塞控制机制,在数据传完后,还会断开连接用来节约系统资源。

  • TCP的缺点 :慢,效率低,占用系统资源高,易被攻击 ,TCP在传递数据之前,要先建连接,这会消耗时间,而且在数据传递时,确认机制、重传机制、拥塞控制机制等都会消耗大量的时间,而且要在每台设备上维护所有的传输连接,事实上,每个连接都会占用系统的CPU、内存等硬件资源。由于TCP存在确认机制和三次握手机制,这些是导致TCP容易被人利用,实现DOS、DDOS、CC等攻击。

  • TCP应用场景 :当对网络通讯质量有要求的时候,比如:整个数据要准确无误的传递给对方,这往往用于一些要求可靠的应用,比如HTTP、HTTPS、FTP等传输文件的协议,POP、SMTP等邮件传输的协议。在日常生活中,常见使用TCP协议的应用比如:浏览器使用HTTP,Outlook使用POP、SMTP,QQ文件传输等。

UDP协议

UDP是用户数据包协议,是一个简单的面向数据报的运输层协议。它不提供可靠性,只是把应用程序传给IP层的数据报发送出去,但是不能保证它们能到达目的地。由于UDP在传输数据报前不用再客户和服务器之间建立一个连接,且没有超时重发等机制,所以传输速度很快。

  • UDP的优点 :UDP没有TCP的握手、确认、窗口、重传、拥塞控制等机制,UDP是一个无状态的传输协议,所以它在传递数据时非常快。没有TCP的这些机制,UDP较TCP被攻击者利用的漏洞就要少一些。但UDP也是无法避免攻击的,比如:UDP Flood攻击……

  • UDP的缺点 :不可靠,不稳定 ,因为UDP没有TCP那些可靠的机制,在数据传递时,如果网络质量不好,就会很容易丢包。

  • UDP应用场景 :当对网络通讯质量要求不高的时候,要求网络通讯速度能尽量的快,这时就可以使用UDP。在日常生活中,常见使用UDP协议的应用比如:QQ语音、QQ视频、TFTP等。TCP和UDP使用IP协议从一个网络传送数据包到另一个网络。把IP想像成一种高速公路,它允许其它协议在上面行驶并找到到其它电脑的出口。TCP和UDP是高速公路上的“卡车”,它们携带的货物就是像HTTP,文件传输协议FTP这样的协议等

基础API(基于Linux)

字节序转换(本地字节序、网络字节序)

#include <arpa/inet.h>

uint32_t htonl(uint32_t hostlong);

uint16_t htons(uint16_t hostshort);

uint32_t ntohl(uint32_t netlong);

uint16_t ntohs(uint16_t netshort);

socket

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

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

socket()为通信创建一个端点,并返回一个指向该端点的文件描述符。 调用成功后返回的文件描述符将是当前没有为进程打开的最低编号的文件描述符。

  1. 参数domain:

     AF_UNIX, AF_LOCAL   Local communication              unix
     AF_INET             IPv4 Internet protocols          ip
     AF_INET6            IPv6 Internet protocols          ipv6
     AF_IPX              IPX - Novell protocols
     AF_NETLINK          Kernel user interface device     netlink
     AF_X25              ITU-T X.25 / ISO-8208 protocol   x25
     AF_AX25             Amateur radio AX.25 protocol
     AF_ATMPVC           Access to raw ATM PVCs
     AF_APPLETALK        AppleTalk                        ddp
     AF_PACKET           Low level packet interface       packet
     AF_ALG              Interface to kernel crypto API
    
  2. 参数type:

     SOCK_STREAM     Provides sequenced, reliable, two-way, connection-based byte streams.  An out-of-band data transmission mechanism may be supported.
    
     SOCK_DGRAM      Supports datagrams connectionless, unreliable messages of a fixed maximum length.
    
     SOCK_SEQPACKET  Provides a sequenced, reliable, two-way connection-based data transmission path for datagrams of fixed maximum length; a consumer is required to read an entire packet with each input system call.
    
     SOCK_RAW        Provides raw network protocol access.
    
     SOCK_RDM        Provides a reliable datagram layer that does not guarantee ordering.
    
     SOCK_PACKET     Obsolete and should not be used in new programs; see packet.
    
     SOCK_NONBLOCK   Set the O_NONBLOCK file status flag on the new open file description.  Using this flag saves extra calls to fcntl to achieve the same result.
           
     SOCK_CLOEXEC    Set the close-on-exec FD_CLOEXEC flag on the new file descriptor.  See the description of the O_CLOEXEC flag in open for reasons why this may be useful.
    
  3. 参数protocol:

     一般为 0 
    
  4. 返回值return: 成功时,返回一个新套接字的文件描述符。 错误时,返回-1,并适当地设置errno。 errno: 常见errno

     EACCES : Permission to create a socket of the specified type and/or protocol is denied.
    
     EAFNOSUPPORT : The implementation does not support the specified address family.
    
     EINVAL : Unknown protocol, or protocol family not available.
    
     EINVAL : Invalid flags in type.
        
     EMFILE : The per-process limit on the number of open file descriptors has been reached.
        
     ENFILE : The system-wide limit on the total number of open files has been reached.
        
     ENOBUFS :
     ENOMEM : Insufficient memory is available.  The socket cannot be created until sufficient resources are freed.
        
     EPROTONOSUPPORT : The protocol type or the specified protocol is not supported within this domain.
        
     Other errors may be generated by the underlying protocol modules.
    

bind

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

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

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

On success, zero is returned.  On error, -1 is returned, and errno is set appropriately.

listen

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

int listen(int sockfd, int backlog);

On success, zero is returned.  On error, -1 is returned, and errno is set appropriately.

accept

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

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


On success, these system calls return a nonnegative integer that is a file descriptor for the accepted socket.
On error,  -1  is  returned,  and errno is set appropriately.

connect

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

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

read/recv/recvform

#include <unistd.h>

size_t read(int fd, void *buf, size_t count);
#include <sys/types.h>
#include <sys/socket.h>

size_t recv(int sockfd, void *buf, size_t len, int flags);

size_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                        struct sockaddr *src_addr, socklen_t *addrlen);

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

write/send/sendto

#include <unistd.h>

size_t write(int fd, const void *buf, size_t count);

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

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

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

size_t sendmsg(int sockfd, const struct msghdr *msg, int flags);

sockopt

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

int getsockopt(int sockfd, int level, int optname,
                      void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname,
                      const void *optval, socklen_t optlen);
  • sockfd: 套接字描述符
  • level: 选项定义层次,SOL_SOCKET、IPPROTO_TCP、IPPROTO_IP
  • optval: 指针,指向存放选项值的缓冲区 int opt=1;(const char *)&opt
  • optlen: optval缓冲区长度 sizeof(opt)
  • optname:需要设置的选项,在SOL_SOCKET下,optname选项有:
    SO_DEBUG        //打开或关闭调试信息。
    // 当option_value不等于0时,打开调试信息,否则,关闭调试信息
    SO_REUSEADDR    //打开或关闭地址复用功能。
    // 当option_value不等于0时,打开,否则,关闭。
    SO_DONTROUTE    //打开或关闭路由查找功能。
    // 当option_value不等于0时,打开,否则,关闭。
    SO_BROADCAST    //允许或禁止发送广播数据。
    // 当option_value不等于0时,允许,否则,禁止。
    SO_SNDBUF       //设置发送缓冲区的大小。
    // 发送缓冲区的大小是有上下限的
    // 其上限为256 * (sizeof(struct sk_buff) + 256),下限为2048字节。
    // 该操作将sock->sk->sk_sndbuf设置为val * 2,之所以要乘以2,是防止大数据量的发送,突然导致缓冲区溢出。最后,该操作完成后,因为对发送缓冲的大小 作了改变,要检查sleep队列,如果有进程正在等待写,将它们唤醒。
    SO_RCVBUF       //设置接收缓冲区的大小。
    // 接收缓冲区大小的上下限分别是:256 * (sizeof(struct sk_buff) + 256)和256字节。
    // 该操作将sock->sk->sk_rcvbuf设置为val * 2。
    SO_KEEPALIVE    //套接字保活。
    // 如果协议是TCP,并且当前的套接字状态不是侦听(listen)或关闭(close),
    // 那么,当option_value不是零时,启用TCP保活定时 器,否则关闭保活定时器。对于所有协议,该操作都会根据option_value置或清 sock->sk->sk_flag中的 SOCK_KEEPOPEN位。
    SO_OOBINLINE    //紧急数据放入普通数据流。
    SO_NO_CHECK     //打开或关闭校验和。
    SO_PRIORITY     //设置在套接字发送的所有包的协议定义优先权。
    // 这个值在0到6之间(包括0和6),由option_value指定。
    // 赋给sock->sk->sk_priority。
    SO_LINGER       
    //如果选择此选项, close或 shutdown将等到所有套接字里排队的消息成功发送或到达延迟时间后>才会返回. 
    // 否则, 调用将立即返回。
    /*
    该选项的参数(option_value)是一个linger结构:
        struct linger {
            int   l_onoff;   
            int   l_linger;  
        };
如果linger.l_onoff值为0(关闭),则清 sock->sk->sk_flag中的SOCK_LINGER位;否则,置该位,并赋sk->sk_lingertime值为 linger.l_linger。
    */
    SO_PASSCRED     //允许或禁止SCM_CREDENTIALS 控制消息的接收。
    SO_TIMESTAMP    //打开或关闭数据报中的时间戳接收。
    SO_RCVLOWAT     //设置接收数据前的缓冲区内的最小字节数。
    SO_RCVTIMEO     //设置接收超时时间。
    SO_SNDTIMEO     //设置发送超时时间。
    SO_BINDTODEVICE //将套接字绑定到一个特定的设备上。
    SO_ATTACH_FILTERSO_DETACH_FILTER  //关于数据包过滤,它们最终会影响sk->sk_filter。

close

#include <unistd.h>

int close(int fd);

通信流程

TCP

服务器

  • 服务器端创建 socket -> 绑定 IP 和端口号 -> 启动侦听 ->接受客户端连接 ->与客户端通信收发数据
socket=>start: socket
bind=>operation: bind
listen=>operation: listen
accept=>operation: accept
recv=>operation: recv/send
close=>end: close
socket(right)->bind(right)->listen(right)->accept(right)->recv(right)->close

客户端

  • 客户端创建 socket -> 连接 server ->收发数据;
socket=>start: socket
connect=>operation: connect
recv=>operation: recv/send
close=>end: close
socket(right)->connect(right)->recv(right)->close

UDP

服务器

  • 对于udp来讲,可以没有服务器,服务器只是认为规定的一种 主动发送的一方为客户端,被动接受的一方为服务器
  • 如果作为服务器被动等待别人发送数据,这个服务器需要绑定上固定的ip和端口
  • 创建套接字 -> 绑定(非必须的) ->读写 -> 关闭
socket=>start: socket
bind=>operation: bind
recv=>operation: recvfrom/sendto
close=>end: close
socket(right)->bind(right)->recv(right)->close

客户端

  • 创建套接字 -> 绑定(非必须的) ->读写 -> 关闭
socket=>start: socket
bind=>operation: bind
recv=>operation: recvfrom/sendto
close=>end: close
socket(right)->bind(right)->recv(right)->close

知识点/问题

  1. 基础网络通信原理(如三次握手和四次挥手)
  2. socket 基础 API 的使用
  3. 客户端与服务器端网络通信的流程
  4. TCP 与 UDP 的区别
  5. select 函数的用法
  6. 非阻塞 connect 函数的写法
  7. epoll 的水平和边缘模式
  8. 阻塞socket与非阻塞 socket 的区别
  9. send/recv 函数的返回值情形
  10. REUSE_ADDR 选项
  11. 完成端口(IOCP 模型)
  12. nagle 算法
  13. keepalive 选项
  14. Linger 选项
  15. 对于某一端出现大量 CLOSE_WAIT 或者 TIME_WAIT 如何解决
  16. 通讯协议如何设计或如何解决数据包的粘包与分片问题 粘包问题链接
  17. 心跳机制如何设计(如何检查死链)
  18. 断线重连机制如何设计;
  19. 对 IO Multiplexing 技术的理解;
  20. 收发数据包正确的方式,收发缓冲区如何设计;
  21. 优雅关闭;
  22. 定时器如何设计;
  23. epoll 的实现原理。
  24. 如果 A 机器与 B 机器网络 connect 成功后从未互发过数据,此时其中一机器突然断电,则另外一台机器与断电的机器之间的网络连接处于哪种状态?