2024-8-21网络编程课后作业

1.设计一个建议聊天室,用户进入及进入会广播用户IP、端口和用户名

代码:

include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>                       
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>

int sockfd;  // 组播套接字文件描述符
int sock_fd; // 广播套接字文件描述符
struct sockaddr_in broadcast; // 广播地址结构体
struct sockaddr_in multicast; // 组播地址结构体
socklen_t multicast_len; // 组播地址长度
const char*Username;    
struct sockaddr_in local; // 本地地址结构体

// 接收组播消息的子线程函数
void *recv_multicast(void*arg) {
    int sockfd = *(int *)arg; // 获取传入的组播套接字文件描述符
    char buf[1024]; // 用于存储接收到的消息
    struct sockaddr_in from_addr; // 存储发送方的地址信息
    socklen_t addr_len = sizeof(from_addr); // 地址信息长度

    while (1) {
        memset(buf, 0, 1024); // 清空缓冲区
        // 接收组播消息
        int nbytes = recvfrom(sockfd, buf, 1024, 0, (struct sockaddr *)&from_addr, &addr_len);
        if (nbytes < 0) {
            perror("recvfrom failed"); // 接收失败时打印错误信息
            continue;
        }
        printf("Received: %s\n", buf);    // 打印接收到的消息
    }
}

// 处理程序退出时的函数
void handle_exit(int sig) {
    printf("Handling exit...\n"); // 添加调试信息,确认函数被调用
    // 发送退出组播组的广播消息
    char exit_message[1024];
    snprintf(exit_message, sizeof(exit_message), "[端口号: %d IP: %s] 用户%s退出了聊天室", ntohs(local.sin_port), inet_ntoa(local.sin_addr),Username);
    int result = sendto(sock_fd, exit_message, strlen(exit_message), 0, (struct sockaddr*)&broadcast, sizeof(broadcast));
    if (result < 0) {
        perror("Error sending exit message"); // 如果发送失败,输出错误信息
    } else {
        printf("Exit message sent: %s\n", exit_message); // 如果发送成功,输出调试信息
    }
    
    // 关闭套接字
    close(sockfd);
    close(sock_fd);
    exit(0); // 退出程序
}

int main(int argc, const char *argv[]) {
    if (argc != 4) { // 检查命令行参数是否正确
        printf("Usage: %s <local_ip> <local_port> <Username>n", argv[0]); // 提示正确用法
        return -1; // 参数错误时退出
    }
	Username = argv[3];

    
    // 1. 创建组播套接字
    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    
    // 2. 设置套接字的组播属性
    struct ip_mreq zubo;
    zubo.imr_multiaddr.s_addr = inet_addr("224.118.118.118"); // 设置组播地址
    zubo.imr_interface.s_addr = INADDR_ANY; // 允许从任何网络接口加入组播组
    if (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &zubo, sizeof(zubo)) < 0) {
        perror("setsockopt IP_ADD_MEMBERSHIP error");
        return -1;
    }

    // 创建广播套接字
    sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
    
    // 设置广播属性
    int bt = 1;
    if (setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, &bt, sizeof(bt)) < 0) {
        perror("setsockopt SO_BROADCAST error");
        return -1;
    }
    
    // 定义广播结构体
    broadcast.sin_family = AF_INET;
    broadcast.sin_port = htons(atoi(argv[2])); // 设置广播端口
    broadcast.sin_addr.s_addr = inet_addr("255.255.255.255"); // 设置广播地址

    // 绑定本地IP和端口
    local.sin_family = AF_INET;
    local.sin_port = htons(atoi(argv[2])); // 设置本地端口
    local.sin_addr.s_addr = inet_addr(argv[1]); // 设置本地IP

    // 绑定组播结构体
    multicast.sin_family = AF_INET;
    multicast.sin_port = htons(atoi(argv[2])); // 设置组播端口
    multicast.sin_addr.s_addr = inet_addr("224.118.118.118"); // 设置组播地址
    
    socklen_t local_len = sizeof(local); // 本地地址长度
    multicast_len = sizeof(multicast); // 组播地址长度

    // 绑定本地地址到组播套接字
    if (bind(sockfd, (struct sockaddr *)&local, local_len) < 0) {
        perror("bind error");
        return -1;
    }

    // 创建接收组播消息的子线程
    pthread_t tid;
    pthread_create(&tid, NULL, recv_multicast, (void*)&sockfd);

    // 发送入场信息
    char join_message[1024];
    snprintf(join_message, sizeof(join_message), "[端口号: %d IP: %s] 用户%s加入了聊天室", ntohs(local.sin_port), inet_ntoa(local.sin_addr),argv[3]);
    int result = sendto(sock_fd, join_message, strlen(join_message), 0, (struct sockaddr*)&broadcast, sizeof(broadcast));
    if (result < 0) {
        perror("Error sending join message");
    } else {
        printf("Join message sent: %s\n", join_message);
    }

    // 捕获退出信号
    signal(SIGINT, handle_exit);

    // 通信循环
    char *buf = malloc(100); // 分配100字节缓冲区
    while (1) {
        memset(buf, 0, 100); // 清空缓冲区
        fgets(buf, 100, stdin); // 从标准输入读取一行
        sendto(sockfd, buf, 100, 0, (struct sockaddr *)&multicast, multicast_len); // 将输入内容发送到组播地址
    }

    return 0;
}

代码运行效果如下图:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值