程序设计之(7)高级网络编程

本文深入讲解了网络编程的高级部分,包括如何进行网络超时检测,使用套接字选项和定时器实现超时控制。此外,介绍了广播通信的原理及发送、接收广播包的方法。同时,探讨了组播的实现,包括组播地址和组播包的发送与接收流程。最后,详细阐述了UNIX域套接字的使用,包括本地地址、服务器端和客户端的创建与通信流程。
摘要由CSDN通过智能技术生成

最后一个部分主要介绍网络编程高级部分,通过这些方法,网络程序可以实现更多的功能,更加完善。

一.网络超时检测

在网络通信过程中,经常会出现不可预知的各种情况。例如网络线路突发故障.通信一方异常结束等。一旦出现上述情况。很可能长时间都不会收到数据,而且无法判断是没有数据还是数据无法到达。如果使用的是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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

飞赴

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

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

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

打赏作者

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

抵扣说明:

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

余额充值