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