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;
}
代码运行效果如下图: