본문 바로가기
코스웨어/10년 시스템제어

[시스템제어]4월20일 보고서 19번 이상은

by 알 수 없는 사용자 2010. 4. 20.
728x90
반응형

-네트워크-
TCP는 연결형 프로토콜이고
UDP는 비연결형 프로토콜이다
연결과 비연결은 차이점이라고하면
연결형이라고하면 우리가 일반적으로  사용하는  전화랑 비슷하다고 보면 된다.
비연결형은 늘상 즐겨하는 게임속에서  게임관리자들이  게임안에 접속한 모든 사람에게
메세지를 전달하게 하는 것과 비슷하다. 게임안에서 사용자는 그 메세지를   오는걸 볼수 도 있지만 못볼수도  있다.
여기서  신뢰성과 비신뢰성이 온다.  이는 정보의  전달시  전송에서  오류가 나거나 목적지까지 도착을  못할경우   다시 전송하라고 메세지를 보내는경우가 TCP이며 UDP는 그것을 할수가  없다.

-nonblocking_server-
일반적으로 TCP는 정상적으로 송수신이 완료했는지의 결과를 기다려, 송수신 처리에 완료하고 나서 나머지의 처리를 행하는 방식이며 이것을 동기(blocking)이라고 한다.
하지만 UDP같은 비동기(nonblocking)같은경우는 데이터의 송수신이 완료하고 있지 않아도 가능한 처리를 진행시켜 송수신이 끝나지않으면 진행되지 않는 처리가 있는 경우에, 거기서 송수신의 완료를 기다린다. 

다음은   nonblocking  server이다.
client가 3명접속가능한 nonblocking server이다.
-----------------------------------------------------------------------------
#include "stdafx.h"

#include <winsock2.h> //windows.h보다 먼저 선언해야함
#include <windows.h>
#include <process.h>
#include <stdlib.h>
#include <iostream>



#define BUFSIZE 512  //BUFSIZE 512크기만큼 생성

unsigned int WINAPI ServerProc(LPVOID lpParam);
//서버에서의 접속관련  일을 처리한다.
unsigned int WINAPI ClientContorl(LPVOID lpParam);
//서버에서 recv()관련 일을 처리
SOCKET gsock[3];
//client sock 정보 저장(client 접속수를  저장할 배열)
int sock_count=0;
//client 접속수를 count할 변수
int main(int argc, char* argv[])
{
    DWORD dwID;
    //스레드 ID
    HANDLE h = (HANDLE)_beginthreadex(NULL, 0, ServerProc, NULL, 0, (unsigned *)&dwID); 
    //서버관련 스레드 생성
    
    WaitForSingleObject(h,INFINITE);  
    //스레드 종료를 기다림 
    
    printf("Main Thread 종료\n");
    
    CloseHandle(h);           
    //핸들을 반환
    
    return 0;
}



unsigned int WINAPI ServerProc(LPVOID lpParam)
{
    WSADATA wsa;
    //윈속  DLL초기화를 위한 변수
    int retval;
    //함수의 리턴값을 저장할  변수
    if(WSAStartup(MAKEWORD(2,2),&wsa) !=0)
    //윈속  초기화 버전  2.2(xp,98,2000등등 xp의 버전은 2,2이다 그 dll를 사용하겠다.
        return -1;
    //초기화 실패하면  프로그램종료
    
    SOCKET listen_sock = socket(AF_INET,SOCK_STREAM,0);
    //데이터  통신을 위한 소켓생성 % 소켓의 프로토콜 , 동기로 설정

    if(listen_sock == INVALID_SOCKET) 
    //listen_sock 실패시  초기화실패  메세지 출력 
        printf("소켓 초기화 실패\n");
    
    //---------------넌 블록킹 소켓으로 전환--------
    u_long on =1;  //
    retval = ioctlsocket(listen_sock,FIONBIO,&on);
  
    
/*ioctlsocket()는소켓의 입출력  모드  제어함수 
    int ioctlsocket (
        SOCKET   s,
        long   cmd,
        u_long FAR*   argp
  );
    1.SOCKET s는 작업대상 소켓이며(지금 listen_sock을 받는다.)
    
    2.long cmd 입력할 소켓s가 수행할 명령이다(FIONBIO는 argp 
    매개변수가 0이 아닐 경우 소켓 s의 넌블록킹(비동기) 모드를 활성화  
    argp 매개변수가 0일 경우는 블록킹(동기) 모드는 비활성화

    3.u_long FAR* argp command에 대한 입/출력 할 사용 값을 받음
    */

         
    if(retval==SOCKET_ERROR)
    {
        printf("소켓 속성 변경 실패\n");
    //소켓입출력 모드 제어함수가 실패시 메세지 띄우고 0 값을 반환
        return 0;
    }
    //----------------------------------------------
    
    SOCKADDR_IN serveraddr;
    //sock  setting
    ZeroMemory(&serveraddr,sizeof(serveraddr));
    //주소를 담을 변수선언  
    serveraddr.sin_family= AF_INET;
    //프로토콜 설정
    serveraddr.sin_port=htons(9000);
    //포토 설정
    serveraddr.sin_addr.s_addr=htonl(INADDR_ANY);
    //네트워크 카드 설정

   
    retval=bind(listen_sock,(SOCKADDR*)&serveraddr,sizeof(serveraddr));
    //bind 서버의 지역 IP주소와 지역 포트번호를 결정
    //(클라이언트의 접속을 수용할 소켓,이변수의 주소와 
    //지역포트 번호로 초기화 시킴,소켓주소 구조체변수의 길이)
    if(retval==SOCKET_ERROR)  //에러발생시 메세지 출력
        printf("bind error\n");
    
    //listen
    //client의 접속을 받아들일수 있는 상태로 포트 상태를 변경한다.
    retval=listen(listen_sock,SOMAXCONN);
    if(retval==SOCKET_ERROR)  //listen()에러발생시 메세지출력
        printf("listen error\n");
    
    
    SOCKET client_sock;
    //server에 접속하는 client정보를 받을 소켓 생성
    SOCKADDR_IN clientaddr;
    //접속할client 길이 생성 구조체
    int addrlen;
    //길이 받을 변수
    HANDLE hThread;
    //스레드사용할 HANDLE선언
    DWORD THreadID;
    //스레드ID생성
    
    //accept()
    addrlen = sizeof(clientaddr);
    //클라이언트에서 접근을 하면 연결을 허락해준다.
    //(소켓 서버, 클라이언트의 주소(sockaddr 타입), 클라이언트 주소의사이즈)
    
    printf("client 접속대기\n");
    
    Sleep(1000);
    
    while(sock_count<3//sock_count가 3인경우는  접속자수를 3명으로 제한하는것
    { 
        //printf("client 접속대기\n");
        client_sock = accept(listen_sock,(SOCKADDR *)&clientaddr,&addrlen);
        if(client_sock ==INVALID_SOCKET)
        { //WSAEWOULDBLOCK이란 읽을려고 하는데 첫번째 데이터가 없을때 발생이 된다.
            //즉 NonBlocking 모드에서는 자주 발생할수 밖에 없는 에러이다.
            if(WSAGetLastError()!=WSAEWOULDBLOCK)
                printf("accept error\n");
        }
        else    
        {   
            gsock[sock_count]=client_sock;  //에러가발생안할경우  client_sock을
            //gsock배열에입력
            
            printf("client 접속자 : %d 명\n",sock_count+1); //접속자수출력
            
            printf("[TCP 서버]클라이언트 접속: IP 주소=%s, 포트번호 %d\n",\
                inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port));
    //접속한 클라이언트  IP주소 포트번호 출력
            
            sock_count++;
    //  접속자수증가
            
            if(sock_count==1)  //접속자수가 1이면 clientCountorl()스레드생성 
    {
                hThread = (HANDLE)_beginthreadex(NULL, 0, ClientContorl,(LPVOID)client_sock, 0
(unsigned *)&THreadID); //스레드 생성
            }
      
        }
        
        Sleep(500);
    }
    
    if(hThread==NULL)
        printf("Client Thread Error\n");  //스레드실패시  에러출력
    
    WaitForSingleObject(hThread,INFINITE);  //스레드 종료를 기다림 
    
    printf("server thread 종료\n");
    CloseHandle(hThread);    //스레드종료
    closesocket(listen_sock);//소켓종료
    WSACleanup();          //윈속 종료
    return 0;
}



unsigned int WINAPI ClientContorl(LPVOID lpParam)//클라이언트 접속시 데이타 처리부분

    char buf[BUFSIZE+1]; 
    SOCKADDR_IN clientaddr;
    int addrlen;
    int retval;
    // client 정보 얻기
    
    
    while(1)
    {
        if(sock_count==0//접속자수가 0이면 바로 반복문종료
            break;
        //데이타 받기
        printf("데이타 수신 대기\n");  
        Sleep(500);
        
        for(int i=sock_count-1; i>=0; i--) //접속자수만큼 반복문
        {
            retval = recv(gsock[i], buf, BUFSIZE, 0 ); 
            
            if(retval == SOCKET_ERROR)  //recv가 실패할경우 에러메세지출력
            {   
                int error;
                error=WSAGetLastError(); //윈속에서 생성되는  에러를 변수  error에 입력
                
                if(error==WSAECONNRESET)  
                {  //WSAGetLastError()가 소켓종료 메시지이면 10054
                    for(int num=i; num<sock_count; num++)  
                    {   //중간에 client 나갈경우  정리할 반복문
                        
                        addrlen = sizeof( gsock[num]);  //소켓의크기입력
                        getpeername( gsock[num],(SOCKADDR *)&clientaddr,&addrlen); 
      //연결되어있는  client의 이름 인터페이스 어드레스와  포트번호얻음
                        closesocket( gsock[num]);
                        printf("ClientContorl 종료 [TCP서버]:IP 주소:%s, 포트번호=%d\n"\
                            ,inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port));
                        
                        if(num==2
                        {//마지막 client 종료시 바로 반복문 통과
                            break;
                        }
                        gsock[num] = gsock[num+1]; 
      closesocket(gsock[num+1];
      //두번째 client종료시 세번째를 앞으로이동후 소켓종료
                    }
                    
                    sock_count--;
        //전체갯수를  하나 줄임 
                    break;
                }
                else if(error!=WSAEWOULDBLOCK) //recv버퍼에 데이터가 없다고 에러발생 
                {     //하지만 동기에서 버퍼에 데이터가 없다면  recv로 기다린다  
                    printf("데이타 받기 실패!");
                    break;
                }
                
            }
            else if(retval ==0//enter 입력시 종료
            {
                break;
            }
            else
            {
                //데이터 출력
                buf[retval]='\0';
    //저장된 buf의 마지막값에  \0을 넘음으로써  출력시 에러방지 
                printf("[TCP/%s:%d] %s\n",inet_ntoa(clientaddr.sin_addr),ntohs        
(clientaddr.sin_port),buf); //출력
               
                //데이터 보내기
                retval = send(gsock[i], buf, retval, 0);
                if(retval==SOCKET_ERROR)  //recv error발생시 메세지출력 빠져나옴
                {
                    printf("recv error!\n");
                    break;
                }
            }
        }
    }
    
    printf("server 종료....\n"); //종료메세지출력
    
    return 0;
}
------------------------------------------------------------------------------

-ATMEGA-
단방향직렬통신프로그램

#include<stdio.h>
#include<windows.h>


int main()
{
  char szPort[15];  //포트명을 저장할 변수
    
  wsprintf(szPort,"COM%d",2); //포트 2번으로 통신

  HANDLE  m_hComn =NULL;  //HANDLE생성후   초기값 NULL
  m_hComn = CreateFile(szPort,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
  //Createfile()통신포트열기          

  if(m_hComn==INVALID_HANDLE_VALUE)  //포트접속실패시 에러
  {
    printf("(!) Failed to create a  Comm Device file \n");
    return FALSE;
  }

  DCB dcb;        //DCB 구조체생성
  dcb.DCBlength=sizeof(DCB);    //포트의 크기 비트레이 바이트크기 등을
  GetCommState(m_hComn,&dcb);    //통신의기본설정상태읽기  
  dcb.BaudRate=9600;      
  dcb.ByteSize=8;
  dcb.Parity=0;
  dcb.StopBits=0;

  SetCommState(m_hComn,&dcb);    //통신상태,속도읽어오기
  OVERLAPPED  osWrite;      //송신용 객체 생성
  osWrite.Offset=0;
  osWrite.OffsetHigh=0;
  osWrite.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL); //통신객체 이벤트읽어오기
  char buf[100]="Hello world  \r\n";
  while(1)
  {
    printf("send:%s",buf);
    WriteFile(m_hComn,buf,strlen(buf),NULL,&osWrite);  //데이터전송
    Sleep(1000);            
  }
  CloseHandle(m_hComn);  //통신포트닫기

  return 0;
}
-결과값-




직렬통신의 절차로 그림과같이
6단계로 이루어진다.
1.CreateFile() 통신포트열기
2.GetCommState() 통신의기본상태읽기
3.SetCommState()  통신의상태와  속도
4.CreateEvent() 통신할객체이벤트읽기
5.WrtieFile(),ReadFile() 데이터전송,수신
(여기서 단방향외 양방향일경우 ReadFile이 필요하다)
6.CloseHandle()  통신포트닫기

-양방향직렬통신-
위에서본 단방향일경우 한쪽에서만  Write만 가능하다 그리고 받을려고하는 Read부분이 없다.
그래서   프로그램실행시 서로 Write Read하게  만들어본다.


양방향일경우 단방향과 틀리게 WritFile부분과  ReadFile()부분이 있다.
OVERLAPPED  osRead;
osRead.Offset=0;
osRead.OffsetHigh=0;
osRead.hEvent=CreateEvent(NULL,TRUE,FALSE,NULL); 
while(1)
{
  WriteFile(m_hComn,buf,strlen(buf),NULL,&osWrite);
  printf("send:%s",buf);
  ReadFile(m_hComn,buf,100,NULL,&osRead);
  printf("recv:%s",buf);
}

기본적으로 osRead로 ReadFile()위한 오브젝트 를 생성하는것과
우리가 통신하는  while(1)부분에서  WriteFile부분과 ReadFile 부분으로
전송하고 수신하고를 해야한다.
단... 상대쪽이 전송하기전에는  계속 수신한다.
 
다음 결과값이다.


숫자를  fgets를   통해 읽고  전송후 하나씩 증가시키는  경우 scanf를통해 입력한다.
while(1)
{

  WriteFile(m_hComn,&inum,4,NULL,&osWrite);
  ReadFile(m_hComn,&inum,4,NULL,&osRead);
  printf("recv:%d\n",inum);
  Sleep(1000);
}

while
(1)
{
  ReadFile(m_hComn,&inum,4,NULL,&osRead);
  printf("recv:%d\n",inum);
  inum++;
  WriteFile(m_hComn,&inum,4,NULL,&osWrite);
  Sleep(1000);
}
-결과값-


다음 양방향 통신을 통해서  채팅비슷하게 되게 했다.  무엇보다 문자열을 주고받는경우이다.
그리고 여기서  중요한점은 상대편이 send하기전에는 반대쪽에서send할수 없다.

char  buf[100]={0,};  //Read()용 버퍼 초기화
char buf2[100]={0,}; //Write()용 버퍼 초기화

while(1)
{
  fgets(buf2,100,stdin);//buf2를 크기만큼입력
  WriteFile(m_hComn,buf2,strlen(buf2),NULL,&osWrite);
//  printf("send:%s\n",buf);
  ReadFile(m_hComn,buf,100,NULL,&osRead);
  printf("recv:%s\n",buf);
  for(int inum=0;inum<100;++inum)//받은buf에 NULL넣어초기화
  {
    buf[inum]=NULL;
  }
  Sleep(1000);
}
------------------------------------------------
char buf[100]={0,}; //Read()용버퍼 초기화
char buf2[100]={0,};//Write()용버퍼 초기화
while(1)
{

  ReadFile(m_hComn,buf,100,NULL,&osRead);
  printf("recv:%s\n",buf);
  for(int inum=0;inum<100;++inum)//받은buf에 NULL넣어초기화
  {
  buf[inum]=NULL;
  }
  fgets(buf2,100,stdin);//buf2를 크기만큼입력
  WriteFile(m_hComn,buf2,strlen(buf2),NULL,&osWrite);
//  printf("send:%s\n",buf);
  Sleep(1000);
}

결과값




-DK128 직렬통신-


직렬통신을 사용하기위해서 PORT D를 사용하는데  그중 27번째와 28번째를 입출력이 아닌 직렬통신연결로 송수신으로 사용한다.
두가지  USART를 제공한 USART0와 USART1을 사용한다. 하지만DK128 USART1만 사용하도록 제작되었다.
그리고  USART1에서 5가지의 Register가  제공된다.
1. UDR1
2. UCSR1A
3. UCSR1B
4. UCSR1C
5. UBRR1H/L

보다 시피 TxD 와RxD로 송신수신을 받고  위그림에서 RxD는 27번째 Txd는 28번째를 사용한다.
5. UBRR1H/L

일반적으로 우리는 비동기를 사용하려고하고 그리고  저도표에 보이는  UBRR대한 공식은 전송시 Error율을 줄이기위해서 저 공식이 나온다 MCU의 속도에 /16*전송속도 -1 로 인해서  나온다. (자세한 내용은 ATMEGA 메뉴얼 198쪽을 통해 알수있다.)
1. UDR1

보다 시피 UDRn의 송신과 수신버퍼가 따로되어있다. 

2.UCSR1A


UCSR1A Register는 5비트는 송신버퍼 상태를  알수 있고
7비트는 수신버퍼 상태를 알수 있다.
만약 송신버퍼에 어떤값이 들어가있을경우 저  5번째비트는 0이고
버퍼가 비어있다면 1이된다
그리고 송신버퍼의 값에 따라서 7번째 비트의  값이 0과 1로 변한다.

3. UCSR1B

우리가 PORTD의 27번과 28번을  직렬포트사용하려는 설정은 여기 UCSR1B에서 설정할수가 있다 바로  저 4번비트와  3번비트에서 1값을 넣을경우 27번과 28번을  직렬포트로  사용할수 있다.

3. UCSR1C


UCSR1C경우는 통신상태등 정보를  설정할수있다.
6번비트는 동기와 비동기를 설정할수있다.

보다시피 비동기를 사용하기위해서 저자리에 0을 넣어야한다.

5번비트와 4번비트는 Parity Mode를 설정할수 있다.
하지만 우린 사용하지 않기 때문에

두자리에 0을 넣어서  설정한다.

3번비트 stop bit설정이다

1비트이므로 0을 넣어  설정하면된다.

그리고  2번비트와 1번비트는
문자사이즈 설정이다


우리는 문자사이즈를 8bit로 사용한다 그래서 UCSZn2 UCSZn1 UCSZn0 에 각각 0 1 1 를 넣어야한다  하지만  UCSZn1 UCSZn0 같은경우 UCSR1C에 각각 2번  1번 비트에 있다 하지만 UCSZn2같은경운 UCSR1B에 2번 비트에 있기때문에 유의있게 봐야한다.

다음은  연결시 Hello  DK128!을 찍는것이다
#include<avr/io.h> // IO 제어  헤더 파일
#define CPU_CLOCK 16000000  //MCU의 속도
#define BAUD_RATE 19200    //통신시 이용할 속도
#define BAUD_RATE_L (CPU_CLOCK/(16l*BAUD_RATE))-1 
#define BAUD_RATE_H ((CPU_CLOCK/(16l*BAUD_RATE))-1)>>8
//통신속도의 결과 값을 입력하기 위해   상하위 비트로 구분
//161은 16+L이며, 연산 시  값이  너무 커져 overflow가 발생하므로
//32비트 연산을  위해 16에 Long을 의미하는 l을 붙인다.

void uart_send_byte(unsigned char byte)//1byte 전송 함수
{
  while(!(UCSR1A&(1<<UDRE)));// 전송 버퍼가 빌 때 까지 기다린다.
  UDR1=byte ; //문자 1개를 전송한다.
}
int main()
{
  unsigned char buf[]="Hello DK128!"//전송할 문자열
  unsigned int i;    //반복 제어 변수
  UBRR1L = (unsigned char)BAUD_RATE_L; //baud rate 설정
  UBRR1H =(unsigned  char)BAUD_RATE_H;
  //parity설정 1stop bit설정,문자사이즈 8bit 설정
  UCSR1C=(0<<UPM1)|(0<<UPM0)|(0<<USBS)|
    (1<<UCSZ1)|(1<<UCSZ0);
  //송신수신 interrupt설정(PORTD27번28번사용), 문자사이즈 8bit설정
  UCSR1B=(1<<TXEN)|(1<<RXEN)|(0<<UCSZ2);
  
  for(i=0;i<sizeof(buf);++i)//문자단위로 전체 문자열 전송
  {
    uart_send_byte(buf[i]);//문자 1개를직렬포트로 전송
  }
  return 0;
}

728x90