1/**
2 * UDP Chat (peer-to-peer)
3 *
4 * Simple UDP-based chat. Each instance can both send and receive.
5 * Uses select() to monitor both stdin and the UDP socket.
6 *
7 * Build: make
8 * Usage: ./udp_chat <local_port> <remote_ip> <remote_port>
9 * Example: Terminal 1: ./udp_chat 9001 127.0.0.1 9002
10 * Terminal 2: ./udp_chat 9002 127.0.0.1 9001
11 */
12
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
16#include <unistd.h>
17#include <sys/socket.h>
18#include <sys/select.h>
19#include <netinet/in.h>
20#include <arpa/inet.h>
21
22#define BUF_SIZE 1024
23
24int main(int argc, char *argv[]) {
25 if (argc != 4) {
26 fprintf(stderr, "Usage: %s <local_port> <remote_ip> <remote_port>\n",
27 argv[0]);
28 exit(EXIT_FAILURE);
29 }
30
31 int local_port = atoi(argv[1]);
32 const char *remote_ip = argv[2];
33 int remote_port = atoi(argv[3]);
34
35 /* Create UDP socket */
36 int sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
37 if (sock_fd < 0) {
38 perror("socket");
39 exit(EXIT_FAILURE);
40 }
41
42 /* Bind to local port */
43 struct sockaddr_in local_addr = {
44 .sin_family = AF_INET,
45 .sin_addr.s_addr = INADDR_ANY,
46 .sin_port = htons(local_port)
47 };
48
49 if (bind(sock_fd, (struct sockaddr *)&local_addr,
50 sizeof(local_addr)) < 0) {
51 perror("bind");
52 close(sock_fd);
53 exit(EXIT_FAILURE);
54 }
55
56 /* Setup remote address */
57 struct sockaddr_in remote_addr = {
58 .sin_family = AF_INET,
59 .sin_port = htons(remote_port)
60 };
61 inet_pton(AF_INET, remote_ip, &remote_addr.sin_addr);
62
63 printf("UDP Chat - listening on port %d, sending to %s:%d\n",
64 local_port, remote_ip, remote_port);
65 printf("Type messages (Ctrl+D to quit):\n\n");
66
67 fd_set read_fds;
68 char buffer[BUF_SIZE];
69 int max_fd = (sock_fd > STDIN_FILENO) ? sock_fd : STDIN_FILENO;
70
71 while (1) {
72 FD_ZERO(&read_fds);
73 FD_SET(STDIN_FILENO, &read_fds);
74 FD_SET(sock_fd, &read_fds);
75
76 int ready = select(max_fd + 1, &read_fds, NULL, NULL, NULL);
77 if (ready < 0) {
78 perror("select");
79 break;
80 }
81
82 /* User typed something */
83 if (FD_ISSET(STDIN_FILENO, &read_fds)) {
84 if (fgets(buffer, BUF_SIZE, stdin) == NULL)
85 break;
86
87 sendto(sock_fd, buffer, strlen(buffer), 0,
88 (struct sockaddr *)&remote_addr, sizeof(remote_addr));
89 }
90
91 /* Received a message */
92 if (FD_ISSET(sock_fd, &read_fds)) {
93 struct sockaddr_in from_addr;
94 socklen_t from_len = sizeof(from_addr);
95 ssize_t bytes = recvfrom(sock_fd, buffer, BUF_SIZE - 1, 0,
96 (struct sockaddr *)&from_addr,
97 &from_len);
98 if (bytes > 0) {
99 buffer[bytes] = '\0';
100 char ip[INET_ADDRSTRLEN];
101 inet_ntop(AF_INET, &from_addr.sin_addr, ip, sizeof(ip));
102 printf("[%s:%d] %s", ip, ntohs(from_addr.sin_port), buffer);
103 }
104 }
105 }
106
107 close(sock_fd);
108 printf("Chat ended\n");
109 return 0;
110}