네트워크프로그래밍 6 장과제샘플코드 - 1:1 채팅 (udp 버전 )
과제 서버에서먼저 bind 하고그포트를다른사람에게알려줄것 클라이언트에서알려준포트로접속 서로간에키보드입력을받아상대방에게메시지전송 2
Makefile 1 SRC_DIR =../../common 2 COM_OBJS = $(SRC_DIR)/addressUtility.o $(SRC_DIR)/dieWithMessage.o ucprocess.o 3 SRV_OBJS = ucserver.o 4 CLI_OBJS = ucclient.o 5 6 CFLAGS = -g -std=gnu99 7 CC = gcc 8 9 all: 10 make ucserver 11 make ucclient 12 13 ucserver : $(COM_OBJS) $(SRV_OBJS) 14 gcc -o ucserver $(CFLAGS) $(COM_OBJS) $(SRV_OBJS) 15 16 ucclient : $(COM_OBJS) $(CLI_OBJS) 17 gcc -o ucclient $(CFLAGS) $(COM_OBJS) $(CLI_OBJS) 18 19 install : 20 mv ucserver ucclient ~/bin 21 22 clean : 23 rm *.o ucserver ucclient 3
ucprotocol.h 1 #ifndef UC_PROTOCOL_H_ 2 #define UC_PROTOCOL_H_ 3 4 #include <stdint.h> 5 #include <unistd.h> 6 7 enum { 8 MAX_WIRE_SIZE = 512, 9 MAX_MSG_SIZE = 500 10 }; 11 12 int read_and_sendto(int ifd, char *nickname, int sock, struct sockaddr *toaddr); 13 int recvfrom_and_write(int sock, struct sockaddr *fromaddr, int ofd); 14 #endif 4
ucprocess.c (1) 1 #include <stdlib.h> 2 #include <string.h> 3 #include <sys/types.h> 4 #include <sys/socket.h> 5 #include <netdb.h> 6 #include "../../common/practical.h" 7 #include "ucprotocol.h" 8 9 /******************************************************************************/ 10 int read_and_sendto(int ifd, char *nickname, int sock, struct sockaddr *toaddr) 11 { 12 uint8_t snd_msg[max_msg_size] = {0}; 13 uint8_t outbuf[max_wire_size] = {0}; 14 15 if (read(ifd, snd_msg, MAX_MSG_SIZE) <= 0) { 16 fprintf(stderr, "Read ERROR!\n"); 17 return 0; 18 } 5
ucprocess.c (2) 19 else { 20 sprintf(outbuf, "%s: ", nickname); 21 strncat(outbuf, snd_msg, MAX_WIRE_SIZE - strlen(outbuf)); 22 23 int buf_len = strlen(outbuf); 24 ssize_t numbytessent = sendto(sock, outbuf, buf_len, 0, 25 toaddr, sizeof(*toaddr)); 26 27 if (numbytessent!= buf_len) { 28 fprintf(stderr, "Write ERROR!\n"); 29 return 0; 30 } 31 } 32 return 1; 33 } 34 6
ucprocess.c (3) 35 /******************************************************************************/ 36 int recvfrom_and_write(int sock, struct sockaddr *fromaddr, int ofd) 37 { 38 uint8_t inbuf[max_wire_size] = {0}; 39 int addrlen = sizeof(*fromaddr); 40 41 ssize_t numbytesrcvd = recvfrom(sock, &inbuf, MAX_WIRE_SIZE, 0, 42 fromaddr, &addrlen); 43 if (numbytesrcvd < 0) 44 return 0; 45 46 if (write(ofd, inbuf, MAX_WIRE_SIZE) <= 0) { 47 fprintf(stderr, "Write ERROR!\n"); 48 return 0; 49 } 50 return 1; 51 } 7
ucserver.c (1) 1 #include <stdlib.h> 2 #include <string.h> 3 #include <sys/types.h> 4 #include <sys/socket.h> 5 #include <netdb.h> 6 #include "../../common/practical.h" 7 #include "ucprotocol.h" 8 9 /******************************************************************************/ 10 int main(int argc, char *argv[]) { 11 12 if (argc!= 2) // 명령어행의인자개수가알맞은지확인 13 DieWithUserMessage("Parameter(s)", "<Server Port/Service>"); 14 15 char *service = argv[1]; // 첫번째인자 : 지역포트 / 서비스이름 16 17 struct addrinfo addrcriteria; // 원하는주소기준설정 18 memset(&addrcriteria, 0, sizeof(addrcriteria)); // 구조체를 0으로초기화 19 addrcriteria.ai_family = AF_UNSPEC; // IPv4,IPv6 모두반환 20 addrcriteria.ai_flags = AI_PASSIVE; // 주소 / 포트에무관하게반환 21 addrcriteria.ai_socktype = SOCK_DGRAM; // 데이터그램소켓만반환 22 addrcriteria.ai_protocol = IPPROTO_UDP; // UDP만반환 23 8
ucserver.c (2) 24 struct addrinfo *servaddr; 25 int rtnval = getaddrinfo(null, service, &addrcriteria, &servaddr); 26 if (rtnval!= 0) 27 DieWithUserMessage("getaddrinfo() failed", gai_strerror(rtnval)); 28 29 int sock = socket(servaddr->ai_family, servaddr->ai_socktype, 30 servaddr->ai_protocol); 31 if (sock < 0) 32 DieWithSystemMessage("socket() failed"); 33 34 if (bind(sock, servaddr->ai_addr, servaddr->ai_addrlen) < 0) 35 DieWithSystemMessage("bind() failed"); 36 37 int in_the_session = 0; 38 39 struct sockaddr_storage clntaddr; 40 socklen_t clntaddrlen = sizeof(clntaddr); 9
ucserver.c (3) 42 fd_set master_set, read_set; 43 44 FD_ZERO(&master_set); 45 FD_SET(0, &master_set); 46 FD_SET(sock, &master_set); 47 48 while(read_set = master_set, select(sock+1, &read_set, NULL, NULL, NULL) > 0) { 49 if (FD_ISSET(0, &read_set) && in_the_session) { 50 if (in_the_session) { 51 if (!read_and_sendto(0, "SERVER", sock, 52 (struct sockaddr *)&clntaddr)) 53 DieWithSystemMessage("sendto() failed"); 54 } 55 } 10
ucserver.c (4) 56 if (FD_ISSET(sock, &read_set)) { 57 if (!recvfrom_and_write(sock, (struct sockaddr *) &clntaddr, 1)) { 58 DieWithSystemMessage("recvfrom() failed"); 59 } 60 61 if (!in_the_session) { // First message 62 fputs("handling client ", stdout); 63 PrintSocketAddress((struct sockaddr *) &clntaddr, stdout); 64 fprintf(stdout, "\n"); 65 in_the_session = 1; 66 } 67 } 68 } 69 freeaddrinfo(servaddr); 70 close(sock); 71 } 11
ucclient.c (1) 1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 #include <unistd.h> 5 #include <sys/socket.h> 6 #include <netdb.h> 7 #include "../../common/practical.h" 8 #include "ucprotocol.h" 9 10 /******************************************************************************/ 11 int main(int argc, char *argv[]) { 12 13 if (argc!= 4 ) // 명령어행인자의개수가알맞은지확인 14 DieWithUserMessage("Parameter(s)", 15 "<Server Address/Name> <Server Port/Service> <nickname>"); 16 17 char *server = argv[1]; // 첫번쨰인자 : 서버의주소 / 이름 18 char *servport = argv[2]; // 두번째인자 : 서버포트 / 서비스이름 19 char *nickname = argv[3]; // 세번째인자 : 별명 20 12
ucclient.c (2) 21 // 어떤형태의주소를원하는지시스템에알림 22 struct addrinfo addrcriteria; // 원하는주소기준설정 23 memset(&addrcriteria, 0, sizeof(addrcriteria)); // 구조체를 0으로초기화 24 addrcriteria.ai_family = AF_UNSPEC; // IPV4,IPV6 모두반환 25 addrcriteria.ai_socktype = SOCK_DGRAM; // 데이터그램소켓만반환 26 addrcriteria.ai_protocol = IPPROTO_UDP; // TCP만반환 27 28 // 주소들획득 29 30 struct addrinfo *addr; 31 struct addrinfo *servaddr; // 서버주소의리스트 32 33 int rtnval = getaddrinfo(server, servport, &addrcriteria, &servaddr); 34 if (rtnval!= 0) 35 DieWithUserMessage("getaddrinfo() failed", gai_strerror(rtnval)); 36 37 int sock = -1; 38 for (addr = servaddr; addr!= NULL; addr = addr->ai_next) { 39 // 데이터그램 UDP소켓생성 40 sock = socket(addr->ai_family, addr->ai_socktype, 41 addr->ai_protocol); // 클라이언트소켓식별자 42 if (sock > 0) 43 break; 44 } 13
ucclient.c (3) 46 fd_set master_set, read_set; 47 48 FD_ZERO(&master_set); 49 FD_SET(0, &master_set); 50 FD_SET(sock, &master_set); 51 52 while(read_set = master_set, select(sock+1, &read_set, NULL, NULL, NULL) > 0) { 53 if (FD_ISSET(0, &read_set)) { 54 if (!read_and_sendto(0, nickname, sock, addr->ai_addr)) 55 DieWithSystemMessage("sendto() failed"); 56 } 57 58 if (FD_ISSET(sock, &read_set)) { 59 struct sockaddr_storage fromaddr; 60 61 if (!recvfrom_and_write(sock, (struct sockaddr *) &fromaddr, 1) ) { 62 DieWithSystemMessage("recvfrom() failed"); 63 } 64 } 65 } 66 67 freeaddrinfo(servaddr); 68 close(sock); 69 70 exit(0); 71 } 14