美文网首页我爱编程
Linux网络编程1:TCP编程基础

Linux网络编程1:TCP编程基础

作者: AmberXiao | 来源:发表于2018-05-29 22:38 被阅读2次

    参考书:《Linux网络编程(第2版)》,宋敬彬等编著。

    1.TCP通信流程

    tcp连接过程
    图片来源:https://blog.csdn.net/u012234115/article/details/54142273

    2. 套接字基础知识

    通用套接字数据结构

    struct sockaddr{  //套接字地址结构
      sa_family_t sa_family, //协议族,类型为usigned_short
      char sa_data[14] //协议族数据
    }
    

    实际使用的套接字数据结构

    struct sockaddr_in {  //以太网套接字地址结构
      u8 sin_len, //本结构体的长度
      u8 sin_family, //协议族,通常与socket的domain相同
      u16 sin_port, //16位的端口号,网络字节序
      struct in_addr sin_addr, //IP地址,32位
      char sin_zero[8] //未用
    }
    struct in_addr{ //IP地址结构
      u32 s_addr  //32位IP地址,网络字节序
    }
    

    sockaddr_in.sin_family一般取值AF_INET表示TCP/IP协议。
    sockaddr与sockaddr_in的大小完全一致,因此通常使用sockaddr_in进行设置,然后强制转换为sockaddr。

    3. 创建套接字函数 socket()

    socke()函数建立一个套接字文件描述符,调用成功返回文件描述符,失败返回-1.

    #include <sys/types.h>
    #include <sys/socket.h>
    int socket(
      int domain,  //设置网络通信域,根据此参数选择协议族
      int type, //设置套接字类型
      int protocol //用于指定某个协议的特定类型
    )
    

    domain

    名称 含义 名称 含义
    PF_UNIX,PF_LOCAL 本地通信 PF_X25 ITU-T X.25/ISO-8208协议
    PF_INET,AF_INET IPv4 Internet协议 PF_AX25 amateur radio AX.25协议
    PF_INET6 IPv6 Internet协议 PF_ATMPVC 原始ATM PVC访问
    PF_IPX IPX-Novell协议 PF_APPLETALK appletalk
    PF_NETLINK 内核用户界面设备 PF_PACKET 底层包访问

    type

    名称 含义
    SOCK_STREAM Tcp连接,支持带外数据传输
    SOCK_DGRAM 支持udp连接
    SOCK_SEQPACKET 序列化包
    SOCK_RAW Raw类型,提供原始网络协议访问
    SOCK_RDM 提供可靠的数据报文,不过可能数据会有乱序
    SOCK_PACKET 专用类型,不能在通用程序中使用

    SOCK_STREAM的套接字表示双向的字节流,与管道类似,流式套接字在进行通信之前必须已经使用connect()建立连接。连接中可以使用read() 和write()进行数据传输。流式通信保证数据不会丢失或重复接受,当数据在一定时间内仍没有接受完毕,可认为这个连接已经死掉。

    protocol
    用于指定某个协议的特定类型,通常协议中只有一种类型,protocol取值为0.

    errno

    含义
    EACCES 没有权限建立制定的domain的type的socket
    EAFANOSUPPORT 不支持所给的地址类型
    EINVAL 不支持此协议或者协议不可用
    EMFILE 进程文件表溢出
    ENFILE 已经达到系统允许打开的文件数量,打开文件过多
    ENOBUFS/ENOMEM 内存不足。socket只有到资源足够或者有进程释放内存
    EPROTONOSUPPORT 制定的协议type在domain中不存在
    其他

    4. 绑定地址端口 bind()

    bind()函数将套接字和指定的端口进行绑定,如果使用connect()函数则没有绑定的必要。绑定成功返回0,失败返回-1.

    #include <sys/types.h>
    #include <sys/socket.h>
    int bind(
      int sockfd, //文件描述符
      const struct sockaddr *my_addr, //sockaddr结构体指针
      socket_t addrlen //sockaddr结构体的长度
    )
    

    errno

    含义 备注
    EADDRINUSE 给定地址已用
    EBADF sockfd不合法
    EINVAL sockfd已经绑定到其他地址
    ENOTSOCKET sockfd不是socket描述符
    EACCESS 地址被保护,用户权限不足
    待补充

    5. 监听本地端口 listen()

    服务器顺序处理客户端连接,同一时间只能处理一个客户端连接。当多个客户端连接请求同时到来时,将不能处理的客户端连接请求放到等待队列中,队列长度由listen()定义。
    listen()函数仅对SOCK_STREAM和SOCK_SEQPACKET的协议有效。
    设置成功返回0,失败返回-1.

    #include <sys/socket.h>
    int listen(
      int sockfd, //侦听的描述符
      int backlog //队列最大长度
    )
    

    errno

    含义
    EADDRINUSE 另一个socket已经在同一端口侦听
    EBADF sockfd不合法
    ENOTSOCK sockfd不是socket的描述符
    EOPNOTSUPP socket不支持listen操作

    6. 接收一个网络请求 accept()

    当一个客户端的请求到达服务器时,会在队列中等待,直到服务器处理请求。
    accept()函数执行成功,返回表示客户端连接的socket描述符,失败返回-1.

    #include <sys/types.h>
    #include <sys/socket.h>
    int accept(
      int sockfd, //监听的文件描述符
      struct sockaddr *addr, // 存储客户端信息的结构体
      socket_len *addrlen //上结构体的长度
    )
    

    accept()函数调用成功后,会将客户端的信息存储在*addr指向的结构体中。
    errno

    含义
    待补充

    7. 连接目标服务器 connect()

    客户端建立套接字之后,不需要进行绑定就可以使用connect()函数直接连接服务器。connect()成功返回0,失败返回-1.

    #include <sys/types.h>
    #include <sys/socket.h>
    int connect(
      int sockfd, //需要与服务器连接的文件描述符
      struct sockaddr *addr, //包含服务器信息的结构体
      int addrlen // 上述结构体的长度
    )
    

    errno

    含义
    待补充

    8. 写入函数 write() & send()

    使用write()对套接字写入的方式与对普通文件的操作一样。

    ssize_t write(
      int fd, //文件描述符
      const void *buf, //数据缓存区
      size_t nbytes//写入字符的大小
    );
    

    返回值大于0:返回写入数据的大小,并不一定写入了指定的全部数据,所以一般放在while循环里。
    返回值小于0:EINTR表示在写的时候出现了中断错误。EPIPE表示网络连接出现问题。

    send()函数功能与write()类似,提供了第四个函数控制发送操作。

     int send(
      int sockfd,//文件描述符
      void *buf,//数据缓存区
      int len,//数据大小
      int flags
    );
    
    flags值 含义
    MSG_DONTROUTE send函数使用的标志,表示目的主机在本地网络上,不需要查找表
    MSG_OOB 可以接受和发送带外的数据

    9. 读取函数 read() & recv()

    read()从套接字中读取数据。

    ssize_t read(
      int fd,//文件描述符
      void *buf,//数据缓存区
      int length//要读的数据大小
    );
    

    返回值等于0:已经读到文件结尾。
    返回值大于0:实际读的数据大小。
    返回值小于0:与write相同。

    recv()功能与read()类似,添加了第四个参数控制接受操作。

    int recv(
      int sockfd,//文件描述符
      void *buf,//数据缓存区
      int len,//数据大小
      int flags
    );
    
    flags值 含义
    MSG_OOB 可以接受和发送带外的数据
    MSG_PEEK recv使用的标志,只从缓冲区读取数据而清除其内容,下次读时仍然是一样的内容,一般用在多进程读写数据时
    MSG_WAITALL recv使用的标志,表示等到所有信息都到达时才返回。当1.读到指定数据大小,返回len2.读到文件结尾返回值小于等于len3.操作发生错误时返回-1

    10. 关闭套接字 close() & shutdown()

    关闭连接可以用close()实现,关闭之后就不再使用此描述符进行数据收发。

    #include <sys/socket.h>
    int close( int sockfd)
    

    shutdown()函数可以使用更多方式关闭连接,调用成功返回0,失败返回-1。

    #include <sys/socket.h>
    int shutdown(
      int sockfd, // 要关闭的描述符
      int how //关闭方式
    )
    

    how

    含义
    SHUT_RD 0,切断读,不能通过此方式进行读操作
    SHUT_WR 1,切断写,不能通过此方式进行写操作
    SHUT_RDWR 2,切断读写,与close相同

    相关文章

      网友评论

        本文标题:Linux网络编程1:TCP编程基础

        本文链接:https://www.haomeiwen.com/subject/otzajftx.html