코드 설명
Thread 와 Socket Programming 어느 정도 배운 상태에서 나올수 있는 숙제입니다. Blocking 방식으로 스레드를 활용하여 여러명을 받아 메시지를 뿌리는 프로그램입니다. Mutex 을 활용하여 자원을 공유하여 수행합니다. 5명까지 받을수 있도록 하되 그 이상을 받으면 오류를 내는 프로그램 입니다.
참고 - 학교 과제용 코드 입니다. 그러니 이제 검색되므로 이 코드로는 채점되기 쉽지 않을 겁니다. 예제로 쓰시기 바랍니다.
받기
클라이언트
서버
실행화면
코드내용
Thread 와 Socket Programming 어느 정도 배운 상태에서 나올수 있는 숙제입니다. Blocking 방식으로 스레드를 활용하여 여러명을 받아 메시지를 뿌리는 프로그램입니다. Mutex 을 활용하여 자원을 공유하여 수행합니다. 5명까지 받을수 있도록 하되 그 이상을 받으면 오류를 내는 프로그램 입니다.
참고 - 학교 과제용 코드 입니다. 그러니 이제 검색되므로 이 코드로는 채점되기 쉽지 않을 겁니다. 예제로 쓰시기 바랍니다.
받기
클라이언트
서버
실행화면
코드내용
/* 저작권 http://ccl.korea.ac.kr - 리포트 소유권 http://cakel.tistory.com - 프로그램 저작권 교육용을 목적으로 자유롭게 쓰실수 있습니다. Thread_Client.cpp - 스레드를 활용한 멀티 채팅 서버의 클라이언트 로컬 호스트 주소에 서버를 생성 / 복수의 클라이언트를 받은 후에 클라이언트가 받은 메시지를 다른 클라언트들에게 전송하는 기능을 가지고 있습니다. */ #include <stdio.h> #include <string.h> #include <winsock.h> #include <process.h> #pragma comment(lib,"wsock32.lib") #define PORT 5000 // 사용할 포트는 5000 #define IP "127.0.0.1" // 접속할 서버는 로컬 호스트 void recv_thread(void*); // 스레드 수행 함수의 프로토 타입 int ret = 0; // 리턴 값 int s = 0; // 소켓 값 HANDLE hMutex; // 뮤텍스용 int main() { // Welcome Screen printf("+---------------------------+\n"); printf("+ 스레드를 이용한 멀티 채팅 +\n"); printf("+ 클라이언트 부분 +\n"); printf("+ http://cakel.tistory.com +\n"); printf("+ http://ccl.korea.ac.kr +\n"); printf("+---------------------------+\n"); // 뮤택스 초기화 hMutex = CreateMutex(NULL, FALSE, FALSE); if(!hMutex) { printf("Mutex 생성 오류\n"); return 1; } // 윈속 초기화 WSADATA wsd; char buff[1024]; if(WSAStartup(MAKEWORD(1,1), &wsd) != 0) { printf("Winsock 오류\n"); return 1; } // 서버로 접속하기 위한 소켓 생성 sockaddr_in server; s = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); if (s == SOCKET_ERROR) { printf("socket() 오류\n"); closesocket(s); WSACleanup(); return 1; } server.sin_addr.s_addr = inet_addr(IP); server.sin_family = AF_INET; server.sin_port = htons(PORT); // 서버와 연결 if( connect(s, (sockaddr*)&server, sizeof(server)) == SOCKET_ERROR) { printf("connect() 오류\n"); closesocket(s); WSACleanup(); return 1; } printf("서버와 연결이 되었음.\n"); // 서버에서 우선 주는 Welcome 메시지 분석 ret = recv(s, buff, 1024,0); printf("%s", buff); // 가득 찼다고 메시지가 왔다면 if(!strcmp("가득찼습니다.\n",buff)) { closesocket(s); WSACleanup(); return 0; // 접속을 종료 } // 정상 접속이 되면 스레드 작동 - 받는 메시지를 스레드로 실시간 수행 _beginthread(recv_thread, 0, NULL); // 보내는 메시지는 스레드로 넣을 필요가 없습니다. while(ret != INVALID_SOCKET || ret != SOCKET_ERROR) { // 딜레이 : CPU 점유율 감소용 Sleep(10); printf("보낼 메시지 입력 : "); fgets(buff, 1024, stdin); // 전송 결과 잘못 된 결과를 얻었을때 탈출 if(ret == INVALID_SOCKET || ret == SOCKET_ERROR) break; // 서버로 보내는 글은 fgets 을 받고 나서 순차적으로 보냄 ret = send(s, buff, strlen(buff), 0); // 버퍼 초기화 memset(buff, 0, 1024); } // 정상/비정상으로 서버와 통신 결과값이 틀린 값을 받았을 때 printf("서버와 연결이 끊겼습니다.\n"); closesocket(s); WSACleanup(); return 0; } // 받는 스레드 부분 void recv_thread(void* pData) { int ret_thread = 65535; char buff_thread[1024] = {0}; // 스레드용 리턴 값이 우너하는 값이 아니면 받는 중에 서버와 통신이 끊겼다고 보고 나감 while(ret_thread != INVALID_SOCKET || ret_thread != SOCKET_ERROR) { Sleep(10); // CPU 점유률 100% 방지용 // 서버에서 주는 메시지를 실시간으로 기다렸다가 받습니다. ret_thread = recv(s, buff_thread, sizeof(buff_thread), 0); // 서버에서 받는 작업을 한 결과 비정상일 때 탈출 if(ret_thread == INVALID_SOCKET || ret_thread == SOCKET_ERROR) break; // 정상적으로 받은 버퍼를 출력 printf("\n%d 메시지 받음 : %s", strlen(buff_thread), buff_thread); memset(buff_thread,0,1024); // 받은 버퍼를 초기와 } // 작업이 끝난 소켓을 무효화시킴 WaitForSingleObject(hMutex,100L); ret = INVALID_SOCKET; ReleaseMutex(hMutex); return; }
/* 저작권 http://ccl.korea.ac.kr - 리포트 소유권 http://cakel.tistory.com - 프로그램 저작권 교육용을 목적으로 자유롭게 쓰실수 있습니다. 로컬 호스트 주소에 서버를 생성 / 복수의 클라이언트를 받은 후에 클라이언트가 받은 메시지를 다른 클라언트들에게 전송하는 기능을 가지고 있습니다. */ #include <stdio.h> #include <process.h> #include <winsock.h> #include <windows.h> #pragma comment(lib, "wsock32.lib") #define PORT 5000 // 사용포트는 5000 #define MAX_CLIENT 5 // 최대 허용 인원 수 5개 #define ALLOW 65535 // 최대 생성 가능 소켓 번호 65535 void recv_client(void *ns); // 스레드 함수 프로토 타입 int client_num = 0; // 점유 횟수 (클라이언트 갯수) int seat = 0; // 클라언트 번호 char welcome_ok[] = "번 클라이언트, 환영합니다.\n\0"; // Welcome 정상 초기 글 char welcome_full[] = "가득찼습니다.\n"; // Welcome 사용자 초가시 생기는 글 int client_sock[ALLOW]; // client_sock (클라이언트 Welcome Socket) HANDLE hMutex = 0; // 뮤택스 int main() { // Welcome Screen printf("+---------------------------+\n"); printf("+ 스레드를 이용한 멀티 채팅 +\n"); printf("+ 서버 부분 +\n"); printf("+ http://cakel.tistory.com +\n"); printf("+ http://ccl.korea.ac.kr +\n"); printf("+---------------------------+\n"); // 뮤택스 생성 hMutex = CreateMutex(NULL, FALSE, NULL); // 생성 실패시 오류 if(!hMutex) { printf("Mutex 오류\n"); CloseHandle(hMutex); return 1; } // 윈속 초기화 WSADATA wsd; if(WSAStartup(MAKEWORD(1,1), &wsd) != 0) // 사용 소켓 버전은 1.1 { printf("Winsock 오류\n"); WSACleanup(); return 1; } // Listen 소켓 생성 int s, addrsize, ret; sockaddr_in server, client; s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (s == SOCKET_ERROR) { printf("socket() 오류\n"); closesocket(s); WSACleanup(); return 1; } server.sin_addr.s_addr = htonl(INADDR_ANY); server.sin_family = AF_INET; server.sin_port = htons(PORT); // Bind 하기 if( bind(s, (sockaddr*)&server, sizeof(server) ) == SOCKET_ERROR) { printf("bind() 오류\n"); closesocket(s); WSACleanup(); return 1; } printf("클라이언트를 기다립니다. %d 남음\n", MAX_CLIENT - client_num); listen(s,10); addrsize = sizeof(client); // 사용자의 접속을 기다립니다. while(1) { // Blocking 방식으로 Client 를 기다립니다. client_sock[seat] = accept(s, (sockaddr*)&client, &addrsize); // accept 시(중요, client_num 가 accept() 함수 수행중 에 변할수 있으므로 // MAX_CLIENT 도달시랑 따로 accept() 시 문제 발생 가능성 있음 if(client_num < MAX_CLIENT) // 정상 맞이 하기 { if(client_sock[seat] != INVALID_SOCKET || client_sock[seat] != SOCKET_ERROR){} _beginthread(recv_client, 0, &client_sock[seat]); Sleep(10); printf("%d번 클라이언트 %s:%d 에서 접속\n", seat, inet_ntoa(client.sin_addr), ntohs(client.sin_port)); } else // 가득 찾다. { addrsize = sizeof(client); if (client_sock[seat] == INVALID_SOCKET) { printf("accept() 오류\n"); closesocket(client_sock[seat]); closesocket(s); WSACleanup(); return 1; } ret = send(client_sock[seat], welcome_full, sizeof(welcome_full), 0); closesocket(client_sock[seat]); // 메시지 보내고 바로 끊는다. } } return 0; } void recv_client(void *ns) { // 정상적으로 받아 드릴때, 스레드 실행 // 클라이언트의 숫자를 늘림 WaitForSingleObject(hMutex,INFINITE); client_num++; // 클라이언트 갯수 증가 seat++; // 클라이언트 번호 증가 printf("슬롯 %d개 남음\n", MAX_CLIENT - client_num); // 갯수로 판단 ReleaseMutex(hMutex); char welcome[100] = {0}; // accept 된 소켓에게 줄 버퍼 생성 char buff[1024] = {0}; int ret, i; itoa(seat,welcome,10); // 클라이언트 번호 strcat(welcome,welcome_ok); // 정상 환영 메시지 환영 ret = send(*(SOCKET*)ns, welcome, sizeof(welcome), 0); // 전송 while(ret != SOCKET_ERROR || ret != INVALID_SOCKET) { ret = recv(*(SOCKET*)ns, buff, 1024, 0); // 클라이언트의 메시지를 받음 // broadcast 부분 for(i = 0; i < ALLOW; i++) { // 받은 클라이언트 소켓의 메모리 주소와 보내는 클라이언트 소켓 메모리 주소가 다를때만 전송 WaitForSingleObject(hMutex,INFINITE); if( ((unsigned*)&client_sock[i] != (SOCKET*)ns)) { send(client_sock[i], buff, strlen(buff), 0); } ReleaseMutex(hMutex); } // 서버 콘솔 창에 전송 기록 남김 if(strlen(buff) != 0) printf("%d 메시지 보냄 : %s", strlen(buff), buff); memset(buff, 0, 1024); } // 접속된 소켓이 연결을 해제 시켰을때 WaitForSingleObject(hMutex,INFINITE); client_num--; printf("%d 클라이언트 해제\n슬롯 %d개 남음\n", seat, MAX_CLIENT - client_num); ReleaseMutex(hMutex); closesocket(*(int*)ns); return; }
Posted by 카켈