윈도우 소켓을 이용한 멀티스레드 채팅 프로그램 소스(C)


윈도우소켓_채팅.zip


서버측

 


 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Windows.h>
#include <process.h>
 
#define BUF_SIZE 100
#define MAX_CLNT 256
 
unsigned WINAPI HandleClient(void* arg);//쓰레드 함수
void SendMsg(char* msg,int len);//메시지 보내는 함수
void ErrorHandling(char* msg);
 
int clientCount=0;
SOCKET clientSocks[MAX_CLNT];//클라이언트 소켓 보관용 배열
HANDLE hMutex;//뮤텍스
 
int main(int argc,char* argv[]){
    WSADATA wsaData;
    SOCKET serverSock,clientSock;
    SOCKADDR_IN serverAddr,clientAddr;
    int clientAddrSize;
    HANDLE hThread;
    if(argc!=2){
        printf("Usage : %s <port>\n",argv[0]);
        exit(1);
    }
    if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0//윈도우 소켓을 사용하겠다는 사실을 운영체제에 전달
        ErrorHandling("WSAStartup() error!");
 
    hMutex=CreateMutex(NULL,FALSE,NULL);//하나의 뮤텍스를 생성한다.
    serverSock=socket(PF_INET,SOCK_STREAM,0); //하나의 소켓을 생성한다.
 
    memset(&serverAddr,0,sizeof(serverAddr));
    serverAddr.sin_family=AF_INET;
    serverAddr.sin_addr.s_addr=htonl(INADDR_ANY);
    serverAddr.sin_port=htons(atoi(argv[1]));
 
    if(bind(serverSock,(SOCKADDR*)&serverAddr,sizeof(serverAddr))==SOCKET_ERROR) //생성한 소켓을 배치한다.
        ErrorHandling("bind() error");
    if(listen(serverSock,5)==SOCKET_ERROR)//소켓을 준비상태에 둔다.
        ErrorHandling("listen() error");
 
    while(1){
        clientAddrSize=sizeof(clientAddr);
        clientSock=accept(serverSock,(SOCKADDR*)&clientAddr,&clientAddrSize);//서버에게 전달된 클라이언트 소켓을 clientSock에 전달
        WaitForSingleObject(hMutex,INFINITE);//뮤텍스 실행
        clientSocks[clientCount++]=clientSock;//클라이언트 소켓배열에 방금 가져온 소켓 주소를 전달
        ReleaseMutex(hMutex);//뮤텍스 중지
        hThread=(HANDLE)_beginthreadex(NULL,0,HandleClient,(void*)&clientSock,0,NULL);//HandleClient 쓰레드 실행, clientSock을 매개변수로 전달
        printf("Connected Client IP : %s\n",inet_ntoa(clientAddr.sin_addr));
    }
    closesocket(serverSock);//생성한 소켓을 끈다.
    WSACleanup();//윈도우 소켓을 종료하겠다는 사실을 운영체제에 전달
    return 0;
}
 
unsigned WINAPI HandleClient(void* arg){
    SOCKET clientSock=*((SOCKET*)arg); //매개변수로받은 클라이언트 소켓을 전달
    int strLen=0,i;
    char msg[BUF_SIZE];
 
    while((strLen=recv(clientSock,msg,sizeof(msg),0))!=0//클라이언트로부터 메시지를 받을때까지 기다린다.
        SendMsg(msg,strLen);//SendMsg에 받은 메시지를 전달한다.
 
    //이 줄을 실행한다는 것은 해당 클라이언트가 나갔다는 사실임 따라서 해당 클라이언트를 배열에서 제거해줘야함
    WaitForSingleObject(hMutex,INFINITE);//뮤텍스 실행
    for(i=0;i<clientCount;i++){//배열의 갯수만큼
        if(clientSock==clientSocks[i]){//만약 현재 clientSock값이 배열의 값과 같다면
            while(i++<clientCount-1)//클라이언트 개수 만큼
                clientSocks[i]=clientSocks[i+1];//앞으로 땡긴다.
            break;
        }
    }
    clientCount--;//클라이언트 개수 하나 감소
    ReleaseMutex(hMutex);//뮤텍스 중지
    closesocket(clientSock);//소켓을 종료한다.
    return 0;
}
 
void SendMsg(char* msg,int len){ //메시지를 모든 클라이언트에게 보낸다.
    int i;
    WaitForSingleObject(hMutex,INFINITE);//뮤텍스 실행
    for(i=0;i<clientCount;i++)//클라이언트 개수만큼
        send(clientSocks[i],msg,len,0);//클라이언트들에게 메시지를 전달한다.
    ReleaseMutex(hMutex);//뮤텍스 중지
}
void ErrorHandling(char* msg){
    fputs(msg,stderr);
    fputc('\n',stderr);
    exit(1);
}
cs


클라이언트 측


 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Windows.h>
#include <process.h>
 
#define BUF_SIZE 100
#define NAME_SIZE 20
 
unsigned WINAPI SendMsg(void* arg);//쓰레드 전송함수
unsigned WINAPI RecvMsg(void* arg);//쓰레드 수신함수
void ErrorHandling(char* msg);
 
char name[NAME_SIZE]="[DEFAULT]";
char msg[BUF_SIZE];
 
int main(int argc,char* argv[]){
    WSADATA wsaData;
    SOCKET sock;
    SOCKADDR_IN serverAddr;
    HANDLE sendThread,recvThread;
    if(argc!=4){
        printf("Usage : %s <IP> <port> <name>\n",argv[0]);
        exit(1);
    }
    if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0)// 윈도우 소켓을 사용한다고 운영체제에 알림
        ErrorHandling("WSAStartup() error!");
 
    sprintf(name,"[%s]",argv[3]);
    sock=socket(PF_INET,SOCK_STREAM,0);//소켓을 하나 생성한다.
 
    memset(&serverAddr,0,sizeof(serverAddr));
    serverAddr.sin_family=AF_INET;
    serverAddr.sin_addr.s_addr=inet_addr(argv[1]);
    serverAddr.sin_port=htons(atoi(argv[2]));
 
    if(connect(sock,(SOCKADDR*)&serverAddr,sizeof(serverAddr))==SOCKET_ERROR)//서버에 접속한다.
        ErrorHandling("connect() error");
 
    //접속에 성공하면 이 줄 아래가 실행된다.
 
    sendThread=(HANDLE)_beginthreadex(NULL,0,SendMsg,(void*)&sock,0,NULL);//메시지 전송용 쓰레드가 실행된다.
    recvThread=(HANDLE)_beginthreadex(NULL,0,RecvMsg,(void*)&sock,0,NULL);//메시지 수신용 쓰레드가 실행된다.
 
    WaitForSingleObject(sendThread,INFINITE);//전송용 쓰레드가 중지될때까지 기다린다./
    WaitForSingleObject(recvThread,INFINITE);//수신용 쓰레드가 중지될때까지 기다린다.
    //클라이언트가 종료를 시도한다면 이줄 아래가 실행된다.
    closesocket(sock);//소켓을 종료한다.
    WSACleanup();//윈도우 소켓 사용중지를 운영체제에 알린다.
    return 0;
}
 
unsigned WINAPI SendMsg(void* arg){//전송용 쓰레드함수
    SOCKET sock=*((SOCKET*)arg);//서버용 소켓을 전달한다.
    char nameMsg[NAME_SIZE+BUF_SIZE];
    while(1){//반복
        fgets(msg,BUF_SIZE,stdin);//입력을 받는다.
        if(!strcmp(msg,"q\n")||!strcmp(msg,"Q\n")){//q를 입력하면 종료한다.
            closesocket(sock);
            exit(0);
        }
        sprintf(nameMsg,"%s %s",name,msg);//nameMsg에 메시지를 전달한다.
        send(sock,nameMsg,strlen(nameMsg),0);//nameMsg를 서버에게 전송한다.
    }
    return 0;
}
 
unsigned WINAPI RecvMsg(void* arg){
    SOCKET sock=*((SOCKET*)arg);//서버용 소켓을 전달한다.
    char nameMsg[NAME_SIZE+BUF_SIZE];
    int strLen;
    while(1){//반복
        strLen=recv(sock,nameMsg,NAME_SIZE+BUF_SIZE-1,0);//서버로부터 메시지를 수신한다.
        if(strLen==-1)
            return -1;
        nameMsg[strLen]=0;//문자열의 끝을 알리기 위해 설정
        fputs(nameMsg,stdout);//자신의 콘솔에 받은 메시지를 출력한다.
    }
    return 0;
}
 
void ErrorHandling(char* msg){
    fputs(msg,stderr);
    fputc('\n',stderr);
    exit(1);
}
cs