网络部分阶段测试编程题

This commit is contained in:
flykhan 2023-09-22 23:25:11 +08:00
parent 5cdb1cc311
commit 93ec33f5b6
6 changed files with 333 additions and 0 deletions

BIN
exam/t1_client Executable file

Binary file not shown.

62
exam/t1_client.c Normal file
View File

@ -0,0 +1,62 @@
// 利用 setsocketopt() 使用嵌套字支持广播,并编写两个程序,其中一个发送广播数据,另一个接收广播数据
// 客户端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
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;
}

BIN
exam/t1_server Executable file

Binary file not shown.

50
exam/t1_server.c Normal file
View File

@ -0,0 +1,50 @@
// 利用 setsocketopt() 使用嵌套字支持广播,并编写两个程序,其中一个发送广播数据,另一个接收广播数据
// 服务端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
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;
}

89
exam/t2.c Normal file
View File

@ -0,0 +1,89 @@
// 写一个 TCP 并发的服务器,实现 echo 功能
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
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;
}

132
exam/t3.c Normal file
View File

@ -0,0 +1,132 @@
/*
*
*
* 1 arp/ip/tcp/udp
* 2
*/
#include <stdio.h>
#include <pcap/pcap.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h> // memset
#include <net/ethernet.h> // ether_header
#include <netinet/ip.h> // ip
#include <netinet/if_ether.h> // ether_arp
#include <unistd.h> // 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;
}