728x90
rdt2.2 over UDP 구현
전송계층 프로토콜은 UDP를 사용하되 응용 프로그램 상에서 rdt2.2 프로토콜을 구현
client.c는 총 3개의 패킷을 보내며 각각 다음의 상황을 재현
1. 정상적인 패킷
2. [Our-of-Order] 순서가 뒤바뀐 패킷 (임의로 순서를 바꿔서 패킷을 보낸다.)
3. [Bit error] 비트에러가 발생한 패킷 (임의로 데이터를 변조하여 비트에러를 발생시켜서 패킷을 보낸다.)
rdt2.2를 기반으로 작동하는 서버 프로그램은 이 세가지 상황 모두에 대해 잘 대처할 수 있어야 합니다. Out of-order와 bit error의 경우는 ack을 통해 재전송을 요청하고, client를 이를 통해 패킷을 재전송
⚫ 이 때 반드시 탐지한 오류가 무엇인지, seq 번호는 무엇인지 출력해야 한다
구현코드
client.c
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#define OUT_OF_ORDER 2
#define BIT_ERROR 3
int hash(int value) { // 체크섬 계산용 해시함수.
int j;
int byte, crc, mask;
crc = 0xFFFFFFFF;
for (int i = 0; i < 4; i++) {
byte = (value >> (i * 8)) & 0xFF;
crc = crc ^ byte;
for (j = 7; j >= 0; j--) {
mask = -(crc & 1);
crc = (crc >> 1) ^ (0xEDB88320 & mask);
}
}
return ~crc;
}
#pragma pack(1)
struct my_hdr{ // 데이터 전송용 구조체
int number;
int seq;
int checksum;
} __attribute__((packed));
int main(int argc, char *argv[]) {
if ( argc < 2 ){
printf("Input : %s port number\n", argv[0]);
return 1;
}
int SERVER_PORT = atoi(argv[1]);
/* localhost에서 통신할 것이므로 서버 ip주소도 그냥 localhost */
const char* server_name = "localhost"; // 127.0.0.1
struct sockaddr_in srv_addr; // Create socket structure
memset(&srv_addr, 0, sizeof(srv_addr)); // Initialize memory space with zeros
srv_addr.sin_family = AF_INET; // IPv4
srv_addr.sin_port = htons(SERVER_PORT);
inet_pton(AF_INET, server_name, &srv_addr.sin_addr); // Convert IP addr. to binary
int sock;
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
printf("Could not create socket\n");
exit(1);
}
int n = 0;
struct my_hdr SendBuffer={0,}; // 메시지 송신용 버퍼. my_hdr 구조체 형태로 만듬.
struct my_hdr RecvBuffer; // 메시지 수신용 버퍼.
struct sockaddr_in cli_addr;
int cli_addr_len = sizeof(cli_addr);
int wait_for_ack;
int expected_ack_number =0;
SendBuffer.seq = 0; // Seqeuence number 초기화
for(int i=1;i<4;i++){ // 총 3개의 패킷을 테스트케이스로 보냄
SendBuffer.number = i; // 데이터는 그냥 편의상 i로..
SendBuffer.checksum = hash(SendBuffer.number); // 32비트 해시함수로 Checksum 계산
if(i==OUT_OF_ORDER){ // 두번째 패킷의 경우 임의로 Out-of-order를 발생시킴
SendBuffer.seq = 1 - SendBuffer.seq; // 순서 번호를 반전시켜서 out-of-order 상황을 만듬.
printf("[Out-of-Order] ");
}
if(i==BIT_ERROR){ // 세번째 패킷의 경우 임의로 Bit error를 발생시킴
SendBuffer.number ^= 4; // 네번째 비트를 반전시켜서 비트에러를 발생시킴
printf("[Bit error] ");
}
if(i!=OUT_OF_ORDER) expected_ack_number = SendBuffer.seq; // 받기를 기대하는 ack번호와 seq 번호일치
else expected_ack_number = 1 - expected_ack_number;//그게 아니라면 기대하는 ack 번호를 반전시킴
printf("Sent msg with %d, Expect ACK for %d\n" ,SendBuffer.seq,expected_ack_number);
wait_for_ack = 1; // 전송 끝내고 ACK을 기다리는 state로 넘어감
sendto(sock, &SendBuffer, sizeof(SendBuffer), 0, (struct sockaddr *)&srv_addr, sizeof(srv_addr));//rdt_send(data) 패킷 0 전송
while(wait_for_ack){ // Stop-and-wait 프로토콜이므로 ACK이 올떄까지 반복함.
n = recvfrom(sock, &RecvBuffer, sizeof(RecvBuffer), 0, (struct sockaddr *)&cli_addr, &cli_addr_len);//ack을 받는다
if (n > 0) {
if(expected_ack_number == RecvBuffer.seq){ //기대하는 ack번호와 seq 번호 일치하는 경우
printf("Received ACK for %d\n", RecvBuffer.seq);
SendBuffer.seq = 1 - SendBuffer.seq; // 순서 번호 토글
wait_for_ack = 0; // wait state를 끝내고 다음 메시지 전송 준비
}
else{ // Seq 번호 불일치하는 경우 (bir error 거나 Out-of-order거나)
printf("Expected ACK for %d, but Received ACK for %d. Retransmit! \n", expected_ack_number,RecvBuffer.seq);
if(i==OUT_OF_ORDER) SendBuffer.seq = 1 - SendBuffer.seq; // 정상적인 seq를 다시 넣어줌.
if(i==BIT_ERROR) SendBuffer.number = i; // 정상적인 number를 다시 넣어줌.
SendBuffer.checksum = hash(SendBuffer.number); // 해시값을 다시 넣어줌.
sendto(sock, &SendBuffer, sizeof(SendBuffer), 0, (struct sockaddr *)&srv_addr, sizeof(srv_addr));//패킷 재전송
expected_ack_number = SendBuffer.seq;//다시 기대되는 ack number를 정상적인 seq넘버로
printf("Sent msg with %d, Expect ACK for %d\n", SendBuffer.seq,expected_ack_number);
}
}
}
}
close(sock);
return 0;
}
server.c
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
int hash(int value) { // 체크섬 계산용 해시함수.
int j;
int byte, crc, mask;
crc = 0xFFFFFFFF;
for (int i = 0; i < 4; i++) {
byte = (value >> (i * 8)) & 0xFF;
crc = crc ^ byte;
for (j = 7; j >= 0; j--) {
mask = -(crc & 1);
crc = (crc >> 1) ^ (0xEDB88320 & mask);
}
}
return ~crc;
}
#pragma pack(1)
struct my_hdr{ // 데이터 전송용 구조체
int number;
int seq;
int checksum;
} __attribute__((packed));
int main(int argc, char *argv[]) {
if ( argc < 2 ){
printf("Input : %s port number\n", argv[0]);
return 1;
}
int SERVER_PORT = atoi(argv[1]);
struct sockaddr_in srv_addr;
memset(&srv_addr, 0, sizeof(srv_addr));
srv_addr.sin_family = AF_INET;
srv_addr.sin_port = htons(SERVER_PORT);
srv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
int sock;
if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
printf("Could not create listen socket\n");
exit(1);
}
if ((bind(sock, (struct sockaddr *)&srv_addr, sizeof(srv_addr))) < 0) {
printf("Could not bind socket\n");
exit(1);
}
struct sockaddr_in cli_addr;
int cli_addr_len = sizeof(cli_addr);
struct my_hdr RecvBuffer; // 메시지 송수신용 버퍼.
int expected_seq_number = 0; // 예상되는 순서 번호 초기화
int n =0;
while (1) {
n = recvfrom(sock, &RecvBuffer, sizeof(RecvBuffer), 0, (struct sockaddr *)&cli_addr, &cli_addr_len);//패킷 0을 받음
if (n > 0) {
/*이 부분을 수정*/
if (RecvBuffer.checksum == hash(RecvBuffer.number)) {
if (expected_seq_number == RecvBuffer.seq) { // 기대하는 seq 번호가 받은 seq번호와 일치하는 경우
printf("Received msg w/ SEQ: %d\n", RecvBuffer.seq);
printf("ACK for SEQ% d sent\n", RecvBuffer.seq);;//seq번호가 일치하는 경우 ack번호도 일치하게 보내줘야함
sendto(sock, &RecvBuffer, sizeof(RecvBuffer), 0, (struct sockaddr*)&cli_addr, sizeof(cli_addr));//ack을전송
expected_seq_number = 1 - RecvBuffer.seq;//기대되는 seq넘버 (1다음 0 0다음 1)
}
else { // seq 번호 불일치하는 경우
//체크섬 값이 일치할 때 (bit error가 일어나지 않을 때)
printf("[Out-of-Order] Expect %d but received msg w/ SEQ: %d\n", expected_seq_number, RecvBuffer.seq);
printf("ACK for SEQ %d sent\n", RecvBuffer.seq);//받은 그대로의 패킷 seq를 ack으로 전송
sendto(sock, &RecvBuffer, sizeof(RecvBuffer), 0, (struct sockaddr*)&cli_addr, sizeof(cli_addr));//클라이언트로 ack 재전송
}
}
else {
printf("[Bit error] Received msg w/ SEQ: %d\n", RecvBuffer.seq);
printf("ACK for SEQ %d sent\n", 1 - RecvBuffer.seq);
RecvBuffer.seq = 1 - RecvBuffer.seq;
sendto(sock, &RecvBuffer, sizeof(RecvBuffer), 0, (struct sockaddr*)&cli_addr, sizeof(cli_addr));//ack 재전송
}
}
}
close(sock);
return 0;
}
☑️실행 결과

client 실행

server 실행
728x90
'💾 lecture > 컴퓨터 네트워크' 카테고리의 다른 글
컴퓨터 네트워크 과제 echo software router 구현 (0) | 2024.06.05 |
---|---|
컴퓨터 네트워크 과제 UDP Echo server 구현 (0) | 2024.06.05 |
[NetWork] 네트워크 정리 (3) (0) | 2023.10.28 |
[NetWork] 네트워크 정리(2) (0) | 2023.10.28 |
[NetWork] 네트워크 정리 (1) (0) | 2023.10.27 |