2010년/4월

4월19일 network(nagle알골/넌블로킹client)/Atmaga(C코딩,컴직렬통신)

뽀얀햄스터 2010. 4. 19. 12:28

-네트워크-
동기와 비동기의 차이점
간단하게   동기는 신뢰성이 있고
비동기는  유연성이 있다.
하지만  비동기 데이터를  전송할때   확실하게 상대방에서  보낼때는  정확성이 떨어진다.
또  동기 같은경우는  데이터를 전송할때 만약 send부분이 많다면  보낼때가지 기다려야한다.
비동기같은경우  채팅을  예를  든다면  host에서 볼경우   누가  send했는지 모른다. 그래서  이것이 뒤죽박죽이 된다.
동기에서는   데이터를 보내고  받는곳에서  데이터를   받았다고  ack를 날려준다.

-nagle알고리즘-

 Nagle 알고리즘?
1. 네트워크상의 패킷 수를 줄이기 위해 제안된 알고리즘
2. ACK 를 수신해야만 다음 전송을 진행하는 알고리즘

위 그림만 봐도...
Nagle 알고리즘 ON 의 경우에는 패킷수가 훨씬 작은 것을 알 수 있습니다.
N 을 전송하고 ACK 메세지를 받은 다음에 다음 메세지를 전송합니다.

#include "stdafx.h"

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



#define BUFSIZE 512

unsigned int WINAPI ServerProc(LPVOID lpParam);
unsigned int WINAPI ClientContorl(LPVOID lpParam);
SOCKET gsock[3];  //client  sock  정보 저장
int  sock_count;

  //갯수 


int main(int argc, CHAR* argv[])

  DWORD dwID;
  
  HANDLE h = (HANDLE)_beginthreadex(NULL, 0, ServerProc, NULL, 0, (unsigned *)&dwID); //스레드 생성
  
  WaitForSingleObject(h,INFINITE);  //스레드 종료를 기다림 
  
  printf("Main Thread 종료\n");
  
  CloseHandle(h);           //핸들을 반환
  
  return 0;
}

// 링커->입력->추가종속성에 ws2_32.lib를 추가 해주어야 한다.

unsigned int WINAPI ServerProc(LPVOID lpParam)
{
  WSADATA wsa;
  int retval;
  //윈속 초기화
  if(WSAStartup(MAKEWORD(2,2),&wsa) !=0)
    return -1;
  
  SOCKET listen_sock = socket(AF_INET,SOCK_STREAM,0);
  
  if(listen_sock == INVALID_SOCKET) 
    printf("소켓 초기화 실패\n");
  
  //---------------넌 블록킹 소켓으로 전환--------
  u_long on =1;
  retval = ioctlsocket(listen_sock,FIONBIO,&on);
  if(retval==SOCKET_ERROR)
  {
    printf("소켓 속성 변경 실패\n");
    return 0;
  }
  //----------------------------------------------
  
  SOCKADDR_IN serveraddr;
  
  ZeroMemory(&serveraddr,sizeof(serveraddr));
  serveraddr.sin_family= AF_INET;
  serveraddr.sin_port=htons(9000);
  serveraddr.sin_addr.s_addr=htonl(INADDR_ANY);
  
  //bind 서버의 지역 IP주소와 지역 포트번호를 결정
  //(클라이언트의 접속을 수용할 소켓,이변수늬 주소와 지역포트 번호로 초기화 시킴,소켓주소 구조체변수의 길이)
  retval=bind(listen_sock,(SOCKADDR*)&serveraddr,sizeof(serveraddr));
  if(retval==SOCKET_ERROR)
    printf("bind error\n");
  
  //listen
  //client의 접속을 받아들일수 있는 상태로 포트 상태를 변경한다.
  retval=listen(listen_sock,SOMAXCONN);
  if(retval==SOCKET_ERROR)
    printf("listen error\n");
  
  
  SOCKET client_sock;
  SOCKADDR_IN clientaddr;
  int addrlen;
  HANDLE hThread;
  DWORD THreadID;
  
  //accept()
  addrlen = sizeof(clientaddr);
  //클라이언트에서 접근을 하면 연결을 허락해준다.
  //(소켓 서버, 클라이언트의 주소(sockaddr 타입), 클라이언트 주소의사이즈)
  
  //client_sock==INVALID_SOCKET
  while(sock_count<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
    {
      if(sock_count>=0)
      {
        hThread = (HANDLE)_beginthreadex(NULL, 0, ClientContorl,(LPVOID)client_sock, 0, (unsigned *)&THreadID); //스레드 생성
        
      }
      gsock[sock_count]=client_sock;
      printf("[TCP 서버]클라이언트 접속: IP 주소=%s, 포트번호 %d\n",\
        inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port));
      printf("client 접속자 %d\n",sock_count+1);
      sock_count++;
    
    }
    
    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)//클라이언트 접속시 데이타 처리부분

  //  SOCKET client_sock = (SOCKET)lpParam;
  char buf[BUFSIZE+1];
  SOCKADDR_IN clientaddr;
  int addrlen;
  int retval;
  
  // client 정보 얻기
  //  addrlen = sizeof(clientaddr);
  //  getpeername(client_sock,(SOCKADDR *)&clientaddr,&addrlen);
  
  while(1)
  {
    //데이타 받기
    //  printf(" 수신 대기\n");
    Sleep(500);
    int a=0;
    for(int  i=0;i<sock_count;++i)
    {
      
      retval = recv(gsock[i], buf, BUFSIZE, 0 );
      if(retval == SOCKET_ERROR)
      {
      
        if(WSAGetLastError()!=WSAEWOULDBLOCK)
        {
        //  printf("데이타 받기 실패!");
        //  break;
        a++;
        }
        
      

      }      
      else if(retval==0)
      {
        for(int num=i;num<sock_count;num++)
        {
          addrlen = sizeof(gsock[num]);
          getpeername(gsock[num],(SOCKADDR *)&clientaddr,&addrlen);
          closesocket(gsock[num]);
          printf("ClientContorl 종료 [TCP서버]:IP 주소:%s, 포트번호=%d\n"\
            ,inet_ntoa(clientaddr.sin_addr),ntohs(clientaddr.sin_port));
          if(num==2)
          {
            break;
          }
          gsock[num]=gsock[num+1];        
        }
        sock_count--;
        if(sock_count==0)
        {
          break;
        }
      }
      else
      {
        //데이터 출력
        buf[retval]='\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)
        {
          printf("recv error!\n");
          break;
        }
      }
    }
  
  if(a==3)
  {
    break;

  }
    
  }
  printf("sever 종료\n");

  return 0;
}

이것은 3명이 들어와서 종료해야 다종료가 되는.. 아직 미완성임

-ATMEGA-
1. 난수를 발생하는 rand 함수를 활용하여 주사위를 10번 던졌을 때 주사위 값을 출력하시오.
(주사위 값 : 1~6)
출력)  dice value :  5
  dice value :  2
  dice value :  1
  ...
  dice value :  1
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int main()
{
  int inum;
  int icnt;
  srand((int)time(NULL));
  for(icnt=0;icnt<10;++icnt)
  {
    inum=(rand()%6+1);
    printf("dice value : %d\n",inum);
  }
  return 0;
}
2. 출현 빈도 그래프 
"  0에서부터 9까지의 60개의 수가 배열에 저장되어 있을 때, 배열 원소 60개의 합과 평균을 구하여 출력하고 각 수에 대한 빈도 수를 세어서 다음과 같이 막대 그래프 형식으로 출력하시오
"
  프로그램의 주요한 부분에 주석을 명시하시오.

 
#include<stdio.h>
int main()
{
  int inum[60]={5,6,7,2,5,3,9,4,6,4,
        4,8,0,6,3,7,0,2,0,8,
        7,8,0,5,8,7,3,9,7,8,
        3,5,2,9,7,5,3,8,7,2,
        7,4,7,2,5,3,8,7,5,6,
        4,7,6,1,6,5,7,7,7,6};
   int icnt;  //배열의 처음부터  끝까지 카운트
   int sum=0;  //값들의  합
   float avarage;  //값들의 평균  
   int count;  //0~9까지 갯수 카운트  
   int ccnt;  //0~9까지 개수 증가치
   int  ppt;  //0~9까지 for 비교연산 증가치
   for(icnt=0;icnt<60;++icnt)  //전체 값의 합계 구함
   {
    sum=sum+inum[icnt];
 }
   avarage=(float)sum/60;    //전체 값의 평균
  printf("Sum  of array : %d\n",sum);
  printf("Average of Array: %.4f\n",avarage);
  printf("no  count bar  graph\n");
  for(ppt=0;ppt<10;++ppt)  //0~9까지 비교증가
  {
    count=0;    //개수의 초기화
    for(icnt=0;icnt<60;++icnt)  //배열0~ 59까지증가
    {  
      if(inum[icnt]==ppt)  //0~9까지 비교해서 
      {
        count++;  //갯수  구함
      }
  
    }
    printf("%d     %d\t",ppt,count);
     for(ccnt=0;ccnt<count;++ccnt)  //개수만큼 *출력
     {
      printf("*");
     }
     printf("\n");
  }
  return 0;
}
[도전] 로또 번호 생성 프로그램
"  사용자에게 로또 게임 수를 입력받아 게임의 횟수별로 임의로 6개의 수(1~45)를 자동 생성하는 프로그램을 작성하시오. 단, 생성된 난수는 모두 다른 값이어야 한다.
"
   로또 번호는 정수형 배열을 선언하여 설정한다.
"  프로그램의 주요한 부분에 주석을 명시하시오.
 

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
int main()
{
  int inum[6];  //저장될 로또 번호 배열
  int i;    //로또 횟수
  int icnt;    //0~5까지의 배열공간
  int GameCount;  //입력될 로또 횟수
  int znum;  //0에서 icnt까지 같은값있는지 비교할 수
  int  bool;  //로또번호가 같은값이 들어가면 1 아니면 0
  printf("
Enter  Game count : 
");
  scanf("
%d",&GameCount);
  srand((int)time(NULL));
  for(i=0;i<GameCount;++i)  //로또횟수만큼  for문
  {
      
    for(icnt=0;icnt<6;++icnt)    //총  6개의 로또번호가  들어감
    {
      bool=0;      //비교되어넣는값 0으로 초기화
      inum[icnt]=(rand()%45)+1;  //inum[icnt]에 0~45까지 random값입력
      for(znum=0;znum<icnt;++znum)  //inum[icnt]와 inum[znum] inum[icnt-1]
              //까지의 값을 비교  
        {
          if(inum[icnt]==inum[znum])  //비교값이 같으면 icnt에
          {        // -1를 넣고 bool값 변경
            icnt--;      
            bool=1;
            break;
            
          }
        
        }
      if(bool==0)          //bool값이 0일때만출력
      {
        printf("%d\t",inum[icnt]);
      }  
    }
    printf("\n");

  }
  return  0;
}

-단방향 직렬통신-
#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);

  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;
}