성엽이
KKIMSSI
성엽이
전체 방문자
오늘
어제
  • 분류 전체보기 (454)
    • :) (2)
    • C프로그래밍이론 (9)
    • C++프로그래밍 (64)
      • STL (1)
    • C# (2)
    • Visual studio 10.0 (9)
    • AT91SAM7S256 (21)
    • 논리회로 (14)
    • AVR2560 (11)
    • TCPIP (16)
    • NetWork (4)
      • Ubuntu Linux (2)
    • Assembly (21)
    • UNIX 프로그래밍 (6)
    • RFID 분석 (1)
    • Win32 API (7)
    • Cortex-M3 (4)
    • Unity (91)
    • Flutter (9)
    • OwnProject (11)
      • It's mine (5)
      • 마인드스톰 실습 (1)
      • 보고서 자료 (2)
      • RPi B+ (2)
    • ETC (25)
      • 상식 (3)
    • MFC (40)
    • PostgeSQL (18)
    • 영상제어 (6)
      • VFW_영상처리 (1)
    • Python (0)
    • Java (30)
      • SpringBoot (2)
      • Javascript (1)
      • JSP (13)
      • Spring (8)
    • Oracle (4)
      • SQL (3)
    • HTML (6)
      • Thymeleaf (1)
      • CSS (1)
      • Bootstrap (3)
    • IDE (1)
      • VS Code (1)
    • Android (2)
    • Privacy Policy (0)
    • MYSQL (2)
      • MariaDB (2)
    • AWS (5)
    • 개인공부 (0)

블로그 메뉴

  • 홈
  • 태그
  • 미디어로그
  • 위치로그
  • 방명록
  • 관리자
  • 글쓰기

공지사항

인기 글

태그

  • 문자열 나누기
  • ARM Reverse Engineering
  • Boot Code 분석
  • MFC
  • WINAPI

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
성엽이

KKIMSSI

2013.07.10_Thread를 이용한 Server 구현.
TCPIP

2013.07.10_Thread를 이용한 Server 구현.

2013. 7. 10. 18:48


 TCPserver.c

 #include "smart.h"


#define MAXPENDING   5
#define MAXUSER    10

///// Thread ID 를 알아야하기때문에 구조체로 넘겨줌. //
// User 사람 수 관리.
typedef struct _TInfo
{
  unsigned int    uiUser;  // 위치번호.
  int         iSock;    // 소켓번호.
  pthread_t     t_ID;    // Thread 번호.
}TInfo;
/////

void *ClientRecv(void *);

///// 크리티컬 섹션 시작. /////
unsigned int   uiUser;
TInfo *       stpLink[MAXUSER]; // 포인터 배열. 사람수 만큼 만듦.
///// 크리티컬 섹션 종료. /////

///// 뮤텍스 객체 선언. /////
pthread_mutex_t MLock;

int main(int iArg, char *cpArg[])
{
  int         servSock; 
  TInfo       st_TempInfo;
  struct       sockaddr_in echoServAddr;
  struct       sockaddr_in echoClntAddr;
  unsigned short   echoServPort;
  unsigned int   clntLen;
  int       iRet;
  int       iCnt;
  int       iCnt2;
  unsigned char   ucBuff[500];
  
  if(1 == iArg)
  {
    echoServPort = 9999;
  }
  else if(2 == iArg)
  {  
    echoServPort = atoi(cpArg[1]);
  }
  
  servSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if(0 > servSock)
  {
    printf("socket() failed");

    return 0;
  }

  memset(&echoServAddr, 0, sizeof(echoServAddr));
  echoServAddr.sin_family = AF_INET;
  echoServAddr.sin_addr.s_addr = htonl(INADDR_ANY);
  echoServAddr.sin_port = htons(echoServPort);

  iRet = bind(servSock, (struct sockaddr *)&echoServAddr, sizeof(echoServAddr));
  if(0 > iRet)
  {
    close(servSock);
    printf("bind() failed");

    return 0;
  }

  iRet = listen(servSock, MAXPENDING);
  if(0 > iRet)
  {
    close(servSock);
    printf("listen() failed");
    return 0;
  }

  clntLen = sizeof(echoClntAddr);

  uiUser = 0;
  
  // 뮤텍스 초기화.
  pthread_mutex_init(&MLock, NULL);
  while(1)
  {  // 구조체의 iSock 넣음.
    st_TempInfo.iSock = accept(servSock, (struct sockaddr *)&echoClntAddr, &clntLen);
    if(0 > st_TempInfo.iSock)
    {
      printf("accept() failed");
      continue;  
    }  
    printf("Handling client ip : %s\n", inet_ntoa(echoClntAddr.sin_addr));
    printf("Handling client port : %d\n", ntohs(echoClntAddr.sin_port));
    printf("Handling client socket number : %d\n", st_TempInfo.iSock);
    if(MAXUSER <= uiUser)
    {
      close(st_TempInfo.iSock);
      continue;
    }
    // Lock !
    // uiUser 접근할때마다 Lock/UnLock 을 걸어줘서.
    // 다른 Thread가 접근을 못하도록 한다. 즉 공유자원을 
    // 같이 써서 발생하는 혼선을 막는다.
    pthread_mutex_lock(&MLock);
    
    // 구조체에다가 uiUser 의 값을 넣어둠.
    st_TempInfo.uiUser = uiUser;
  
    // 첫번째 인자에 Thread ID 주소.
    // pthread_create 의 마지막 인자를 구조체 통째로 넘겨줌.
    pthread_create(&st_TempInfo.t_ID, 0, ClientRecv, &st_TempInfo);
    ++uiUser;
    pthread_mutex_unlock(&MLock);
    // Unlock !
    while(0 != st_TempInfo.iSock);  // ClientRecv 에서 Thread를 생성하기 전까지 잡아둠.
    printf("현재 접속자 수 : %d\n", uiUser);
  }

  close(servSock);

  return 0;
}

// Thread ID 를 받아서 Client Sock을 확보
void *ClientRecv(void *vp)
{
  unsigned char   uc_Buffer[500];
  unsigned char   ucS_Buffer[500];
  int       iRet;
  unsigned int   uiCnt;
  // stMyInfo 구조체를 하나 더 생성해서 stTempInfo의 값을
  // 몽땅 넣어줌. (uiUser, iSock, Thread ID)
  TInfo stMyInfo = *((TInfo *)vp);

  // 전역변수에 선언해놓은 포인터를 이용해서 stMyInfo를 가리킨다.
  stpLink[stMyInfo.uiUser] = &stMyInfo;    
  
  // 구조체안의 iSock 의 번호를 삭제.
  ((TInfo *)vp)->iSock = 0;
  // 구조체 안의 iSock 번호가 0 이므로 main 함수의 while(0 != st_TempInfo.iSock) 을 통과.
  // Thread 중복을 방지하기 위해서 사용.
  // Thread 와 Main 이 여기서 부터 따로 돌겠다.
  
  while(1)
  {
    iRet = read(stMyInfo.iSock, uc_Buffer, 500);
    if(1 >= iRet)
    {  // Ctrl+C로 오류가 뜨면 멈춤.
      break;
    }
    uc_Buffer[iRet - 1] = 0; // Enter 방지.

      // 서버에 직접 적힘.
    printf("[MyUserNum:%d]:[%s]\n", stMyInfo.uiUser, uc_Buffer);
    if('$' == uc_Buffer[0])
    {  
      break;
    }
    // 다른 Thread 에 보내기위해서 ucS_Buffer에다가 글을 넣어둠.
    iRet = sprintf(ucS_Buffer, "[MyUserNum:%d]:[%s]\n", stMyInfo.uiUser, uc_Buffer);
  
    // 메모리에 출력할 갯수(iRet)를 크기만큼 넣어주고,
    // uiUser의 사람수만큼 for 문 돌림.
    for(uiCnt=0 ; uiCnt<uiUser ;++uiCnt)
    {  
      // 내가 적은 문자출력은 나에게 보내주지 않음.
      if(&stMyInfo == stpLink[uiCnt])
      {  
        continue;
      }
      // stpLink[] 포인터가 가리키는 iSock의 번호에다가 각각 보낸다.
      write(stpLink[uiCnt]->iSock, ucS_Buffer, iRet);
    }
  }
  // Thread 종료.
  pthread_mutex_lock(&MLock);
  
  // uiUser 감소.
  --uiUser;
  // stMyInfo 안의 uiUser에다가 , 
  // 현재 감소된 uiUser의 수를 넣어준다.
  // 포인터로 가르킨다.
  stpLink[stMyInfo.uiUser] = stpLink[uiUser];

  // 두명이 접속 했을시에 전역번수의 uiUser의 값은 2명이다.
  // 이때 stpLink가 가리키는 stMyInfo.uiUser의
  // 배열 갯수가 [0],[1] 로 2개이다. 
  // 만약 [0]번의 손님이 나가면, stpLink[0]이 가리키고있는 곳을 
  // [1]번이 가리키는 곳의 구조체로 바꿔주고, 구조체의 uiUser 번호를
  // 바꿔줘서 uiUser로 유저의 현재 수를 체크할수있다.
  stpLink[stMyInfo.uiUser]->uiUser = stMyInfo.uiUser;
  
  pthread_mutex_unlock(&MLock);
  close(stMyInfo.iSock);
  return 0;

}


int sprintf(char *str, const char *format, ...);             <=   sprintf() 함수          

#include <stdio.h>


int main()
{

 char cBuff[500] = "안녕하세요\n"; 
 
 sprintf(cBuff,"1234는 16진수로 %X입니다.\n", 1234);
 // cBuff(메모리) 안에 " " 안의 내용이 프린트(저장) 됨.
 // 반환값은 프린트된 문자 갯수임. 
 

 printf(cBuff); // cBuff안의 문자열이 출력됨.
 

 return 0;


}

 

 

 

 

 

 


 



 : stTempInfo가 ClientRecv로 넘어오면 그때의 구조체를 stMyInfo에 넣어두고 사용하는 방식으로 테스트 해보았다.

 






 TCPclient.c

#include "smart.h"


#define RCVBUFSIZE   500

void My_Memset(void *, unsigned char, unsigned int);
void My_Bzero(void *, unsigned int);

int main(int argc, char *argv[])
{
  int sock;
  struct sockaddr_in echoServAddr;
  char *servIP;
  char cBuff[RCVBUFSIZE];
  int iRet;
  fd_set fs_status;
  
  if(1 == argc)
  {
    servIP = "192.168.10.200";
  }
  else
  {
    servIP = argv[1];
  }

  sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  if(0 > sock)
  {
    printf("socket() failed\n");
    
    return 0;
  }

  My_Bzero(&echoServAddr, sizeof(echoServAddr));
  echoServAddr.sin_family = AF_INET;
  echoServAddr.sin_addr.s_addr = inet_addr(servIP);
  echoServAddr.sin_port = htons(9999);

  iRet = connect(sock, (struct sockaddr *)&echoServAddr, sizeof(echoServAddr));
  if(0 > iRet)
  {
    close(sock);
    printf("connect() failed\n");

    return 0;
  }
    
  while(1)
  {
    FD_ZERO(&fs_status);
    FD_SET(0, &fs_status);
    FD_SET(sock, &fs_status);
    iRet = select(sock+1, &fs_status, 0, 0, 0);
    if(0 > iRet)
    {
      write(sock, LOGOUT, sizeof(LOGOUT));
      printf("select() failed\n");

      break;
    }
    if(1 == FD_ISSET(0, &fs_status))
    {
      iRet = read(0, cBuff, 500);
      if(CLTEND == cBuff[0])
      {
        write(sock, LOGOUT, sizeof(LOGOUT));
        printf("Log out\n");

        break;  
      }
      write(sock, cBuff, iRet);
      
    }
    if(1 == FD_ISSET(sock, &fs_status))
    { // Ctrl+C 했을시 조건문.
      iRet = read(sock, cBuff, 500);
      if(1 > iRet)
      {
        printf("Server Down\n");
        break;  
      }
      if(0 == strcmp(ENDMSG, cBuff))
      {
        printf("Server Down\n");
        break;  
      }

      printf("[Server: ");
      fflush(stdout);
      write(1, cBuff, iRet-1);
      printf("]\n");
    }
  }
  close(sock);

  return 0;
}

void My_Memset(void *vp, unsigned char ucPad, unsigned int uiSize)
{
  while(0 != uiSize)
  {
    *(unsigned char *)vp = ucPad;
    --uiSize;
    vp = (unsigned char *)vp + 1;
  }
  
  return;
}

void My_Bzero(void *vp, unsigned int uiSize)
{
  My_Memset(vp, 0x00, uiSize);

  return;
}




저작자표시 (새창열림)

'TCPIP' 카테고리의 다른 글

2013.08.05_Window에서 Packet Capture 하기 연습  (0) 2013.08.05
2013.07.12_SockCapture_소켓으로 패킷캡쳐하기_  (0) 2013.07.12
2013.07.09_TCP/IP를 Thread로 통신_  (0) 2013.07.09
2013.07.08_Inter Process Communication(IPC)_활용.  (0) 2013.07.09
2013.07.04_멀티태스킹 테스트  (0) 2013.07.05
    'TCPIP' 카테고리의 다른 글
    • 2013.08.05_Window에서 Packet Capture 하기 연습
    • 2013.07.12_SockCapture_소켓으로 패킷캡쳐하기_
    • 2013.07.09_TCP/IP를 Thread로 통신_
    • 2013.07.08_Inter Process Communication(IPC)_활용.
    성엽이
    성엽이

    티스토리툴바