Linux网络异常处理(6.12)

本文详细解析了网络异常处理策略,包括客户端和服务端的异常情况处理方法,以及实现心跳包的方法。同时,文章深入介绍了广播和组播的原理、流程和使用方法,包括如何接收和发送广播消息及组播消息。通过实例代码展示了如何在不同场景下应用这些网络技术,旨在提升开发者在网络编程领域的实践能力。
摘要由CSDN通过智能技术生成
[1] 网络异常处理
    1. 客户端出了问题(死机、重启了、网络断了...)
    2. 服务端出了问题(死机、重启了、网络断了、内存耗尽、...)
    3. 网络不正常
    
    解决办法: 心跳包
    
    心跳包的实现方法:
    1. 利用TCP协议的KeepAlive(TCP协议实现的心跳包)
       见《tcp_keepalive》的服务端
       
    2. 设置接收超时检测心跳,对端需要定时发送
       heart_beat程序只在接收端实现了如何(利用阻塞超时)检测超时
       
    3. 如何实现应用程序的定时发送和超时检测?
       alarm
    
    总结: 利用实现心跳包
    
[2] 广播

    1. 接收端流程

//       (1) 创建套接字
           sockfd = socket(AF_INET, SOCK_DGRAM, 0);
           
//       (2) 绑定IP和端口到socket
           struct sockaddr_in addr;
           
           bzero(&addr, sizeof(addr));
           addr.sin_family = AF_INET;
           addr.sin_port = htons(port);
           addr.sin_addr.s_addr = inet_addr(ip);
           // addr.sin_addr.s_addr = INADDR_ANY;
           
           ret = bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));
           
//       (3) 接收
           struct sockaddr_in peer_addr;
           socklen_t addrlen = sizeof(peer_addr);
           
           ret = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&peer_addr, &addrlen);
           
//       (3) 关闭
           close(sockfd);  
    2. 发送端流程
//       (1) 创建套接字
           sockfd = socket(AF_INET, SOCK_DGRAM, 0);
           
//       (2) 设置套接字,使能广播
           int on = 1;
           
           ret = setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
           if (-1 == set){
             
           }
           
           SOL_SOCKET:          //设置的选项在socket层
           SO_BROADCAST:        //设置是否允许广播 0 - 不能 1-可以
           
//       (3) 通过广播地址发送广播
           struct sockaddr_in broad_addr;
           
           bzero(&broad_addr, sizeof(broad_addr));
           broad_addr.sin_family = AF_INET;
           broad_addr.sin_port = htons(port);
           broad_addr.sin_addr.s_addr = inet_addr//(一定填广播(192.168.0.255));
           
           ret = sendto(sockfd, buf, len, 0, (struct sockaddr *)&broad_addr, sizeof(broad_addr));
           
//       (4) 关闭
           clos(sockfd);

[3] 组播        
    1. 分组
       每个D类IP地址就是一个组,组播实现原理:
       接收 -- 加入一个组
       发送 -- 向一个组(目标IP地址为组播地址)发送数据包  
       
    2. 组播地址(IP地址和网卡地址)
       IP地址: D类地址 ,高位固定为1110,范围: 224.0.0.0-239.255.255.255
       网卡地址: 前24bit固定为01-00-5e, 最后23bit是D类IP地址的后23bit直接映射下来 -- 仅仅正对于以太网
       
    3. 接收流程

//       (1) 创建套接字
           sockfd = socket(AF_INET, SOCK_DGRAM, 0);
           
//       (2) 加入组
           struct ip_mreq mreq;
           mreq.imr_multiaddr.s_addr = inet_addr(argv[1]);  // "224.10.10.1"
           mreq.imr_interface.s_addr = INADDR_ANY;
           
           ret = setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
           
//       (3) 绑定IP地址和端口
           struct sockaddr_in addr;
           
           bzero(&addr, sizeof(addr));
           addr.sin_family = AF_INET;
           addr.sin_port = htons(port);
           addr.sin_addr.s_addr = inet_addr(ip);
           // addr.sin_addr.s_addr = INADDR_ANY;
           
           ret = bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));
           
//       (4) 接收
           struct sockaddr_in peer_addr;
           socklen_t addrlen = sizeof(peer_addr);
           
           ret = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&peer_addr, &addrlen);
           
//       (3) 关闭
           close(sockfd);
    4. 发送流程
//       (1) 创建套接字
           sockfd = socket(AF_INET, SOCK_DGRAM, 0);
           
//       (2) 发送
           struct sockaddr_in muticast_addr;
           
           bzero(&muticast_addr, sizeof(muticast_addr));
           muticast_addr.sin_family = AF_INET;
           muticast_addr.sin_port = htons(port);
           muticast_addr.sin_addr.s_addr = inet_addr//(一定填多播(224.10.10.1));
           
           ret = sendto(sockfd, buf, len, 0, (struct sockaddr *)&broad_addr, sizeof(broad_addr));
           
//       (3) 关闭
           close(sockfd);

send.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <strings.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

// ./send 192.168.0.255 8888
int main(int argc, const char *argv[])
{
	int ret;
	int sockfd;
	int on = 1;
	char packet[1024];
	struct sockaddr_in broad_addr;
	
	if (argc < 3){
		fprintf(stderr, "Usage: %s <broadcast ip> <port>\n", argv[0]);
		exit(EXIT_FAILURE);
	}
	
	// 1. 创建报文套接字
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (-1 == sockfd){
		perror("Fail to socket.");
		exit(EXIT_FAILURE);
	}
	
	// 2. 设置套接字广播属性
  ret = setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on));
  if (-1 == ret){
  	perror("Fail to setsockopt.");
  	exit(EXIT_FAILURE);
  }
  
  // 3. 通过广播地址发送数据包到指定端口
	bzero(&broad_addr, sizeof(broad_addr));
	broad_addr.sin_family = AF_INET;
	broad_addr.sin_port = htons(atoi(argv[2]));
	broad_addr.sin_addr.s_addr = inet_addr(argv[1]);
	
	while (1){
		putchar('\n');
		putchar('>');
		fgets(packet, sizeof(packet), stdin);
		packet[strlen(packet) - 1] = '\0';
		
		ret = sendto(sockfd, packet, strlen(packet), 0, (struct sockaddr *)&broad_addr, sizeof(broad_addr));
		if (-1 == ret){
			perror("Fail to sendto.");
  		break;
		}
		
		if (strcmp(packet, "quit") == 0){
			break;
		}
	}
	
	// 4. 关闭套接字
	close(sockfd);
	
	return 0;
}
recv.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <strings.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

// ./recv 192.168.2.255 8888
int main(int argc, const char *argv[])
{
	int ret;
	int sockfd;
	char packet[1024];
	struct sockaddr_in broad_addr;
	struct sockaddr_in peer_addr;
	socklen_t addrlen = sizeof(peer_addr);
	
	if (argc < 3){
		fprintf(stderr, "Usage: %s <broadcast ip> <port>\n", argv[0]);
		exit(EXIT_FAILURE);
	}
	
	// 1. 创建报文套接字
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (-1 == sockfd){
		perror("Fail to socket.");
		exit(EXIT_FAILURE);
	}
	
	// 2. 绑定广播地址和端口
	bzero(&broad_addr, sizeof(broad_addr));
	broad_addr.sin_family = AF_INET;
	broad_addr.sin_port = htons(atoi(argv[2]));
	// broad_addr.sin_addr.s_addr = inet_addr(argv[1]);
	broad_addr.sin_addr.s_addr = INADDR_ANY;
	
	ret = bind(sockfd, (struct sockaddr *)&broad_addr, sizeof(broad_addr));
	if (-1 == ret){
		perror("Fail to bind.");
		exit(EXIT_FAILURE);
	}
	
	while (1){
		// 3. 接收广播
		ret = recvfrom(sockfd, packet, sizeof(packet), 0, (struct sockaddr *)&peer_addr, &addrlen);
		if (-1 == ret){
			perror("Fail to recvfrom.");
			break;
		}
		packet[ret] = '\0';
		
		printf("---------------------------------------\n");
		printf("ip       : %s\n", inet_ntoa(peer_addr.sin_addr));
		printf("port     : %d\n", ntohs(peer_addr.sin_port));
		printf("recv(%d)  : %s\n", ret, packet);
		printf("---------------------------------------\n");
		if (strcmp(packet, "quit") == 0){
			break;
		}
	}
	
	// 4. 关闭套接字
	close(sockfd);
	
	return 0;
}

[3] unix域套接字--流式

    1. 头文件
       #include <sys/un.h>
       
    2. 数据结构
       struct sockaddr_un {
        sa_family_t sun_family;
         // __SOCKADDR_COMMON (sun_);
         
        // #define __SOCKADDR_COMMON(sa_prefix) \                                                              
        // sa_family_t sa_prefix##family     
         // 替换原理,带参数的宏,实参替换形参(整个宏中所有的) 
                                                                   
         char sun_path[108]; // 字符串(unix域套接字文件的名)
       };


    3. 服务端流程
       1. 创建套接字
          sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
          
       2. 绑定地址到socket
struct sockaddr_un server_addr;

server_addr.sun_family = AF_UNIX;
strcpy(server_addr.sun_path, "mypath");

ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));

3. 设置为监听模式
   ret = listen(sockfd, 10);
   
4. 接收请求
   clientfd = accept(sockfd, NULL, NULL);
      
5. 接收数据
   ret = recv(clientfd, buf, sizeof(buf), 0);
   
6. 发送数据
   ret = send(clientfd, buf, len, 0);
   
7. 关闭套接字
   close(clientfd);
   close(sockfd);
   
4. 客户端流程
  1. 创建套接字
          sockfd = socket(AF_UNIX, SOCK_STREAM, 0);

server.c

/*
 * 实现目标:
 * UNIX域套接字通讯
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <pthread.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/un.h>

void *handler(void *arg)
{
	int ret = 0;
	int clientfd = (int)(long int)arg;
	char packet[1024];
	
	while (1){
		// 5. 接收数据
		ret = recv(clientfd, packet, sizeof(packet), 0);
		if (-1 == ret){
			perror("Fail to accept.");
			break;
		}
		packet[ret] = '\0';
		
		printf("--------------------------------\n");
		printf("recv(%d): %s\n", ret, packet);
		printf("--------------------------------\n");
		if (strcmp(packet, "quit") == 0){
			break;
		}
		
	}
	
	// 6. 关闭套接字
	close(clientfd);
	
	return (void *)0;
}


// ./server mypath
int main(int argc, const char *argv[])
{
	int ret;
	int sockfd;
	int clientfd;
	pthread_t tid;
	char packet[1024];
	struct sockaddr_un server_addr;
	
	if (argc < 2){
		fprintf(stderr, "Usage: %s <path>\n", argv[0]);
		exit(EXIT_FAILURE);
	}
	
	// 1. 创建套接字
	sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
	if (-1 == sockfd){
		perror("Fail to socket.");
		exit(EXIT_FAILURE);
	}
	
	// 2. 绑定地址
	server_addr.sun_family = AF_UNIX;
	strcpy(server_addr.sun_path, argv[1]);
	
	ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
	if (-1 == ret){
		perror("Fail to bind.");
		exit(EXIT_FAILURE);
	}
	
	// 3. 设置为监听模式
	ret = listen(sockfd, 10);
	if (-1 == ret){
		perror("Fail to listen.");
		exit(EXIT_FAILURE);
	}
	
	while (1){
		// 4. 接收连接请求
		clientfd = accept(sockfd, NULL, NULL);
		if (-1 == clientfd){
			perror("Fail to accept.");
			exit(EXIT_FAILURE);
		}
		printf("--------------------------------\n");
		printf("accept a client\n");
		printf("--------------------------------\n");
	
    pthread_create(&tid, NULL, handler, (void *)(long int)clientfd);
    pthread_detach(tid);
	}
	
	// 6. 关闭套接字
	close(sockfd);
	unlink(argv[1]);
	
	return 0;
}
client.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <errno.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/un.h>

// ./client "mypath"
int main(int argc, const char *argv[])
{
	int ret = 0;
	int sockfd;
	char packet[1024];
	struct sockaddr_un server_addr;
	
	if (argc < 2){
		fprintf(stderr, "Usage: %s <path>\n", argv[0]);
		exit(EXIT_FAILURE);
	}
	
	// 1. 创建监听socket
	sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
	if (-1 == sockfd){
		perror("Fail to socket.");
		exit(EXIT_FAILURE);
	}
	
	// 2. 连接服务端(connect)
	server_addr.sun_family = AF_UNIX;
	strcpy(server_addr.sun_path, argv[1]);
	
	ret = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
	if (-1 == ret){
		perror("Fail to connect.");
		exit(EXIT_FAILURE);
	}
	
	while (1){
		putchar('\n');
		putchar('>');
		fgets(packet, sizeof(packet), stdin);
		packet[strlen(packet) - 1] = '\0';
		
		// 3. 发送(写)
		ret = send(sockfd, packet, strlen(packet), 0);
		if (-1 == ret){
			perror("Fail to connect.");
			exit(EXIT_FAILURE);
		}
		
		/* 4. 接收(读)
		ret = recv(sockfd, packet, sizeof(packet), 0);
		if (-1 == ret){
			perror("Fail to connect.");
			exit(EXIT_FAILURE);
		}
		packet[ret] = '\0';
		
		printf("--------------------------------\n");
		printf("recv(%d) : %s\n", ret, packet);
		printf("--------------------------------\n");*/
		
		if (strcmp(packet, "quit") == 0) break;
	}
	
	// 5. 关闭套接字
	close(sockfd);
	
	
	return 0;
}


          
       2. 连接服务器
          struct sockaddr_un server_addr;

server_addr.sun_family = AF_UNIX;
strcpy(server_addr.sun_path, "mypath");

ret = connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));

  3. 发送数据
   ret = send(clientfd, buf, len, 0);
   
4. 接收数据
   ret = recv(clientfd, buf, sizeof(buf), 0);
   
5. 关闭套接字
   close(sockfd);
  
[3] unix域套接字--报文
    1. 客户端流程
       1. 创建套接字
          sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);
          
       2. 绑定(自己)地址
          struct sockaddr_un self_addr;

self_addr.sun_family = AF_UNIX;
strcpy(self_addr.sun_path, "client_addr");

ret = bind(sockfd, (struct sockaddr *)&self_addr, sizeof(self_addr));

3. 发送/接收
   struct sockaddr_un server_addr;

server_addr.sun_family = AF_UNIX;
strcpy(server_addr.sun_path, "server_addr");

   ret = sendto(sockfd, buf, len, 0, (struct sockaddr *)&server_addr, sizeof(server_addr));
   
   
   // 接收
   struct sockaddr_un peer_addr;
   socklen_t addrlen = sizeof(peer_addr);
   
   ret = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&peer_addr, &addrlen);
   
4. 关闭套接字
   close(sockfd);
   unlink("client_addr");
       
    2. 服务端流程
       1. 创建套接字
          sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);
          
       2. 绑定(自己)地址
          struct sockaddr_un self_addr;

self_addr.sun_family = AF_UNIX;
strcpy(self_addr.sun_path, "server_addr");

ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));

  3. 接收
     struct sockaddr_un peer_addr;
   socklen_t addrlen = sizeof(peer_addr);
   
   ret = recvfrom(sockfd, buf, sizeof(buf), 0, (struct sockaddr *)&peer_addr, &addrlen);
   

4. 发送
   ret = sendto(sockfd, buf, len, 0, (struct sockaddr *)&peer_addr, sizeof(peer_addr));
   
5. 关闭
   close(sockfd);
   unlink("server_addr");

client.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <errno.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/un.h>

// ./server client_addr server_addr
int main(int argc, const char *argv[])
{
	int ret = 0;
	int sockfd;
	char packet[1024];
	struct sockaddr_un server_addr;
	struct sockaddr_un peer_addr;
	socklen_t addrlen = sizeof(server_addr);
	
	if (argc < 3){
		fprintf(stderr, "Usage: %s <client addr> <server addr>\n", argv[0]);
		exit(EXIT_FAILURE);
	}
	
	// 1. 创建套接字
	sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);
	if (-1 == sockfd){
		perror("Fail to socket");
		exit(EXIT_FAILURE);
	}
	
  // 2. 绑定(自己)地址
	peer_addr.sun_family = AF_UNIX;
	strcpy(peer_addr.sun_path, argv[1]);
	
	ret = bind(sockfd, (struct sockaddr *)&peer_addr, sizeof(peer_addr));
	if (-1 == ret){
		perror("Fail to bind");
		exit(EXIT_FAILURE);
	}
	
	server_addr.sun_family = AF_UNIX;
	strcpy(server_addr.sun_path, argv[2]);
  
	while (1){
  
	  putchar('\n');
	  putchar('>');
	  fgets(packet, sizeof(packet), stdin);
	  packet[strlen(packet) - 1] = '\0';
	  
	  // 3. 发送(写)
	  ret = sendto(sockfd, packet, strlen(packet), 0, (struct sockaddr *)&server_addr, sizeof(server_addr));
	 	if (-1 == ret){
	 		perror("Fail to sendto.");
	 		break;
	 	}
	 	
	 	printf("-----------------------------\n");
	 	printf("send : %s\n", packet);
	 	printf("-----------------------------\n");
	 	
	  
	  // 4. 接收(读)
	  ret = recvfrom(sockfd, packet, sizeof(packet), 0, (struct sockaddr *)&server_addr, &addrlen);
	  if (-1 == ret){
	  	perror("Fail to recvfrom.");
	 		break;
	  }
	  packet[ret] = '\0';
	 	
	 	printf("-----------------------------\n");
		printf("recv(%d): %s\n", ret, packet);
		printf("-----------------------------\n");
	 	
	  if (strcmp(packet, "quit") == 0){
	  	break;
	  }
	}
  
  // 5. 关闭
  close(sockfd);
  unlink(argv[1]);
	
	return 0;
}
server.c

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <strings.h>

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/un.h>

// ./server server_addr
int main(int argc, const char *argv[])
{
	int ret = 0;
	int sockfd;
	char packet[1024];
	struct sockaddr_un server_addr;
	 struct sockaddr_un peer_addr;
	socklen_t addrlen = sizeof(peer_addr);
	
	if (argc < 2){
		fprintf(stderr, "Usage: %s <src addr>\n", argv[0]);
		exit(EXIT_FAILURE);
	}
	
	// 1. 创建套接字
	sockfd = socket(AF_UNIX, SOCK_DGRAM, 0);
	if (-1 == sockfd){
		perror("Fail to socket.");
		exit(EXIT_FAILURE);
	}
	
	// 2. 绑定(自己)地址
	server_addr.sun_family = AF_UNIX;
  strcpy(server_addr.sun_path, argv[1]);
	
	ret = bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
	if (-1 == ret){
		perror("Fail to bind.");
		exit(EXIT_FAILURE);
	}
  	
	while (1){
		// 3. 接收数据包
		ret = recvfrom(sockfd, packet, sizeof(packet), 0, (struct sockaddr *)&peer_addr, &addrlen);
		if (-1 == ret){
			perror("Fail to recvfrom.");
			break;
		}
		packet[ret] = '\0';
		
		printf("-----------------------------\n");
		printf("from   : %s\n", peer_addr.sun_path);
		printf("recv(%d): %s\n", ret, packet);
		printf("-----------------------------\n");
		
		// 4. 发送数据包
		ret = sendto(sockfd, packet, ret, 0, (struct sockaddr *)&peer_addr, sizeof(peer_addr));
		if (-1 == ret){
			perror("Fail to sendto.");
			break;
		}
		
		if (strcmp(packet, "quit") == 0){
			break;
		}
	}
	
	// 5. 关闭
	close(sockfd);
	unlink(argv[1]);
	
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值