카테고리 보관물: (3-2) W5500-EVB Chat_Adder_Ringbuffer_disconnect

(3-2) W5500-EVB CHAT_ADDER_RINGBUFFER_DISCONNECT

안녕하세요.
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가 성립되게 됩니다.


/*
===============================================================================
 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 ;
}

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

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

수정하겠습니다.