最后一个部分主要介绍网络编程高级部分,通过这些方法,网络程序可以实现更多的功能,更加完善。
一.网络超时检测
在网络通信过程中,经常会出现不可预知的各种情况。例如网络线路突发故障.通信一方异常结束等。一旦出现上述情况。很可能长时间都不会收到数据,而且无法判断是没有数据还是数据无法到达。如果使用的是TCP协议,可以检测出来;但如果使用UDP协议的话,需要在程序中进行相关检测。
1.套接字的收超时检测
网络通信的实现涉及多个协议层,开发人员可以通过设定套接字的选项来实现不同的功能。
如下截图
套接字的选项如图很多,分属不同协议层并有其相应的数据类型。
getsockopt()
#include <sys/types.h>
#include <sys/socket.h>
函数原型
int getsockopt(int sockfd, int level, int optname,void *optval, socklen_t *optlen);
函数参数
sockfd:套接字描述符
level: 选项所属协议层
optval: 保存选项值的缓冲区
optlen: 选项值的长度
返回值
成功 0
失败 -1 并设置errno
setsockopt()
#include <sys/types.h>
#include <sys/socket.h>
函数原型
int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
参数同getsockopt一样
2.定时器的超时检测
sigaction()
#include <signal.h>
函数原型
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
参数
signo:信号类型
act:新设定的信号行为
oldact:原先的信号行为
返回值
成功 0
失败 -1,并设置errno
二.广播
之前我们讲的所有通信中,采用的都是单播(唯一的发送方和接收方)的方式,很多时候,需要把数据通信发送给局域网中的所有主机。例如,通过广播ARP包获取目标主机的MAC地址。
1.广播地址
IP地址用来标识网络中的一台主机。IPV4协议用一个32位的无符号数表示网络地址,包括网络号和主机号。子网掩码表示IP地址中网络号占几字节。
2.广播包的发送和接收
广播包的发送和接收通过UDP套接字实现。
(1)发送
1.创建套接字
2.指定目标地址和端口
3.设置套接字选项允许发送广播包
4.发送数据包
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
//1 创建套接字
int sockfd=socket(AF_INET,SOCK_DGRAM,0);
if(sockfd<0)
{
perror("socket");
return -1;
}
//2 设置运行套接字发送广播
int on=1;
if(setsockopt(sockfd,SOL_SOCKET,SO_BROADCAST,&on,sizeof(on))<0)
{
perror("sersockopt");
return -1;
}
//指定接收方地址和端口号
struct sockaddr_in addr;
addr.sin_family =AF_INET;
addr.sin_port =htons(8888);
addr.sin_addr.s_addr=inet_addr("192.168.2.255");
//3 发送数据
while(1)
{
char buf[128]={0};
fgets(buf,32,stdin);
int ret=sendto(sockfd,buf,strlen(buf)-1,0,(struct sockaddr*)&addr,sizeof(addr));
if(ret<0)
{
printf("error\n");
continue;
}
printf("send: %d\n",ret);
}
return 0;
}
(2)接收
1.创建UDP套接字
2.绑定地址和端口
3.接收数据包
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
//1 创建套接字
int sockfd=socket(AF_INET,SOCK_DGRAM,0);
if(sockfd<0)
{
perror("socket");
return -1;
}
//2 绑定广播地址和端口号
struct sockaddr_in addr;
addr.sin_family =AF_INET;
addr.sin_port =htons(3333);
addr.sin_addr.s_addr=inet_addr("0.0.0.0");
//每个网段的最大 ip就是广播地址
if(bind(sockfd,(struct sockaddr*)&addr,sizeof(addr))<0)
{
perror("bind");
return -1;
}
//3 接受数据
while(1)
{
struct sockaddr_in caddr;
socklen_t len=sizeof(caddr);
char buf[128]={0};
int ret=recvfrom(sockfd,buf,sizeof(buf),0,(struct sockaddr *)&caddr,&len);
if(ret<0)
{
printf("error\n");
continue;
}
printf("recv from %s :%s\n",inet_ntoa(caddr.sin_addr),buf);
}
return 0;
}
三.组播
1.组播地址
2.组播包的发送和接收
(1)组播发送流程
(2)组播接收流程
四.UNIX域套接字
1.本地地址
2.UNIX域流式套接字
UNIX域流式套接字用法和TCP套接字基本一致,区别在于使用的协议和地址不同。
UNIX域流式套接字服务器端流程
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <string.h>
int main(int argc, char *argv[])
{
//1、创建unix域的套接字
int sockfd = socket(PF_UNIX, SOCK_STREAM, 0);
if (sockfd < 0){
perror("socket");
return -1;
}
//2、绑定本地地址
struct sockaddr_un addr;
addr.sun_family = PF_UNIX;
strcpy(addr.sun_path, "/tmp/mysocket"); //套接字文件,存在则打开,不存在则创建
if(bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0){
perror("bind");
return -1;
}
//3、监听
if(listen(sockfd, 5) < 0){
perror("listen");
return -1;
}
printf("server init success\n");
//4、阻塞等待,接受连接
while(1){
int connfd = accept(sockfd, NULL, NULL);
if(connfd < 0){
perror("accept");
continue;
}
while(1){
char buf[128] = {0};
int ret = read(connfd, buf, sizeof(buf));
if(ret < 0){
perror("read");
close(connfd);
close(sockfd);
//system("sudo rm /tmp/mysocket | echo 1");
return -1;
} else if (ret == 0){
printf("client was leaved\n");
break;
}
printf("recv: %s\n", buf);
}
close(connfd);
}
return 0;
}
UNIX域流式套接字客户端流程
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
//1、创建UNIX 域的流式套接字
int sockfd = socket(PF_UNIX, SOCK_STREAM, 0);
if(sockfd < 0){
perror("socket");
return -1;
}
//2、绑定本地地址(可选)
//3、连接
struct sockaddr_un addr;
addr.sun_family = PF_UNIX;
strcpy(addr.sun_path, "/tmp/mysocket");
if(connect(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0){
perror("connect");
return -1;
}
while(1){
char buf[128] = {0};
fgets(buf, 128, stdin);
write(sockfd, buf, strlen(buf)-1);
}
close(sockfd);
return 0;
}