【从零开始的嵌入式生活】网络编程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位整数)列表
一个简单的demoif ((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指定控制套接字的层次.可以取三种值:
- SOL_SOCKET:通用套接字选项.
(应用层)
- IPPROTO_IP:IP选项.
(传输层)
- 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在所有网段中都代表广播地址全网广播,一般都会被禁用
广播发送
- 创建用户数据报套接字
- 缺省创建的套接字不允许广播数据包,需要设置属性
setsockopt可以设置套接字属性- 接收方地址指定为广播地址
- 指定端口信息
- 发送数据包
广播接收
- 创建用户数据报套接字
- 绑定本机IP地址和端口
绑定的端口必须和发送方指定的端口相同- 等待接收数据
可以发现这部分基本上与普通的接收方式相同。
广播发送伪码
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
- 创建用户数据报套接字
- 接收方地址指定为组播地址
- 指定端口信息
- 发送数据包
组播接收
相当于之前的server
- 创建用户数据报套接字
- 加入多播组
- 绑定本机IP地址和端口
- 绑定的端口必须和发送方指定的端口相同
- 等待接收数据
加入组播组的方式
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