diff --git a/exam/t1_client b/exam/t1_client new file mode 100755 index 0000000..04c8c1f Binary files /dev/null and b/exam/t1_client differ diff --git a/exam/t1_client.c b/exam/t1_client.c new file mode 100644 index 0000000..86c3163 --- /dev/null +++ b/exam/t1_client.c @@ -0,0 +1,62 @@ +// 利用 setsocketopt() 使用嵌套字支持广播,并编写两个程序,其中一个发送广播数据,另一个接收广播数据 +// 客户端 +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char const *argv[]) +{ + int sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 创建UDP套接字 + if (sockfd < 0) + { + perror("socket error"); + exit(1); + } + + struct sockaddr_in broadcast_addr, recv_addr; // 广播地址信息结构体对象, 接收广播消息的地址信息结构体对象 + memset(&recv_addr, 0, sizeof(recv_addr)); // 初始化接收广播消息的地址信息结构体对象 + recv_addr.sin_family = AF_INET; // 设置地址族 + recv_addr.sin_port = htons(8000); // 设置端口号 + recv_addr.sin_addr.s_addr = INADDR_ANY; // 设置接收任意地址 + + // 绑定套接字 + int is_bind = bind(sockfd, (struct sockaddr *)&recv_addr, sizeof(recv_addr)); // 绑定套接字 + if (is_bind < 0) + { + perror("bind error"); + exit(1); + } + + // 设置套接字选项,启用地址重用,允许接收广播消息 + int reuse_flag = 1; + // 说明:SOL_SOCKET表示当前套接字,SO_REUSEADDR表示启用地址重用,&reuse_flag表示设置选项的值,sizeof(flag)表示选项值的大小 + int ret = setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse_flag, sizeof(reuse_flag)); + if (ret < 0) + { + perror("setsockopt error"); + exit(1); + } + + // 接收广播消息 + char recv_buf[1024]; // 接收缓冲区 + + // 参数说明: sockfd表示套接字描述符,recv_buf表示接收缓冲区,sizeof(recv_buf)表示接收缓冲区大小,0表示阻塞接收,NULL表示不关注对端地址信息,0表示不关注对端地址信息长度 + ssize_t recv_len = recvfrom(sockfd, recv_buf, sizeof(recv_buf), 0, NULL, 0); + if (recv_len < 0) + { + perror("recvfrom error"); + exit(1); + } + + printf("接收到广播消息: %s\n", recv_buf); + + // 关闭套接字 + close(sockfd); + + return 0; +} diff --git a/exam/t1_server b/exam/t1_server new file mode 100755 index 0000000..7d0534c Binary files /dev/null and b/exam/t1_server differ diff --git a/exam/t1_server.c b/exam/t1_server.c new file mode 100644 index 0000000..4da20e4 --- /dev/null +++ b/exam/t1_server.c @@ -0,0 +1,50 @@ +// 利用 setsocketopt() 使用嵌套字支持广播,并编写两个程序,其中一个发送广播数据,另一个接收广播数据 +// 服务端 + +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char const *argv[]) +{ + int sockfd = socket(AF_INET, SOCK_DGRAM, 0); // 创建UDP套接字 + if (sockfd < 0) + { + perror("socket error"); + exit(1); + } + + char broadcast_msg[] = "this is a udp broadcast message"; // 广播消息 + + // 构造广播地址信息结构体对象 + struct sockaddr_in broadcast_addr; // 广播地址信息结构体对象 + memset(&broadcast_addr, 0, sizeof(broadcast_addr)); // 初始化广播地址信息结构体对象 + broadcast_addr.sin_family = AF_INET; // 设置地址族 + broadcast_addr.sin_port = htons(8000); // 设置端口号 + broadcast_addr.sin_addr.s_addr = inet_addr("255.255.255.255"); // 设置广播地址 + + // 设置套接字选项,允许发送广播消息 + int broadcast_enable_flag = 1; + // 说明:SOL_SOCKET表示当前套接字,SO_BROADCAST表示允许发送广播消息,&broadcast_enable_flag表示设置选项的值,sizeof(flag)表示选项值的大小 + int ret = setsockopt(sockfd, SOL_SOCKET, SO_BROADCAST, &broadcast_enable_flag, sizeof(broadcast_enable_flag)); + + // 发送广播消息 + ssize_t send_len = sendto(sockfd, broadcast_msg, strlen(broadcast_msg), 0, (struct sockaddr *)&broadcast_addr, sizeof(broadcast_addr)); + if (send_len < 0) + { + perror("sendto error"); + exit(1); + } + + printf("广播消息发送成功 (%s)\n", broadcast_msg); + + // 关闭套接字 + close(sockfd); + + return 0; +} diff --git a/exam/t2.c b/exam/t2.c new file mode 100644 index 0000000..598e4f1 --- /dev/null +++ b/exam/t2.c @@ -0,0 +1,89 @@ +// 写一个 TCP 并发的服务器,实现 echo 功能 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char const *argv[]) +{ + int sockfd = socket(AF_INET, SOCK_STREAM, 0); + if (sockfd < 0) + { + perror("socket"); + return -1; + } + + struct sockaddr_in server_addr; + memset(&server_addr, 0, sizeof(server_addr)); + server_addr.sin_family = AF_INET; // ipv4 + server_addr.sin_port = htons(8000); // port + server_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 服务端的ip地址 : 默认接收任意ip地址的连接 + + if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) != 0) + { + perror("bind"); + close(sockfd); // 关闭 socket 套接字描述符 + return -1; + } + + listen(sockfd, 5); // 创建队列长度为5的监听队列 + + while (1) + { + struct sockaddr_in client_addr; // 客户端地址信息 + memset(&client_addr, 0, sizeof(client_addr)); + socklen_t client_addr_len = sizeof(client_addr); // 地址信息长度 + + int client_fd = accept(sockfd, (struct sockaddr *)&client_addr, &client_addr_len); // 从监听队列中取出一个连接 + if (client_fd < 0) + { + perror("accept"); + continue; + } + + char client_ip[INET_ADDRSTRLEN] = ""; // 用于保存客户端的ip地址 + inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, sizeof(client_ip)); // 将网络字节序的ip地址转换为点分十进制的ip地址 + printf("客户端 %s 连接成功\n", client_ip); + + pid_t pid = fork(); // 创建子进程 + if (pid == 0) + { + close(sockfd); // 关闭子进程中的监听套接字描述符 + + while (1) + { + char buf[128] = ""; + ssize_t recv_len = recv(client_fd, buf, sizeof(buf), 0); // 接收客户端发送的数据 + if (recv_len > 0) + { + char send_buf[1024] = ""; + sprintf(send_buf, "收到 %s 发送的数据: %s", client_ip, buf); + + send(client_fd, send_buf, strlen(send_buf), 0); // 发送数据给客户端 + + if (strncmp(buf, "exit", 4) == 0) + { + printf("客户端 %s 断开连接\n", client_ip); + break; + } + } + } + + close(client_fd); // 关闭客户端连接套接字描述符 + printf("客户端 %s 断开连接\n", client_ip); + exit(0); // 子进程退出 + } + } + + close(sockfd); // 关闭监听套接字描述符 + + return 0; +} diff --git a/exam/t3.c b/exam/t3.c new file mode 100644 index 0000000..d758a1a --- /dev/null +++ b/exam/t3.c @@ -0,0 +1,132 @@ +/* +* 写一个网络数据分析器 +* 要求: +* 1、对 arp/ip/tcp/udp 数据进行区分。 +* 2、把数据包中的源信息,目的信息等重要的信息打印出来。 +*/ +#include +#include +#include +#include +#include // memset +#include // ether_header +#include // ip +#include // ether_arp +#include // sleep + +int main(int argc, char const *argv[]) +{ + char errbuf[PCAP_ERRBUF_SIZE]; // 用于存储错误信息的缓冲区 + char *dev = pcap_lookupdev(errbuf); // 获取网络接口 + if (dev == NULL) // 获取网络接口失败 + { + printf("pcap_lookupdev error: %s\n", errbuf); + return -1; + } + printf("网络接口: %s\n", dev); + + // 获取网卡的网络号,掩码地址 + bpf_u_int32 netip, netmask; + if (pcap_lookupnet(dev, &netip, &netmask, NULL) == 0) + { + unsigned char ip[INET_ADDRSTRLEN] = ""; + unsigned char mask[INET_ADDRSTRLEN] = ""; + + inet_ntop(AF_INET, &netip, ip, INET_ADDRSTRLEN); + inet_ntop(AF_INET, &netmask, mask, INET_ADDRSTRLEN); + printf("网络地址: %s 掩码地址: %s\n", ip, mask); + } + + // 打开网络设备,开始捕获数据 + pcap_t *pcap_handle = pcap_open_live(dev, BUFSIZ, 1, 0, errbuf); + if (pcap_handle == NULL) + { + printf("pcap_open_live error: %s\n", errbuf); + return -1; + } + + while (1) + { + struct pcap_pkthdr pcap_hdr; // 数据包头 + memset(&pcap_hdr, 0, sizeof(pcap_hdr)); // 清空结构体 + const u_char *packet = pcap_next(pcap_handle, &pcap_hdr); // 获取数据包 + if (packet == NULL) + { + printf("pcap_next error\n"); + return -1; + } + + // 分析 ethernet 报文类型 + // 如果是 arp 报文,则打印 arp 报文信息 + // 如果是 ip 报文,则打印 ip 报文信息 + struct ether_header *eth_hdr = (struct ether_header *)packet; + // 拆分源 mac 地址和目的 mac 地址 + unsigned char src_mac_x[ETHER_ADDR_LEN] = ""; // 源 mac 地址 + unsigned char dst_mac_x[ETHER_ADDR_LEN] = ""; // 目的 mac 地址 + memcpy(src_mac_x, eth_hdr->ether_shost, ETHER_ADDR_LEN); // 拷贝源 mac 地址 + memcpy(dst_mac_x, eth_hdr->ether_dhost, ETHER_ADDR_LEN); // 拷贝目的 mac 地址 + + char src_mac[18] = ""; // 存储 MAC 地址的字符串 + char dst_mac[18] = ""; // 存储 MAC 地址的字符串 + + // 将 MAC 地址转换成字符串 + sprintf(src_mac, "%02x:%02x:%02x:%02x:%02x:%02x", + src_mac_x[0], src_mac_x[1], src_mac_x[2], src_mac_x[3], src_mac_x[4], src_mac_x[5]); + + sprintf(dst_mac, "%02x:%02x:%02x:%02x:%02x:%02x", + dst_mac_x[0], dst_mac_x[1], dst_mac_x[2], dst_mac_x[3], dst_mac_x[4], dst_mac_x[5]); + + if (ntohs(eth_hdr->ether_type) == ETHERTYPE_ARP) + { + // 拆分源 ip 地址和目的 ip 地址 + unsigned char src_ip[INET_ADDRSTRLEN] = ""; // 源 ip 地址 + unsigned char dst_ip[INET_ADDRSTRLEN] = ""; // 目的 ip 地址 + struct ether_arp *arp_hdr = (struct ether_arp *)(packet + sizeof(struct ether_header)); + inet_ntop(AF_INET, arp_hdr->arp_spa, src_ip, INET_ADDRSTRLEN); // 拷贝源 ip 地址 + inet_ntop(AF_INET, arp_hdr->arp_tpa, dst_ip, INET_ADDRSTRLEN); // 拷贝目的 ip 地址 + + printf("arp 报文\n"); + printf("源地址: %s\t\t目的地址: %s\n源MAC: %s\t目的MAC: %s\n", src_ip, dst_ip, src_mac, dst_mac); // 打印 arp 报文信息 + } + else if (ntohs(eth_hdr->ether_type) == ETHERTYPE_IP) + { + // 拆分源 ip 地址和目的 ip 地址 + unsigned char src_ip[INET_ADDRSTRLEN] = ""; // 源 ip 地址 + unsigned char dst_ip[INET_ADDRSTRLEN] = ""; // 目的 ip 地址 + struct ip *ip_hdr = (struct ip *)(packet + sizeof(struct ether_header)); + inet_ntop(AF_INET, &ip_hdr->ip_src, src_ip, INET_ADDRSTRLEN); // 拷贝源 ip 地址 + inet_ntop(AF_INET, &ip_hdr->ip_dst, dst_ip, INET_ADDRSTRLEN); // 拷贝目的 ip 地址 + + printf("ip 报文\n"); + + // 分析 ip 报文类型 + // 如果是 tcp 报文,则打印 tcp 报文信息 + // 如果是 udp 报文,则打印 udp 报文信息 + if (ip_hdr->ip_p == IPPROTO_TCP) + { + printf("tcp 报文\n"); + printf("源地址: %s\t\t目的地址: %s\n源MAC: %s\t目的MAC: %s\n", src_ip, dst_ip, src_mac, dst_mac); // 打印 ip 报文信息 + } + else if (ip_hdr->ip_p == IPPROTO_UDP) + { + printf("udp 报文\n"); + printf("源地址: %s\t\t目的地址: %s\n源MAC: %s\t目的MAC: %s\n", src_ip, dst_ip, src_mac, dst_mac); // 打印 ip 报文信息 + } + else + { + printf("其他报文\n"); + } + } + else + { + printf("其他报文\n"); + } + + sleep(1); + } + + // 关闭网络设备 + pcap_close(pcap_handle); + + return 0; +}