[1] 网络异常处理
1. 客户端出了问题(死机、重启了、网络断了...)
2. 服务端出了问题(死机、重启了、网络断了、内存耗尽、...)
3. 网络不正常
解决办法: 心跳包
心跳包的实现方法:
1. 利用TCP协议的KeepAlive(TCP协议实现的心跳包)
见《tcp_keepalive》的服务端
2. 设置接收超时检测心跳,对端需要定时发送
heart_beat程序只在接收端实现了如何(利用阻塞超时)检测超时
3. 如何实现应用程序的定时发送和超时检测?
alarm
总结: 利用实现心跳包
[2] 广播
[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. 接收流程
[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);
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");
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;
}