본문 바로가기

Ethernet Chat Server/5.링버퍼 연결 종료 코드 추가

채팅 프로그램, 링버퍼 종료 코드 추가(최종)

안녕하세요.
Edward입니다.

최종 채팅 프로그램 소스입니다.

저번 Source Code에 비해 추가된 점은 Disconnect입니다.
TCP에서 Server와 client와 연결을 끊는 것은 이론적으로 4-Handshake way 라고 칭합니다.

4way_handshake

그림의 4 way handshake 는 아래와 같은 방법으로 이루어진다.
최초에는 서로 통신 상태이기 때문에 양쪽이 ESTABLISHED 상태이다.

1. 통신을 종료하고자 하는 Client가 서버에게 FIN 패킷을 보내고 자신은 FIN_WAIT_1 상태로 대기한다.

2. FIN 패킷을 받은 서버는 해당 포트를 CLOSE_WAIT으로 바꾸고 잘 받았다는 ACK 를 Client에게 전하고 ACK를 받은 Client는 상태를 FIN_WAIT_2로 변경한다.
그와 동시에 Server에서는 해당 포트에 연결되어 있는 Application에게 Close()를 요청한다.

3. Close() 요청을 받은 Application은 종료 프로세스를 진행시켜 최종적으로 close()가 되고 server는 FIN 패킷을 Client에게 전송 후 자신은 LAST_ACK 로 상태를 바꾼다.

4. FIN_WAIT_2 에서 Server가 연결을 종료했다는 신호를 기다리다가 FIN 을 받으면 받았다는 ACK를 Server에 전송하고 자신은 TIME_WAIT 으로 상태를 바꾼다. (TIME_WAIT 에서 일정 시간이 지나면 CLOSED 되게 된다.)
최종 ACK를 받은 서버는 자신의 포트도 CLOSED로 닫게 된다.

여기서 주요 포인트는,

1. Client가 FIN Packet을 보내면, Server가 받고 ACK를 client로 보냄.
: client가 FIN Packet을 보냈다면 client가 server로 보낼 data는 이제 없다는 상황.
하지만 아예 종료된 것이 아님. Data를 받을 수는 있음.
2. 그러면 client는 '일정 대기 상태' 돌입.
: 하지만, Server는 아직 전송할 data가 남아있기 때문에 Server는 아직 Disconnect이 아님.
: Server도 FIN Packet을 보내야만 Disconnect가 성립된다.
3. Server는 client로 FIN Packet을 보냄.
: 이 때, Server는 현재 버퍼에 저장되어 있는 Data를 전부 Client로 보낸 뒤 종료에 들어감.
4. Client는 FIN Packet을 받았으면 ACK를 Server로 보내주고 Close하게 된다.

위 부분이 주요 포인트라고 생각하시면 됩니다.

그러면 위의 이론설명을 코드로 구현한 부분을 설명드리자면 채팅을 하다가 '@' 라는 키워드가 나오게 되면 서로의 접속을 끊게 됩니다. 근데 그냥 무작정 끊는 것이 아니라, 현재 버퍼에 저장되어 있는 Data는 전부 보낸뒤에 Disconnect가 성립되게 됩니다.

[code language="c"]

/*
===============================================================================
 Name : W5500-EVB.c
 Author : $(author)
 Version :
 Copyright : $(copyright)
 Description : main definition
===============================================================================
*/

#if defined (__USE_LPCOPEN)
#if defined(NO_BOARD_LIB)
#include <chip.h>
#else
#include <board.h>
#endif
#endif

#include "cr_section_macros.h"
#include "socket.h"
#include"spi_handler.h"
#include "w5500_init.h"

// TODO: insert other include files here

// TODO: insert other definitions and declarations here
#define SOCK_TCPS0 0
#define DATA_BUF_SIZE 2048
#define SERIAL_BUF_SIZE 16
uint8_t gDATABUF[DATA_BUF_SIZE];
uint8_t SERIAL_BUF[SERIAL_BUF_SIZE];

typedef struct { // creation ring buffer
	int tail; // read data pointer
	int head; // write data pointer
	char data[SERIAL_BUF_SIZE]; // receive buffer
} t_rb;

// pointer initial to 0 set , pointer end to SERIAL_BUF_SIZE

t_rb ringbuffer = {0, 0, }; // tail = 0, head = 0
uint8_t AutoSend_flag=0; // global variable

int rb_put( t_rb *rb , char d ) // push -> read UART
{
	int nhead = (rb->head+1) % (SERIAL_BUF_SIZE);
	if(rb->tail == nhead) // Full
	{
		return 0; // erase last data. so, how change?
	}
	rb->data[rb->head] = d;
	rb->head = nhead;
	return 1;
}

/*push -> serial data write , pop -> read the data to send*/
char rb_get( t_rb *rb, int *err) // pop -> send Ethernet
{
	char d;
	int ntail = (rb->tail+1) % (SERIAL_BUF_SIZE); //rest
	if(rb->head == rb->tail) // empty
	{
		*err = 0;
		return 0; // buffer non-write -> empty
	}
	d = rb->data[rb->tail];
	rb->tail = ntail;
	*err = 1;
	return d; // buffer read
}

int finder ( t_rb *rb, char ch ) // new line finder
{
	int i;
	int cnt;

	if(rb->head == rb->tail) // Empty
	{
		return 0;
	}
	else if(rb->head > rb->tail){
		for(i=rb->tail; i<rb->head; i++){
			if(rb->data[i] == ch){
				return (i+1);
			}
		}
	}
	else{
		for(i=rb->tail; i < SERIAL_BUF_SIZE; i++){
			if(rb->data[i] == ch){
				return (i+1);
			}
		}
		cnt = i;
		for(i=0; i<rb->head; i++){
			if(rb->data[i] == ch){
				return (cnt+i+1);
			}
		}
	}
	return 0;
}

int EtherToSerial(uint8_t* buf,uint16_t size)
{
	uint8_t i;
	for(i=0 ; i < size ; i++){
		putchar(buf[i]);
	}
	return 0 ;
}

int SerialToEther(uint8_t sn)
{
	int len = finder(&ringbuffer, 0x0a);
	int err;
	int i;

	if(len > 0)
	{
		for(i=0; i<len; i++){
			SERIAL_BUF[i] = rb_get(&ringbuffer, &err);
		}
		send(sn, SERIAL_BUF, len);
		return len; // data send to len
	}
	else if(AutoSend_flag == 1)
	{
		for(i=0; i<SERIAL_BUF_SIZE; i++){
			SERIAL_BUF[i] = rb_get(&ringbuffer, &err);
		}
		send(sn, SERIAL_BUF, SERIAL_BUF_SIZE);
		AutoSend_flag = 0;
		return SERIAL_BUF_SIZE; // data send to max size
		}
////////////////////////////////// Adder //////////////////////////////////////////
	else{ // disconnect to finding word '@'
		int len2 = finder(&ringbuffer, '@'); // find '@'
		if(len2 > 0){
			for(i=0; i<len2; i++){
				SERIAL_BUF[i] = rb_get(&ringbuffer, &err); // send to buffer to result
			}
			send(sn, SERIAL_BUF, len2); // send data
			disconnect(sn); // disconnect
			return len2;
		}
	}
////////////////////////////////////////////////////////////////////////////////////
	return 0;
}


void UART_IRQHandler(void)
{
	if((Chip_UART_ReadLineStatus(LPC_USART) & UART_LSR_RDR) == 1){
		if(rb_put(&ringbuffer, Chip_UART_ReadByte(LPC_USART)) == 1){
			//printf("Return1 data output%s");
		}
		else{
			AutoSend_flag = 1;
			//printf("Return0 data no output%s");
		}
	}
}

int main(void) {

	//uint8_t ip[4]={192,168,0,45};
	uint16_t port=5000;
	uint8_t sn=SOCK_TCPS0;
	uint16_t size=0;

	#if defined (__USE_LPCOPEN)
	#if !defined(NO_BOARD_LIB)
	// Read clock settings and update SystemCoreClock variable
	SystemCoreClockUpdate();
	// Set up and initialize all required blocks and
	// functions related to the board hardware
	Board_Init();
	// Set the LED to the state of On
	Board_LED_Set(0, true);
	#endif
	#endif
	SPI_Init();
	W5500_Init();
	Net_Conf();

	Chip_UART_IntEnable(LPC_USART, (UART_IER_RBRINT | UART_IER_RLSINT)); //uart enable

	/* Enable UART 0 interrupt */
	NVIC_EnableIRQ(UART0_IRQn);



	//printf("%d:Connecting, ip[%d.%d.%d.%d] port [%d]\r\n", sn, ip[0], ip[1], ip[2], ip[3], port);

	while(1) {
		switch(getSn_SR(sn)) { // Sn_SR(sn) check if) listen state? goto ESTABLISHED
		case SOCK_CLOSED:
			if(socket(sn, Sn_MR_TCP, port, 0x00)==sn){ // socket create
				printf("%d:Socket Opened\r\n",sn);
			}
			else{
				printf("%d:Socket Error\r\n",sn);
				while(1); // end to error
			}

			break;
		case SOCK_INIT:
			//printf("%d:Connecting, ip[%d.%d.%d.%d] port [%d]\r\", sn, ip[0], ip[1], ip[2], ip[3], port);
			/*
 	 	 	 if(connect(sn,ip,port)==SOCK_OK);
 	 	 	 else {
 	 	 	 	 printf("%d:Connect Error\r\n",sn);
 	 	 	 	 while(1);
 	 	 	 	 }
			*/
			if(listen(sn) == SOCK_OK){ // listen state
				printf("%d:listen\r\n",sn);
			}
			else{
				printf("%d:listen error\r\n",sn);
				while(1);
			}

			break;
		case SOCK_LISTEN:
			/* TCP Server Mode */

			break;
		case SOCK_ESTABLISHED:
			/* TCP ESTABLISHED */
			//Connect Interrupt Check
			if(getSn_IR(sn) & Sn_IR_CON) {
				printf("%d:Connected\r\n",sn);
				setSn_IR(sn,Sn_IR_CON);
				Board_LED_Set(0, false);
				Board_LED_Set(1, true);
			}
			//Receive Data Check
			if((size = getSn_RX_RSR(sn)) > 0) {
				if(size > DATA_BUF_SIZE) size = DATA_BUF_SIZE;
				recv(sn,gDATABUF,size);
				EtherToSerial(gDATABUF, size);
			}
			SerialToEther(sn);
			break;

		case SOCK_CLOSE_WAIT:
			if(disconnect(sn) == SOCK_OK){
				printf("%d:disconnect OK\r\n",sn);
			}
			else{
				printf("%d:diconnect Error\r\n",sn);
			}

			break;
		}
		//NVIC_DisableIRQ(UART0_IRQn);
		//Chip_UART_DeInit(LPC_USART);
	}
	return 0 ;
}

[/code]

여기까지 채팅프로그램이었습니다.

궁금하신부분이나 제가 틀린점이 있으면 댓글 달아주세요.

수정하겠습니다.