Super Kawaii Cute Cat Kaoani
본문 바로가기
💾 lecture/컴퓨터 네트워크

컴퓨터 네트워크 과제 rdt2.2 over UDP 구현 과제

by wonee1 2024. 6. 5.
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