Linux网络编程

Posted qifeng1024

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux网络编程相关的知识,希望对你有一定的参考价值。

在学习网络编程之前,先了解一下OSI模型,以及TCP/IP协议和一些基础的知识

 

OSI模型(Open System Interconnection model 开放系统互联模型)

技术图片

 

 这是一个理想化的模型,实际上的TCP/IP协议跟这个模型还不太一样。

分别简单的理解一下这七层模型的意思

物理层:以二进制数据形式在物理媒体上传输数据,也就是最底层的硬件。

    比如各种接口 RS-232 RJ-45 都属于该层

数据链路层:通过物理网络链路提供数据传输,可以理解为硬件和硬件之间的通信

    比如IIC SPI就是属于该层

网络层:负责在源和终点之间建立连接,一般包括网络寻径,选择路由

传输层:提供端对端的网络数据流服务。后面将提到的TCP/IP协议也是属于这两层

会话层:解除或建立与别的接点的联系

表示层:提供多种功能用于应用层数据编码和转化,以确保应用层发送的消息能被其他人识别

    包括数据格式转化和加密 压缩。

应用层:这里的应用层并非我们pc上所使用的软件,而是向应用服务提供网络资源的API

 

 

在TCP/IP协议中,并没有完全照般OSI模型,而是对他进行了整合和拆分

一般我们使用的是一个比较通用的四层的模型

和刚才的模型对比一下:

技术图片

 

 在实际使用中,我们把他分为:应用层、传输层、网络层、和网络接口层(硬件层)

网络接口层:提供TCP/IP协议的数据结构和实际的硬件之间的接口

网络层:IP协议、RIP协议,负责数据包装 寻址和路由

传输层:TCP可靠的数据流运输服务 UDP不可靠的数据报服务

应用层:FTP文件传输协议、HTTP超文本传输协议、Telent远程终端协议、等

 

 

IP地址

Internet Protocol Address  互联网协议地址

每台联网的设备都有一个IP地址,IP地址是一个32位数,常被分成4个4字节的数

ip地址被分为下面几类:

技术图片

 

 

a.b.c.d 的形式,其中abcd都是 0-255的整数

那么问题就来了,一串这样的整数,早晚有一天会用完,毕竟255*255*255*255 = 4228250625

40亿,且不说因为格式等方面的问题,实际分配出去不足40亿。。

光是目前的手机电脑等能联网的设备,也肯定超出了40亿了。

而事实上在2011年IPV4的地址就已经用尽了。

为了解决这个问题,就诞生了IPV6。

IPV6也就是第六版的互联网协议。其地址长度为128位,比32位的IPV4拥有更多的可分配资源。

我们可以使用 ifconfig 查看机器的IP地址

技术图片

 

 

 

 

端口号

前面说的IP地址是你计算机的地址,你一台计算机里面肯定运行了很多程序。

每个应用程序就对应了不同的端口号

同时制定了正确的IP地址和端口号,才能准确的发送数据

UNIX操作系统因具有运行稳定、系统要求低、安全性高,而得到广泛应用。其伯克利套接字,发展较早,具有鲜明特点,例如:UNIX系统有保留端口号的概念。只有具有超级用户特权的进程才允许给它自己分配一个保留端口号,这些端口号介于1~1023之间,一些应用程序将它作为客户与服务器之间身份认证的一部分。大多数TCP/IP实现给临时端口分配1024~5000之间的端口号。大于5000的端口与是为其他服务器预留的(Internet上并不常用的服务)  。

 

 

TCP、UDP协议

TCP  Transmission Control Protocol  传输控制协议

UDP  User Datagram Protocol     用户数据报协议

TCP和UDP都可以使用IPV4或IPV6。

UDP不保证数据包会到达目的地,不保证数据先后顺序跨网络之后保持不变,也不保证只到达一次。

TCP也不能保证发送的数据一定会被对方端接收,但是他会不断的重试。

 

TCP在连接时需要进行三次握手,前面的博客有动图演示讲解

TCP三次握手四次挥手

形象一点说,就是这样一个过程:

1、屌丝(客户端)处于自闭状态,女神(服务器)处于等待状态

2、屌丝问女神”我要跟你处对象,是否同意“,同时屌丝变成发送状态

3、女神收到消息,回复屌丝“我同意,那么我要跟你处对象,你是否同意?”,然后变为接收状态

4、屌丝收到消息,变为“恋爱状态”,回复女神“我同意,开始处对象吧

5、女神收到消息,也变为恋爱状态,于是开始了一段不可描述的故事。。。。

有一天。。。他们要分手了。。

1、屌丝付出了自己所有的爱,然后跟女神说“我要跟你分手”,并把自己变为等待状态

2、女神收到爱和消息,回复“同意分手”,然后又把自己所有的爱给了屌丝,又一次确认“我要和你分手

3、屌丝收到爱和消息,回复“同意分手”,后进入自闭状态。

4、女神收到应答,也进入自闭状态。。。

 

上面例子中用用下划线标注出来的,就是建立连接的三次握手和数据发送完成后的四次挥手。

 

 

 

下面终于进入了网络编程的正题:套接字API的使用

创建套接字

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

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

       domain:

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


       protocol:

       SOCK_STREAM     TCP

       SOCK_DGRAM      UDP

       SOCK_SEQPACKET  为最大长度固定的数据报提供有序、可靠、基于双向连接的数据传输路径

       SOCK_RAW        原始套接字

       SOCK_RDM        提供不保证排序的可靠数据报层。

成功返回非负的套接字描述符

失败返回 -1

 

绑定套接字和服务器地址

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

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


       sockfd    套接字文件描述符

       addr     服务器地址信息
               struct sockaddr {
                   sa_family_t sa_family;
                   char        sa_data[14];
               }

               但实际使用中对于IPV4我们常用这个结构
               struct sockaddr_in {
                    sa_family_t    sin_family;         IPV4对应AF_INET
                    u_int16_t      sin_port;           端口号
                    struct in_addr sin_addr;           IP地址
                };

                /* Internet address. */
                struct in_addr {
                    u_int32_t      s_addr;              IP地址
                };

       addrlen addr的长度 sizeof(struct sockaddr)

成功返回 0  失败返回 -1

主要用与在TCP中的连接

 

监听模式

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

       int listen(int sockfd, int backlog);

       sockfd        套接字文件描述符

       backlog        监听队列长度(等待连接的客户端的个数)缺省值20

 

 

等待客户端连接

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

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

        sockfd        服务器套接字文件描述符

        addr         客户端信息地址

        addrlen     addr的长度    

成功返回连接的客户端的套接字文件描述符

失败返回 -1

 

接收数据

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

       ssize_t recv(int sockfd, void *buf, size_t len, 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);


       sockfd            套接字文件描述符

       buf                存放接收的数据

       len                期望接收的数据长度

       flags            0

       src_addr            源机的IP地址端口号

       addrlen            addr的长度

       msg
                   struct iovec {                    /* Scatter/gather array items */
                       void  *iov_base;              /* Starting address */
                       size_t iov_len;               /* Number of bytes to transfer */
                   };

                   struct msghdr {
                       void         *msg_name;       /* optional address */
                       socklen_t     msg_namelen;    /* size of address */
                       struct iovec *msg_iov;        /* scatter/gather array */
                       size_t        msg_iovlen;     /* # elements in msg_iov */
                       void         *msg_control;    /* ancillary data, see below */
                       size_t        msg_controllen; /* ancillary data buffer len */
                       int           msg_flags;      /* flags on received message */
                   };

成功返回接到数据的实际长度,失败返回 -1

recvfrom() 在参数中添加了源机的地址返回,可以得到是从哪里接收到的数据地址信息。

如果把此函数的第五个参数置空NULL,则和recv() 函数相同

 

 

发送数据

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

       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 struct msghdr *msg, int flags);

send函数参数和recv完全一致,此处不再赘述。

成功返回发送数据的实际长度,失败返回 -1

sendto() 指定地址发送数据,

sendto()  recvfrom() 主要用于UDP的连接

 

 

 

客户端连接服务器

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

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

参数为服务器地址信息

成功返回 0  失败返回 -1

connect() 函数不阻塞,所以在使用之前要确保服务器已经进入等待连接状态。

 

 

使用TCP协议的流程图

技术图片

 

 

UDP协议流程图

技术图片

 

以上是关于Linux网络编程的主要内容,如果未能解决你的问题,请参考以下文章

[linux][c/c++]代码片段01

VSCode自定义代码片段——JS中的面向对象编程

VSCode自定义代码片段9——JS中的面向对象编程

VSCode自定义代码片段14——Vue的axios网络请求封装

VSCode自定义代码片段14——Vue的axios网络请求封装

VSCode自定义代码片段14——Vue的axios网络请求封装