今天是网络编程的最后一篇文章了,这篇文章会补充一下常用的知识点,我尽量快点写0.0。希望有人愿意跟我一起学习呀。
🧑🏻作者简介:一个学嵌入式的年轻人
✨联系方式:2201891280(QQ)
📔源码地址:https://gitee.com/xingleigao/study_qianrushi
⏳全文大约阅读时间: 60min
文章目录
网络信息检索
域名解析
网络属性设置
网络超时处理
广播和组播
广播
广播地址
广播发送
广播接收
广播发送伪码
组播
组播地址
组播发送
组播接收
加入组播组的方式
UNIX域套接字
写在最后
网络信息检索
域名解析
gethostbyname() 根据主机名取得主机信息 只适用于IPV4
#include <netdb.h> extern int h_errno; struct hostent *gethostbyname(const char *name); #include <sys/socket.h> /* 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 <sys/types.h> /* See NOTES */ #include <sys/socket.h> 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选项:
这部分其实就是内容很多,但是用起来还是很快的:之前的
允许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.接收方地址指定为组播地址
2.指定端口信息
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 // <sys/un.h> { 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() // 若没有绑定地址,无法接收数据