qfedu-network-advanced-level/day3/qq2/qq3.c

123 lines
3.1 KiB
C
Raw Permalink Normal View History

2023-09-06 16:31:13 +08:00
// 群聊(组播-多播)
#include <stdio.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/types.h>
/*
* ./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");
}
}
}
}