// 群聊(组播-多播) #include #include #include #include #include #include #include #include #include /* * ./a.out port */ void *recv_task(void *arg); void *send_task(void *arg); int bind_port; char groupip[16] = ""; int main(int argc, char const *argv[]) { if (argc < 3) { printf("usage: %s port groupip\n", argv[0]); return -1; } bind_port = atoi(argv[1]); strcpy(groupip, argv[2]); // 多播组ip int sock_fd = socket(AF_INET, SOCK_DGRAM, 0); if (sock_fd < 0) { perror("socket"); return EXIT_FAILURE; // 1 } // 绑定端口 struct sockaddr_in bind_addr; memset(&bind_addr, 0, sizeof(bind_addr)); bind_addr.sin_family = AF_INET; bind_addr.sin_port = htons(bind_port); bind_addr.sin_addr.s_addr = htonl(INADDR_ANY); bind(sock_fd, (struct sockaddr *)&bind_addr, sizeof(bind_addr)); // 绑定文件描述符和地址端口 // 设置 socket 选项为多播(组号从命令行获取) struct ip_mreq group_mreq; // 组消息请求结构体对象 group_mreq.imr_multiaddr.s_addr = inet_addr(groupip); // 添加多播组的ip group_mreq.imr_interface.s_addr = htonl(INADDR_ANY); // 当前主机的所有 ip 地址加入多播组中 int ret = setsockopt(sock_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &group_mreq, sizeof(group_mreq)); if (ret != 0) { perror("setsockopt"); } pthread_t tid1, tid2; pthread_create(&tid1, NULL, recv_task, &sock_fd); pthread_create(&tid2, NULL, send_task, &sock_fd); pthread_join(tid2, NULL); pthread_cancel(tid1); pthread_join(tid1, NULL); printf("---over---\n"); close(sock_fd); return 0; } void *recv_task(void *arg) { int sock_fd = *((int *)arg); while (1) { struct sockaddr_in data_addr; socklen_t data_addr_len = sizeof(data_addr); bzero(&data_addr, data_addr_len); char buf[128] = ""; ssize_t recv_len = recvfrom(sock_fd, buf, 128, 0, (struct sockaddr *)&data_addr, &data_addr_len); if (recv_len > 0) { char ip[16] = ""; inet_ntop(AF_INET, &data_addr.sin_addr.s_addr, ip, 16); printf("%s(%d): %s\n", ip, ntohs(data_addr.sin_port), buf); } } } void *send_task(void *arg) { int sock_fd = *((int *)arg); while (1) { char buf[128] = ""; if (fgets(buf, 128, stdin) != NULL) { buf[strlen(buf) - 1] = 0; struct sockaddr_in addr; bzero(&addr, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(bind_port); addr.sin_addr.s_addr = inet_addr(groupip); // 一定是多播地址 sendto(sock_fd, buf, strlen(buf) + 1, 0, (struct sockaddr *)&addr, sizeof(addr)); if (strncmp(buf, "bye", 3) == 0) { break; } if (strncmp(buf, "clear", 5) == 0) { system("clear"); } } } }