linux网络编程

本文介绍了网络的基础知识,包括OSI模型和TCP/IP协议栈,详细讲解了IP地址的分类以及网络协议。接着讨论了socket的概念和类型,对比了TCP和UDP的区别,包括它们的连接方式和握手挥手过程。此外,文章还阐述了基于TCP和UDP的客户端和服务端模型,以及IO模型的不同类型,如阻塞IO、非阻塞IO和IO多路复用。最后,提到了服务器模型(循环和并发服务器)以及数据库操作,特别是SQL语句的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、网络基础

1.1网络体系结构

OSI模型   7层 应用层 表示层 会话层 传输层  网络层 数据链路层 物理层

TCP/IP 4层  应用层 传输层 网络层 物理层--》在使用

 

 1.2IP地址

iP地址--》网络地址
            定义:在网络中唯一标识一台主机符号就是IP地址
                    (唯一标识一台主机的符号是MAC地址)
            ipv4维护IP地址
                 4:1 IP协议的版本号是V4
                  2  IPV4协议维护的IP地址宽度是4byte=32bit
                ipv4协议提供了2^32个IP地址,将这些地址分成了5类:
                A 1.0.0.1~126.255.255.254
                B 127.0.0.1~191.255.255.254
                C 192.0.0.1~223.255.255.254  -->用户IP地址 24网络号+8主机号
                D 224.0.0.1~239.255.255.254--》组播地址
                E  240.0.0.1~255.255.255.254-->保留地址
                unsigned int addr=inet_addr("127.0.0.1");


     1.3 网络协议
            定义:在网络通信中对某种通信规则的约定
            分类:1 网络通用协议(TCP/IP协议族)
                    2  行业内部专有协议
                    3 自定义协议
     1.4 端口号
           作用:用作标识一个应用进程
             是一个unsigned int 的整数,表示范围1-65535
           1-1023已被著名的协议或程序占用,用户可用:1024~65535                
     1.5 字节序
          主机字节序:是不同的CPU主机存贮多字节整数的方式,有大端主机字节序,
                          有小端主机字节序
            大端主机字节序:将数据的高字节存放在内存的低地址上
            小端主机字节序:将数据的高字节存放在内存的高地址上
        注意:由于网络通信中两台主机的字节序有可能不同,会造成发送的数据和接收的
                数据反序,所以在传送网络数据前要做字节适配处理,将多字节整数从主机
                字节序转换成网络字节序
            网络字节序:是大端字节序,用于兼容通信双方的字节序         
            htonl:将4字节的数据的主机字节序转换成网络字节序
            htons:将2字节的数据的主机字节序转换成网络字节序    

1.6 socket

1.socket概念

1.是一种特殊的文件描述符

2.需要一种通用的网络编程接口:Socket

2.socket种类

流式套接字(SOCK_STREAM) --->TCP

提供了一个面向连接、可靠的数据传输服务,数据无差错、无重复的发送且按发送顺序接收。内设置流量控制,避免数据流淹没慢的接收方。数据被看作是字节流,无长度限制。

注意:TCP方式的unix域套接字类似于无名管道,用一个套接字文件可以实现双工
                通信,通常用于两个不相关的进程或前后台进程的通信。

数据报套接字(SOCK_DGRAM) --->UDP

提供无连接服务。数据包以独立数据包的形式被发送,不提供无差错保证,数据可能丢失或重复,顺序发送,可能乱序接收。

注意:UDP方式的unix域套接字类似于有名管道,用一个套接字文件只能单工
                通信,如要实现双工通信,需要两个socket文件,通常用于两个不相关的
                进程或前后台进程的通信。    

原始套接字(SOCK_RAW)

二、网络客户端&服务端模型

2.1TCPUDP协议

共同点:同为传输层协议

不同点:

TCP:通过TCP的三次握手,建立通信双方的全双工的通道。TCP对网络丢包、乱序等都会有相应的回复处理。

UDP:不需要建立连接。尽力传输,不保证可靠性而已。

TCP(即传输控制协议):是一种面向连接的传输层协议,它能提供高可靠性通信(即数据无误、数据无丢失、数据无失序、数据无重复到达的通信)

如何抉择使用TCP还是UDP
            1.可靠性
            2.实时性
                 可靠性 > 实时性: TCP
                 可靠性 < 实时性: UDP

他们的区别是:
        tcp:是面向连接的可靠的传输协议
        udp:是面向无连接的不可靠的传输协议
 

TCP:

三次握手:

第一次握手:客户端给服务器发送建链请求(客户端能发了)

第二次握手:服务器给客户端回复建链请求的响应,同时服务器向客户端发送建链请求(服务器能收了,服务器能发了)

第三次握手:客户端给服务器回复建链请求的相应(客户端能收了)

四次挥手:

第一次挥手:客户端向服务器发送断链请求(客户端不发了)

第二次挥手:服务器向客户端回复断链请求的响应(服务器不收了)

第三次挥手:服务器向客户端发送断链请求(服务器不发了)

第四次挥手:客户端向服务器回复断链请求的响应(客户端不收了)

2.2 基于tcp协议的网络客户端和服务端模型

服务端:socket-->bind-->listen-->accept-->IO函数(read/write recv/send)
客户端:socket-->connect-->IO函数

int socket(int domain, int type, int protocol);
        作用:创建一个用于通信的网络套接字
        参数1:协议域 AF_INET(ipv4协议)
        参数2:套接字类型 SOCK_STREAM  
        参数3:需要的其他协议 0--》自动匹配其他必要协议
        返回值:失败 -1  
                      成功 标识socket通道的文件描述符 >3
    
   int bind(int sockfd, const struct sockaddr *addr,
                socklen_t addrlen);
        作用:给socket通道绑定网络接收终端
        参数1:文件描述符  socket的返回值
        参数2:表示本机地址信息的结构体指针
        参数3:地址结构体的长度
        返回值:0 成功  -1 失败

  int listen(int sockfd, int backlog);
        作用:监听客户端的连接请求
        参数1:文件描述符 socket的返回值
        参数2:服务端一次最大监听的客户端个数
        返回值:0 成功 -1 失败

   int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
        作用:接收客户端的连接请求
        参数1:文件描述符 socket的返回值
        参数2:地址信息结构,用来存放接收到的客户端的地址信息
        参数3:存放第二个参数大小的变量的地址
        返回值:成功 返回新的文件描述符--》标识一个新的socket通道--》用于收发数据
                    失败 -1
   ssize_t recv(int sockfd, void *buf, size_t len, int flags);
        作用:接收socket通道中的数据
        参数1:新的文件描述符 是accept的返回值
        参数2:本地接收buf
        参数3:期望接收的字节数
        参数4:是否阻塞的接收的标志 0表示阻塞接收
        返回值:>0 成功接收到的子节数
                    -1 失败
   ssize_t send(int sockfd, const void *buf, size_t len, int flags);
        作用:往socket通道中发送数据
        参数1:新的文件描述符 是accept的返回值
        参数2:本地发送buf
        参数3:期望发送的字节数
        参数4:是否阻塞的发送的标志 0表示阻塞发送
        返回值:成功 返回发送的字节数
                    失败 -1
   int connect(int sockfd, const struct sockaddr *addr,
                   socklen_t addrlen);
        作用:连接服务端
        参数1:文件描述符 socket的返回值
        参数2:目标服务器的主机地址信息结构
        参数3:第二个参数的长度
        返回值:0 成功  -1 失败
  

TCP通信出现粘包问题原因

  1. 信道拥挤产生的
  2. 读写速率不匹配

解决方案:

 1.使用延迟(用时间来匹配速率)

2.写多少,读多少(使用同等大小缓存,使得读写保持一致)

3.对粘合的包进行解析(在有严格的协议格式下,才能使用,数据本身和协议不能产生二义性) 

附:

wireshark抓包工具

1.安装wireshark

2.运行wireshark

2.3 基于udp协议的网络客户端和服务端模型

服务端:socket-->bind-->IO函数(sendto/recvfrom)
 客户端:socket-->IO函数(sendto/recvfrom)

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,
                        struct sockaddr *src_addr, socklen_t *addrlen);
        参数1:文件描述符
        参数2:接收buf
        参数3:期望接收的字节数
        参数4:阻塞或非阻塞标志 0--》阻塞
        参数5:用来保存对方地址信息
        参数6:参数5的长度的地址
        返回值:成功  期望接收到的字节数 失败 -1
        ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
                      const struct sockaddr *dest_addr, socklen_t addrlen);
        参数1:文件描述符
        参数2:发送buf
        参数3:期望发送的字节数
        参数4:阻塞或非阻塞标志 0--》阻塞
        参数5:用来存放对方地址信息
        参数6:参数5的长度
        返回值:成功 期望发送的字节数  失败 -1

三、IO模型

linux 系统提供了4种IO模型:


     1.阻塞IO


        若要读的数据没有准备好,或要写入的目标没有空间,将会发生读写阻塞
        阻塞函数:优点:一直等待结果,直到等到结果
                          缺点:如果等待的接收数据的通道坏掉或文件损坏,那么当前进程
                                    会一直被挂起  

     
      2.非阻塞IO


        若要读的数据没有准备好,IO函数返回一个约定的错误值,不阻塞当前进程                 
         ,可以通过循环去多次尝试读取数据。
        非阻塞函数:优点:没有读到数据不阻塞当前进程,而是直接返回,带回一个错误值
                              缺点:多次调用系统调用函数会有极大的系统资源开销。

                 
      3.IO多路复用


           思想:分三步
                1 构建一个文件描述符集合表,表的大小是1024bit位,每一个bit表示一个
                    文件描述符所对应的IO通道上是否有数据发生(或者IO通道是否活跃?)?
                    如果有数据发生,该bit位为1,没数据发生该bit为0。
                2 使用select函数监控制定范围的文件描述符所对应的IO通道是否有数据发生,
                    如果有数据发生,该函数返回通道的个数,并且将文件描述符集合表的
                    相应bit置位1,同时将其他bit置位0,如果没有数据发生,那么select将
                    阻塞当前进程。
                3 对文件描述符集合表中的置位结果做出相应的判断,使用FD_ISSET判断
                    该文件描述符在描述符集合表中是否为1,如果为1,响应这路IO数据,
                    否则不处理。

            分析:1 文件描述符集合表--》文件描述符对应的IO通道是否有数据发生
                        fd_set  stFdr;1024bit
                    2  select  监控 置位--》stFdr
                    3  对stFdr中的置位情况做判断  0-->fgets 3--》accept   4-->recv/send

         int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);
            作用:监控文件描述符对应的IO通道是否有数据发生
            参数1:要监控的文件描述符的最大个数
            参数2:读文件描述符集合表
            参数3:写文件描述符集合表
            参数4:异常文件描述符集合表
            参数5:超时时间 ,若为NULL,表示将select变成阻塞函数
            返回值:成功 有数据发生的通道的个数 
                      失败 -1

为了设置文件描述符我们要使用几个宏:

    • FD_SET       将fd加入到fdset
    • FD_CLR      将fd从fdset里面清除
    • FD_ZERO   从fdset中清除所有的文件描述符
    • FD_ISSET   判断fd是否在fdset集合中 
  • 宏的形式:
    • void FD_ZERO(fd_set *fdset)    将文件描述符对应位的值置为0
    • void FD_SET(int fd,fd_set *fdset) 将文件描述符对应位的值置为1
    • void FD_CLR(int fd,fd_set *fdset) 将文件描述符对应位的值置为0
    • int FD_ISSET(int fd,fd_set *fdset) 判断文件描述符对应位是1还是0

    fd_set stFdr;//创建描述符集和表
    FD_ZERO(&stFdr);//清空指定的描述符集和表  (原描述符集和表)
    FD_SET(iSever,&stFdr)                     FD_ISSET(i,&stFdrTmp) 

     4异步IO

四、服务器模型

1.循环服务器

定义:该服务可以服务于多个客户端,但在同一时刻只能服务于一个客户端

2.并发服务器

定义:该服务器可以同时服务于多个客户端

  实现并发服务的方法:多进程  多线程  select poll epoll

2.1多进程并发服务器

为了响应客户机的请求,服务器要创建子进程来处理。 如果有多个客户端的话,服务器端需要创建多个子进程。过多的子进程会影响服务器端的运行效率

2.2多线程并发服务器

主进程负责专门连接多个客户端,若有客户端连接进来,主进程就创建一个子线程来负责该客户端的业务数据的收发。

多线程服务器是对多进程服务器的改进

2.3.IO多路复用服务器

select实现并发
                1 文件描述符集合表
                2 select 监控 置位
                3 FD_ISSET(fd,&stFdr)-->判断置位的结果并响应
                    
                stFdr-->原文件描述符集合表--->S
                stFdrTmp-->临时文件描述符集合表--->T

2.4 epoll函数 

select,poll,epoll都是I/O多路复用机制,即能监视多个fd,一旦某fd就绪(读或写就绪),能够通知程序进行相应读写操作。 但select,poll,epoll本质都是同步I/O,因为他们都需在读写事件就绪后,自己负责进行读写,即该读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步I/O实现会负责把数据从内核拷贝到用户空间。

五、网络服务器超时检测

1 select设置超时

int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);
            参数1:监控文件描述符的最大个数
            参数2:读文件描述符集合表
            参数3:写文件描述符集合表
            参数4:异常文件描述符集合表
            参数5:timeout=NULL,一直阻塞,直到有文件描述符就绪或出错
                    timeout=0,仅仅检测文件描述符的状态,然后立即返回,不置位。
                    timeout=时间值,在指定的时间内如果没有描述符对应的IO通道
                                没有数据发生,则超时返回0。
            返回值:成功 返回有IO数据发生的通道的个数
                        若=0,表示超时
                        若<0,表示出错
            struct timeval {--》用来表示时间
                    long tv_sec;--》秒
                   long tv_usec;--》微秒
                };
            struct timeval  t={5,0};
            select(..,&t);  

        2 使用信号和alarm函数


            udp_server.c-->recvform()
        alarm-->闹钟函数--》用作定时
        alarm(5);起一个定时器,定时时间5S,5秒时间到,它会给内核发送一个SIGALRM
                    信号,内核会执行该信号的默认处理动作(中止当前进程)
         signal(SIGALRM,Func);-->set
         int sigaction(int signum, const struct sigaction *act,
                     struct sigaction *oldact); -->set/get
          获取:  sigaction(SIGALRM,NULL,&oldact)-->get
          设置:sigaction(SIGALRM,act,NULL);-->set
      代码:
            struct sigaction  act;
              sigaction(SIGALRM, NULL, &act);--》获取信号对应的旧属性
              act.sa_handler = handler;--》给信号设置新的处理函数
              act.sa_flags &= ~SA_RESTART;--》给信号设置标志-》不重启
              sigaction(SIGALRM, &act, NULL);
              alarm(5);

        注:act.sa_flags &= ~SA_RESTART ;不重启recvfrom函数,结束阻塞,超时退出
            act.sa_flags = SA_RESTART;自动重启,定时时间到时重启recvfrom函数,
                继续阻塞,不会超时退出

                  sa_flags表示信号的标志
            SA_RESTART用在为某个信号设置信号处理函数时,给该信号设置一个标志。            
            一旦在进程中给信号设置了SA_RESTART标志,那么当执行某个阻塞系统调用函数时,
            进程收到该信号时,进程不会返回,而是重新执行该系统调用函数。
            
            struct sigaction {-->用来表示信号对应的属性
            __sighandler_t sa_handler;--》信号对应的处理函数
            unsigned long sa_flags; --》信号标志
            __sigrestore_t sa_restorer;
            sigset_t sa_mask;        --》信号掩码
            };


        3 socket属性超时设置


            udp_server.c-->recvfrom
            通过设置socket通道的SOCKET层面上的接收超时属性去设置
            服务器的超时检测功能。
            struct timeval  tv;
                 tv.tv_sec = 5;   //  设置5秒时间
                 tv.tv_usec = 0;
        setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO,  &tv,  
                       sizeof(tv));     //  设置接收超时

六、广播、组播

在网络通信中,数据包的发送方式有几种?分别是什么?
        三种,分别是单播 广播 组播


        1广播--》一对多


            发送方发送的数据,整个局域网的主机都能接收到
            广播发送:socket-->setsockopt(允许广播发送)    --》sendto(广播地址)    udp_client.c
            广播接收:socket-->bind-->recvfrom  udp_server.c


        2组播-->一对一组


            发送方发送的数据,组播组的成员主机能接收到
            组播发送:socket-->对方的地址(组播地址)--》sendto   udp_client.c
            组播接收:socket-->加入组播组--》bind-->recvfrom     udp_server.c

            加入组播组:
            struct  ip_mreq  mreq;
            bzero(&mreq, sizeof(mreq));
            mreq.imr_multiaddr.s_addr = inet_addr(“224.10.10.1”);
            mreq.imr_interface.s_addr = htonl(INADDR_ANY);

            setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,  
                  sizeof(mreq));

七、数据库

  定义:是指以同一组织方式将相关数据组织在一起,并存放在PC或设备存储器上的供
            多个用户共享使用的文件,与应用程序彼此独立,是一组相关数据的集合。
            用户可以使用通用指令(sql语句)提取查看数据。
  常见的嵌入式数据库:mysql  sqlite ...
  sqlite:是一种小型的嵌入式关系型数据库
  sqlite特点:源码开发 代码精简 免安装 支持SQL语句。。
  对应的源码sqlite3.c/sqlite3.h


  7.1 操作数据库的通用指令--》SQL语句


       数据库文件--》1 创建一张表   指令  表项  约束
                        2 给表插入内容   指令   表项  值
      1 创建一张表
        create table 表名 (字段列表 [各种约束])
        create table student (id int primary key not null,
                                    name text not null,
                                    age int not null)
       primary key--->主键约束
        A  主键值必须唯一,用于标识一条记录,如学生的学号
        B  主键同时也是一个索引,通过主键查找数据的速度最快
        C  主键如果是整形类型,该类的值可以自动增长
    2 插入表
        insert into 表名 (字段列表) values (值列表)
          insert into student (id,name,age) values (1,'zhangsan',21);
    3 查询表项
        select *from 表名--》查询整张表
          select *from 表名 where id=n--->条件查询 查询id=n的这条数据记录
     4 删除表里的内容
           delete from 表名 条件
           delete from student where id=1
    5 修改表表项
          update 表名 set 表项=? 条件
        update student set age=19 where id=1
     7 删除数据库中的一张表
        drop table 表名
        drop table student
    注:一个数据库中可创建多张表,多张表的表名不能重复。
        


  7.2 实现数据库操作的代码


        完整例子
        struct sqlite3-->描述一个数据库文件的结构 
      操作sqlite3数据库的接口函数:    
      sqlite3_open---》创建或打开数据库
      sqlite3_exec-->执行SQL语句
       sqlite3_close---》关闭数据库
      sqlite3_get_table--》查询数据库表    select *from  student

网络编程项目linux百度网盘实现局域网文件共享:

https://download.youkuaiyun.com/download/qq_59557128/87399958?spm=1001.2014.3001.5503

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

じ刂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值