TCP& UDP
포트 번호를 이용해 주소를 지정한다
- 응용 프로그램이 TCP나 UDP를 이용해 통신하려면 반드시 포트 번호를 결정해야한다
데이터 오류를 체크한다
- IP와 달리 TCP와 UDP 데이터에 대한 오류를 체크한다
TCP | UDP | |
프로토콜 | 연결형 프로토콜 세션을 성립 후 통신 |
비연결형 프로토콜 세션을 성립하지 않고 통신 |
신뢰성 | 신뢰성이 있는 데이터 전송 드랍시 데이터 재전송 |
신뢰성이 없는 데이터 전송 드랍시 데이터 재전송하지 않음 |
기본 통신 | 1 : 1 통신 | 1: 1 통신 1: N 통신 |
데이터 경계 | 데이터 경계 구분 바이트 스트림 서비스 |
데이터 경계 구분을 하지 않음 데이터그램 서비스 |
UDP(User Datagram Protocol)
- 연결(세션) 설정을 하지 않으므로 connect() 함수 불필요
- 프로토콜에서 신뢰성 있는 데이터 전송을 보장하지 않는다
- 필요시 응용 프로그램에서 신뢰성 있는 데이터 전송 기능을 구현!!
- 간단한 소켓 함수 호출 절차만 따르면 1:N 통신을 쉽게 구현할 수 있다
- TCP와 달리 데이터 경계 구분을 작업을 할 필요가 없다
UDP Server - Client
- UDP는 TCP와 달리 데이터 재전송과 흐름 제어를 하지 않아 송신 버퍼가 존재하지 않는다
- 소켓 통신을 위해 결정해야할 요소
- 프로토콜 : 통신 규약
- 지역 IP : 서버 또는 클라이언트 주소
- 원격 IP 주소와 원격 포트 번호 : 서버 또는 클라이언트가 통신하는 상대의 주소
UDP Server
socket() : 소켓을 생성, 프로토콜 결정
bind() : 지역 IP 주소와 지역 포트번호를 결정
recvfrom() : 클라이언트가 보낸 데이터를 받는다 -> 클라이언트 주소를 알 수 있다
sendto() : 받은 데이터를 처리한 결과를 보낸다
closesocket() : 모든 작업을 마치면 소켓을 닫는다
UDP Client
socket() : 소켓 생성, 프로토콜 결정
sendto() : 서버에 데이터를 보낸다 -> 서버의 IP주소 포트번호, 지역 IP 주소 지역 포트 번호도 결정
recvfrom() : 서버가 처리해 보낸 데이터를 받는다
closesocekt() : 모든 작업을 마치면 소켓을 닫는다
UDP 서버 클라이언트 모델 사용할 때 주의사항
- 블로킹 소켓을 사용할 경우
- 송수신 함수의 호출 순서가 맞지 않으면 교착상태가 발생할 수 있다
- 클라이언트 데이터를 받은 후 송신자의 주소 (IP 주소, 포트번호)를 확인해야한다
- recvfrom()함수는 UDP 서버가 보낸 데이터는 물론, 전혀 다른 UDP 응용 프로그램이 보낸 데이터를 수신할 수 있다
데이터 전송 함수
sendto()
- 응용 프로그램 데이터를 OS의 송신 버퍼에 복사함으로써 데이터를 전송
- 소켓의 지역 IP 주소와 지역 포트 번호가 아직 결정되지 않은 상태라면 OS가 자동으로 결정한다
// ========== Windows ==========
#include <winsock2.h>
int sendto(
SOCKET sock,
const char* buf,
int len,
int flags,
const struct sockaddr* addr
int addrlen
);
// 성공 : 보낸 바이트 수
// 실패 : SOCKET_ERROR
// ========== Linux ==========
#include <sys/types.h>
#include <sys/socket.h>
ssize_t sendto(
int sock,
void* buf,
size_t len,
int flags,
struct sockaddr* addr
socklen_t addrlen
)
// 성공 : 보낸 바이트 수
// 실패 : -1
recvfrom()
- OS의 수신 버퍼에 도착한 데이터를 응용 프로그램 버퍼에 복사
- UDP 패킷 데이터를 한번에 하나만 읽을 수 있다
// ========== Windows ==========
#include <winsock2.h>
int recvfrom(
SOCKET sock,
char* buf,
int len,
int flags,
const struct sockaddr* addr
int* addrlen
);
// 성공 : 받은 바이트 수
// 실패 : SOCKET_ERROR
// ========== Linux ==========
#include <sys/types.h>
#include <sys/socket.h>
ssize_t recvfrom(
int sock,
void* buf,
size_t len,
int flags,
struct sockaddr* addr
socklen_t* addrlen
)
// 성공 : 받은 바이트 수
// 실패 : -1
UDP Server 구현
UDPServer.cpp
#include "Common.h"
#define SERVERPORT 9000
#define BUFSIZE 512
int main() {
int retval;
// ===== 윈속 초기화 =====
WSADATA wsa;
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) return 1;
// ===== 소켓 생성 =====
SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0); // SOCK_DGRAM : 데이터 그램 방식
if (sock == INVALID_SOCKET) printf("SOCKET() ERROR!!!\n");
struct sockaddr_in serveraddr;
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons(SERVERPORT);
// ===== BIND() =====
retval = bind(sock, (struct sockaddr*)&serveraddr, sizeof(serveraddr));
if (retval == SOCKET_ERROR) printf("BIND() ERROR!!!\n");
// 통신에 사용할 변수들
struct sockaddr_in clientaddr;
int addrlen;
char buf[BUFSIZE + 1];
// ===== 클라이언트 데이터 통신 =====
while (1) {
addrlen = sizeof(clientaddr);
// ===== RECVFROM() =====
retval = recvfrom(sock, buf, BUFSIZE, 0, (struct sockaddr*)&clientaddr, &addrlen);
if (retval == SOCKET_ERROR) {
printf("RECVFROM() ERROR!!!\n");
break;
}
// 받은 데이터 출력
buf[retval] = '\0';
char addr[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &clientaddr.sin_addr, addr, sizeof(addr));
printf("[UDP %s:%d] %s\n", addr, ntohs(clientaddr.sin_port), buf);
// ===== SENDTO() =====
retval = sendto(sock, buf, retval, 0, (struct sockaddr*)&clientaddr, sizeof(clientaddr));
if (retval == SOCKET_ERROR) {
printf("SENDTO() ERROR!!!\n");
break;
}
}
// ===== 소켓 닫기 ====
closesocket(sock);
// ===== 윈속 닫기 =====
WSACleanup();
}
UDP Client 구현
UDPClient.cpp
#include "Common.h"
#define SERVERPORT 9000
#define BUFSIZE 512
char* SERVERIP = (char*)"127.0.0.1";
int main() {
int retval;
// ===== 윈속 초기화 =====
WSADATA wsa;
if (WSAStartup(MAKEWORD(2, 2), &wsa) != 0) return 1;
// ===== 소켓 생성 =====
SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0); // 데이터 그램 방식
if (sock == INVALID_SOCKET) printf("SOCKET() ERROR!!!\n");
// ===== 소켓 주소 구조체 초기화 =====
struct sockaddr_in serveraddr;
memset(&serveraddr, 0, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
inet_pton(AF_INET, SERVERIP, &serveraddr.sin_addr);
serveraddr.sin_port = htons(SERVERPORT);
struct sockaddr_in peeraddr; // 수신주소
int addrlen;
char buf[BUFSIZE + 1];
int len;
// 서버와 데이터 통신
while (1) {
printf("\n[보낼데이터] : ");
if (fgets(buf, BUFSIZE + 1, stdin) == NULL)
break;
// 문자열 정리
len = (int)strlen(buf);
if (buf[len - 1] = '\n')
buf[len - 1] = '\0';
if (strlen(buf) == 0)
break;
// ===== SENDTO() =====
retval = sendto(sock, buf, (int)strlen(buf), 0, (struct sockaddr*)&serveraddr, sizeof(serveraddr));
if (retval == SOCKET_ERROR) {
printf("SENDTO() ERROR!!!!\n");
break;
}
printf("[UDP Client] %d Byte Send\n", retval);
// ===== RECVFROM() =====
addrlen = sizeof(peeraddr);
retval = recvfrom(sock, buf, BUFSIZE, 0, (struct sockaddr*)&peeraddr, &addrlen);
if (retval == SOCKET_ERROR) {
printf("recv() ERROR!!!!\n");
break;
}
// 송신자 IP와 수신자 IP주소 체크 (UDP 서버-클라이언트 모델일 때만)
if (memcmp(&peeraddr, &serveraddr, sizeof(peeraddr))) {
printf("주소가 다름\n");
break;
}
// 데이터 출력
buf[retval] = '\0';
printf("[UDP Client] %d Byte Recive\n", retval);
printf("[DATA] : %s \n", buf);
}
// 소켓 닫기
closesocket(sock);
// 윈속 초기화
WSACleanup();
}
[아이콘 저작권]
디지털 아이콘 제작자: Muhammad_Usman - Flaticon
'개인 공부 > 네트워크' 카테고리의 다른 글
네트워크P-014 (소켓 옵션 & Broadcast) (1) | 2023.05.14 |
---|---|
네트워크P-012(스레드 동기화 - 이벤트) (1) | 2023.05.07 |
네트워크P-011(멀티 스레드 TCP 서버 & 임계영역) (0) | 2023.04.24 |
네트워크P-010(스레드 기다리기 & 재실행) (0) | 2023.04.21 |
네트워크P-009 (스레드 생성 & 종료) (0) | 2023.04.20 |