> 文档中心 > 【从零开始的嵌入式生活】网络编程7——编程扩展

【从零开始的嵌入式生活】网络编程7——编程扩展

【从零开始的嵌入式生活】网络编程7——编程扩展
今天是网络编程的最后一篇文章了,这篇文章会补充一下常用的知识点,我尽量快点写0.0。希望有人愿意跟我一起学习呀。

🧑🏻作者简介:一个学嵌入式的年轻人
✨联系方式:2201891280(QQ)
📔源码地址:https://gitee.com/xingleigao/study_qianrushi
全文大约阅读时间: 60min


文章目录

  • 网络信息检索
    • 域名解析
    • 网络属性设置
    • 网络超时处理
  • 广播和组播
    • 广播
      • 广播地址
      • 广播发送
      • 广播接收
      • 广播发送伪码
    • 组播
      • 组播地址
      • 组播发送
      • 组播接收
      • 加入组播组的方式
  • UNIX域套接字
  • 写在最后

网络信息检索

域名解析

gethostbyname() 根据主机名取得主机信息 只适用于IPV4

  #include       extern int h_errno;      struct hostent *gethostbyname(const char *name);      #include /* for AF_INET */      struct hostent *gethostbyaddr(const void *addr, socklen_t len, int type);      void sethostent(int stayopen);      void endhostent(void);//释放空间      void herror(const char *s);      const char *hstrerror(int err);

可以看到返回值是一个结构体,并且如果是空指针的是出错,我们可以一起看一下这个结构体。

The hostent structure is defined in <netdb.h> as follows:   struct hostent {char  *h_name;     /* official name of host */char **h_aliases;  /* alias list */int    h_addrtype; /* host address type */int    h_length;   /* length of address */char **h_addr_list;/* list of addresses */   }   #define h_addr h_addr_list[0] /* for backward compatibility */

可以看到:h_addr_list是一个列表,多个网络地址(网络字节序的32位整数)列表
一个简单的demo

if ((hs = gethostbyname (argv[1])) == NULL) {//获取地址    herror ("gethostbyname error");    exit (1);   }   if ((fd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {    perror ("socket");    exit (1);   }     bzero (&sin, sizeof (sin));     sin.sin_family = AF_INET;   sin.sin_port = htons (port);   sin.sin_addr.s_addr = *(uint32_t *) hs->h_addr;//拿到地址   endhostent ();//释放空间   hs = NULL;//释放空间

网络属性设置

#include    /* See NOTES */#include getsockopt和setsockopt 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)

level指定控制套接字的层次.可以取三种值:

  1. SOL_SOCKET:通用套接字选项. (应用层)
  2. IPPROTO_IP:IP选项. (传输层)
  3. IPPROTO_TCP:TCP选项. (网络层)

optval选项:

选项名称 说明 数据类型
SO_BROADCAST 允许发送广播数据 int
SO_DEBUG 允许调试 int
SO_DONTROUTE 不查找路由 int
SO_ERROR 获得套接字错误 int
SO_KEEPALIVE 保持连接 int
SO_LINGER 延迟关闭连接 struct linger
SO_OOBINLINE 带外数据放入正常数据流 int
SO_RCVBUF 接收缓冲区大小 int
SO_SNDBUF 发送缓冲区大小 int
SO_RCVLOWAT 接收缓冲区下限 int
SO_SNDLOWAT 发送缓冲区下限 int
SO_RCVTIMEO 接收超时 struct timeval
SO_SNDTIMEO 发送超时 struct timeval
SO_REUSERADDR 允许重用本地地址和端口 int
SO_TYPE 获得套接字类型 int
SO_BSDCOMPAT 与BSD系统兼容 int
IP_HDRINCL 在数据包中包含IP首部 int
IP_OPTINOS IP首部选项 int
IP_TOS 服务类型
IP_TTL 生存时间 int
TCP_MAXSEG TCP最大数据段的大小 int
TCP_NODELAY 不使用Nagle算法 int

这部分其实就是内容很多,但是用起来还是很快的:之前的
允许IP快速重用我们就是:

int b_reuse = 1;setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &b_reuse, sizeof (int));

超时时间设置:

    struct timeval  tv;     tv.tv_sec = 5;   //  设置5秒时间     tv.tv_usec = 0;     setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO,  &tv,    sizeof(tv));   //  设置接收超时      recv() / recvfrom()    //   从socket读取数据

网络超时处理

方法一:设置socket的属性
设置socket的属性 SO_RCVTIMEO就是上面说到的方式
方法二:用select检测socket是否’ready’

  struct fd_set rdfs;   struct timeval  tv = {5 , 0};   // 设置5秒时间   FD_ZERO(&rdfs);   FD_SET(sockfd, &rdfs);   if (select(sockfd+1, &rdfs, NULL, NULL, &tv) > 0)   // socket就绪   {  recv() /  recvfrom()    //  从socket读取数据   }

方法三:设置定时器(timer)

   void  handler(int signo)     {   return;  }     struct sigaction  act;     sigaction(SIGALRM, NULL, &act);     act.sa_handler = handler;     act.sa_flags &= ~SA_RESTART;     sigaction(SIGALRM, &act, NULL);     alarm(5);     if (recv(,,,) < 0) ……

当然网络编程还有心跳检测等多种方式。

广播和组播

广播和组播都是一种一对多的方式,所以一定使用的UDP


广播

如果同时发给局域网中的所有主机,称为广播
只有用户数据报(使用UDP协议)套接字才能广播

广播地址

以192.168.1.0 (255.255.255.0) 网段为例,最大的主机地址192.168.1.255代表该网段的广播地址
发到该地址的数据包被所有的主机接收
255.255.255.255在所有网段中都代表广播地址全网广播,一般都会被禁用


广播发送

  1. 创建用户数据报套接字
  2. 缺省创建的套接字不允许广播数据包,需要设置属性
    setsockopt可以设置套接字属性
  3. 接收方地址指定为广播地址
  4. 指定端口信息
  5. 发送数据包

广播接收

  1. 创建用户数据报套接字
  2. 绑定本机IP地址和端口
    绑定的端口必须和发送方指定的端口相同
  3. 等待接收数据

可以发现这部分基本上与普通的接收方式相同。

广播发送伪码

sockfd = socket(,,);……int on = 1;setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));……sendto(;;;;;);

组播

  • 单播方式只能发给一个接收方
  • 广播方式发给所有的主机。过多的广播会大量占用网络带宽,造成广播风暴,影响正常的通信。
  • 组播(又称为多播)是一种折中的方式。只有加入某个多播组的主机才能收到数据。

组播地址

D类地址(组播地址)
不分网络地址和主机地址,第1字节的前4位固定为1110
224.0.0.1 – 239.255.255.255

组播发送

相当于之前的client

  1. 创建用户数据报套接字
  2. 接收方地址指定为组播地址
  3. 指定端口信息
  4. 发送数据包

组播接收

相当于之前的server

  1. 创建用户数据报套接字
  2. 加入多播组
  3. 绑定本机IP地址和端口
  4. 绑定的端口必须和发送方指定的端口相同
  5. 等待接收数据

加入组播组的方式

struct ip_mreq{    struct  in_addr  imr_multiaddr;    struct  in_addr  imr_interface;};struct  ip_mreq  mreq;bzero(&mreq, sizeof(mreq));mreq.imr_multiaddr.s_addr = inet_addr(235.10.10.3);mreq.imr_interface.s_addr = htonl(INADDR_ANY);setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq,     sizeof(mreq));

UNIX域套接字

socket同样可以用于本地通信
创建套接字时使用本地协议PF_UNIX(或PF_LOCAL)。

socket(AF_LOCAL, SOCK_STREAM, 0)socket(AF_LOCAL, SOCK_DGRAM, 0)

分为流式套接字和用户数据报套接字

1.进程间消息通信:管道、消息队列、共享内存、unix域套接字
易用性:消息队列>unix域套接字>管道>共享内存
效率:共享内存>unix域套接字>管道>消息队列
2.异步通信: 信号
3.信号量和互斥:信号量

struct sockaddr_un //  {sa_family_t  sun_family;char  sun_path[108];  // 套接字文件的路径};

注意点:1.文件必须不存在 2.一般是绝对路径 3.文件在内存中
TCP域套接字服务端:

socket(AF_UNIX, SOCK_STREAM, 0)bind(,本地地址, )listen()accept(,,)recv() / send()

客户端:

socket(PF_UNIX, SOCK_STREAM, 0)bind(,本地地址, )   //   可选connect(,,)recv() / send()

UDP域套接字服务端:

socket(PF_UNIX, SOCK_DGRAM, 0)bind(,本地地址, )recvfrom() sendto()

客户端:

socket(PF_UNIX, SOCK_DGRAM, 0)bind(,本地地址, )     //  可选sendto() recvfrom()    //  若没有绑定地址,无法接收数据

写在最后

最近有点懒,一定尽快更,长按催更见,前面的应用较多大家跟我一起改变世界。啊哈哈哈,求求大家给个三连再走吧,求求你们了0.0


【从零开始的嵌入式生活】网络编程7——编程扩展