여기서 사용하는 소스코드는 아래의 블로그에서 가져왔으며 공개를 원치 않으시는 경우 이 글이 삭제 될 수 있음을 알려드립니다.

출처 : https://crazythink.github.io/2018/05/25/socketchat/

 

하나의 c파일로 이루어진 소스를 공유라이브러리를 사용하기 위해 하나씩 분리시켜 보겠습니다.

  • thread_function.c
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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <fcntl.h>
#include <sys/file.h>
#include <unistd.h>
#include <pthread.h>
 
#define MAXLINE  511
#define MAX_SOCK 1024 // 솔라리스의 경우 64
 
int num_user = 0;
int num_chat = 0;
char ip_list[MAX_SOCK][20];
 
void *thread_function(void *arg) { //명령어를 처리할 스레드
        int i;
        printf("명령어 목록 : help, num_user, num_chat, ip_list\n");
        while (1) {
                char bufmsg[MAXLINE + 1];
                fprintf(stderr, "\033[1;32m"); //글자색을 녹색으로 변경
                printf("server>"); //커서 출력
                fgets(bufmsg, MAXLINE, stdin); //명령어 입력
                if (!strcmp(bufmsg, "\n")) continue;   //엔터 무시
                else if (!strcmp(bufmsg, "help\n"))    //명령어 처리
                        printf("help, num_user, num_chat, ip_list\n");
                else if (!strcmp(bufmsg, "num_user\n"))//명령어 처리
                        printf("현재 참가자 수 = %d\n", num_user);
                else if (!strcmp(bufmsg, "num_chat\n"))//명령어 처리
                        printf("지금까지 오간 대화의 수 = %d\n", num_chat);
                else if (!strcmp(bufmsg, "ip_list\n")) //명령어 처리
                        for (i = 0; i < num_user; i++)
                                printf("%s\n", ip_list[i]);
                else //예외 처리
                        printf("해당 명령어가 없습니다.help를 참조하세요.\n");
        }
}
cs
  • addClient.c
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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
 
#define MAX_SOCK 1024 // 솔라리스의 경우 64
 
int extern num_user;                    // 채팅 참가자 수
int extern clisock_list[MAX_SOCK];              // 채팅에 참가자 소켓번호 목록
char extern ip_list[MAX_SOCK][20];
 
// 새로운 채팅 참가자 처리
void addClient(int s, struct sockaddr_in *newcliaddr) {
        char buf[20];
        inet_ntop(AF_INET, &newcliaddr->sin_addr, buf, sizeof(buf));
        write(1"\033[0G"4);         //커서의 X좌표를 0으로 이동
        fprintf(stderr, "\033[33m");    //글자색을 노란색으로 변경
        printf("new client: %s\n", buf);//ip출력
        // 채팅 클라이언트 목록에 추가
        clisock_list[num_user] = s;
        strcpy(ip_list[num_user], buf);
        num_user++//유저 수 증가
}
cs
  • removeClient.c
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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <time.h>
 
#define MAX_SOCK 1024 // 솔라리스의 경우 64
int extern num_user;                    // 채팅 참가자 수
int extern clisock_list[MAX_SOCK];              // 채팅에 참가자 소켓번호 목록
char extern ip_list[MAX_SOCK][20];              //접속한 ip목록
time_t extern ct;
struct tm extern tm;
 
// 채팅 탈퇴 처리
void removeClient(int s) {
        close(clisock_list[s]);
        if (s != num_user - 1) { //저장된 리스트 재배열
                clisock_list[s] = clisock_list[num_user - 1];
                strcpy(ip_list[s], ip_list[num_user - 1]);
        }
        num_user--//유저 수 감소
        ct = time(NULL);                        //현재 시간을 받아옴
        tm = *localtime(&ct);
        write(1"\033[0G"4);         //커서의 X좌표를 0으로 이동
        fprintf(stderr, "\033[33m");//글자색을 노란색으로 변경
        printf("[%02d:%02d:%02d]", tm.tm_hour, tm.tm_min, tm.tm_sec);
        printf("채팅 참가자 1명 탈퇴. 현재 참가자 수 = %d\n", num_user);
        fprintf(stderr, "\033[32m");//글자색을 녹색으로 변경
        fprintf(stderr, "server>"); //커서 출력
}
cs
  • getmax.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <fcntl.h>
#include <unistd.h>
 
#define MAX_SOCK 1024 // 솔라리스의 경우 64
 
int extern listen_sock;                 // 서버의 리슨 소켓
int extern num_user;
int extern clisock_list[MAX_SOCK];              // 채팅에 참가자 소켓번호 목록
 
 
// 최대 소켓번호 찾기
int getmax() {
        // Minimum 소켓번호는 가정 먼저 생성된 listen_sock
        int max = listen_sock;
        int i;
        for (i = 0; i < num_user; i++)
                if (clisock_list[i] > max)
                        max = clisock_list[i];
        return max;
}
cs
  • tcp_listen.c
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
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <time.h>
 
 
// listen 소켓 생성 및 listen
int  tcp_listen(int host, int port, int backlog) {
        int sd;
        struct sockaddr_in servaddr;
 
        sd = socket(AF_INET, SOCK_STREAM, 0);
        if (sd == -1) {
                perror("socket fail");
                exit(1);
        }
        // servaddr 구조체의 내용 세팅
        bzero((char *)&servaddr, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_addr.s_addr = htonl(host);
        servaddr.sin_port = htons(port);
        if (bind(sd, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
                perror("bind fail");  exit(1);
        }
        // 클라이언트로부터 연결요청을 기다림
        listen(sd, backlog);
        return sd;
}
cs

 


이번에는 위 함수들을 선언한 header 파일들을 만들겠습니다.

  • thread_function.h
1
2
3
4
5
6
#ifndef FUNCS1_H
#define FUNCS1_H
 
void *thread_function(void *arg);
 
#endif
cs
  • addclient.h
1
2
3
4
5
6
#ifndef FUNCS1_H
#define FUNCS1_H
 
void addClient(int s,struct sockaddr_in,*newcliaddr);
 
#endif
cs
  • removeclient.h
1
2
3
4
5
6
#ifndef FUNCS1_H
#define FUNCS1_H
 
void removeClient(int s);
 
#endif
cs
  • getmax.h
1
2
3
4
5
6
#ifndef FUNCS1_H
#define FUNCS1_H
 
int getmax();
 
#endif
cs
  • tcp_listen.h
1
2
3
4
5
6
#ifndef FUNCS1_H
#define FUNCS1_H
 
int tcp_listen(int host, int port, int backlog);
 
#endif
cs

 


이렇게 만든 헤더파일들을 각 기능별 c파일에 선언해 주시면 됩니다.

  • ex)include "tcp_listen.h" 이런식으로 모든 코드에 모든 헤더를 선언하셔야 됩니다.

 


모든 lib파일에 추가 했다면 마지막으로 server.c를 작성합니다.

 

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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/file.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <time.h>
#include <pthread.h>
 
#include "thread.h"
#include "addclient.h"
#include "removeclient.h"
#include "getmax.h"
#include "tcp_listen.h"
 
//this is server C source file
#define MAXLINE  511
#define MAX_SOCK 1024 // 솔라리스의 경우 64
 
char *EXIT_STRING = "exit";    // 클라이언트의 종료요청 문자열
char *START_STRING = "Connected to chat_server \n";
// 클라이언트 환영 메시지
int maxfdp1;                // 최대 소켓번호 +1
int num_user = 0;            // 채팅 참가자 수
int num_chat = 0;            // 지금까지 오간 대화의 수
int clisock_list[MAX_SOCK];        // 채팅에 참가자 소켓번호 목록
char ip_list[MAX_SOCK][20];        //접속한 ip목록
int listen_sock;            // 서버의 리슨 소켓
 
                            // 새로운 채팅 참가자 처리
void addClient(int s, struct sockaddr_in *newcliaddr);
int getmax();                // 최대 소켓 번호 찾기
void removeClient(int s);    // 채팅 탈퇴 처리 함수
int tcp_listen(int host, int port, int backlog); // 소켓 생성 및 listen
void errquit(char *mesg) { perror(mesg); exit(1); }
 
time_t ct;
struct tm tm;
 
int main(int argc, char *argv[]) {
    struct sockaddr_in cliaddr;
    char buf[MAXLINE + 1]; //클라이언트에서 받은 메시지
    int i, j, nbyte, accp_sock, addrlen = sizeof(struct
        sockaddr_in);
    fd_set read_fds;    //읽기를 감지할 fd_set 구조체
    pthread_t a_thread;
 
    if (argc != 2) {
        printf("사용법 :%s port\n", argv[0]);
        exit(0);
    }
 
    // tcp_listen(host, port, backlog) 함수 호출
    listen_sock = tcp_listen(INADDR_ANY, atoi(argv[1]), 5);
    //스레드 생성
    pthread_create(&a_thread, NULL, thread_function, (void *)NULL);
    while (1) {
        FD_ZERO(&read_fds);
        FD_SET(listen_sock, &read_fds);
        for (i = 0; i < num_user; i++)
            FD_SET(clisock_list[i], &read_fds);
 
        maxfdp1 = getmax() + 1;    // maxfdp1 재 계산
        if (select(maxfdp1, &read_fds, NULLNULLNULL< 0)
            errquit("select fail");
 
        if (FD_ISSET(listen_sock, &read_fds)) {
            accp_sock = accept(listen_sock,
                (struct sockaddr*)&cliaddr, &addrlen);
            if (accp_sock == -1) errquit("accept fail");
            addClient(accp_sock, &cliaddr);
            send(accp_sock, START_STRING, strlen(START_STRING), 0);
            ct = time(NULL);            //현재 시간을 받아옴
            tm = *localtime(&ct);
            write(1"\033[0G"4);        //커서의 X좌표를 0으로 이동
            printf("[%02d:%02d:%02d]", tm.tm_hour, tm.tm_min, tm.tm_sec);
            fprintf(stderr, "\033[33m");//글자색을 노란색으로 변경
            printf("사용자 1명 추가. 현재 참가자 수 = %d\n", num_user);
            fprintf(stderr, "\033[32m");//글자색을 녹색으로 변경
            fprintf(stderr, "server>"); //커서 출력
        }
 
        // 클라이언트가 보낸 메시지를 모든 클라이언트에게 방송
        for (i = 0; i < num_user; i++) {
            if (FD_ISSET(clisock_list[i], &read_fds)) {
                num_chat++;                //총 대화 수 증가
                nbyte = recv(clisock_list[i], buf, MAXLINE, 0);
                if (nbyte <= 0) {
                    removeClient(i);    // 클라이언트의 종료
                    continue;
                }
                buf[nbyte] = 0;
                // 종료문자 처리
                if (strstr(buf, EXIT_STRING) != NULL) {
                    removeClient(i);    // 클라이언트의 종료
                    continue;
                }
                // 모든 채팅 참가자에게 메시지 방송
                for (j = 0; j < num_user; j++)
                    send(clisock_list[j], buf, nbyte, 0);
                printf("\033[0G");        //커서의 X좌표를 0으로 이동
                fprintf(stderr, "\033[97m");//글자색을 흰색으로 변경
                printf("%s", buf);            //메시지 출력
                fprintf(stderr, "\033[32m");//글자색을 녹색으로 변경
                fprintf(stderr, "server>"); //커서 출력
            }
        }
 
    }  // end of while
 
    return 0;
}
cs

 


컴파일하는 방식에 따라서 makefile을 수정하겠습니다.

  • player/Makefile
1
2
3
4
5
6
7
8
9
10
11
12
EXEC=server
CC=gcc
CFLAGS=-Wall
 
$(EXEC): server.o
        $(CC) -o server server.o -L../lib -lserverfuncs -lpthread
 
%.o: %.c
        $(CC) --I../include  $< -o $@ -lpthread
 
clean:
        rm server.o server
cs
  • lib/Makefile
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
CC=gcc
LIB=libserverfuncs.so.1.0
OBJS=thread.o addclient.o removeclient.o getmax.o tcp_listen.o
CFLAGS=-Wall
POINTER=libserverfuncs.so libserverfuncs.so.1
 
$(LIB): $(OBJS)
        $(CC) -fPIC -shared -Wl,-soname=libserverfuncs.so.1 $(OBJS) -o $@ -lc
        ln -s libserverfuncs.so.1.0 libserverfuncs.so
        ln -s libserverfuncs.so.1.0 libserverfuncs.so.1
 
%.o: %.c
        $(CC) -fPIC -c $(CFLAGS) -I../include  $< -o $@
 
clean:
        rm -f $(OBJS) $(LIB) $(POINTER)
cs

 


결과

  • make 명령어 사용

  • server 실행파일 실행 - 실행하기 전에 공유라이브러리의 PATH설정 부분을 잘 보고 따라해야 실행이 됩니다.

 

 

여기서 사용하는 소스코드는 아래의 블로그에서 가져왔으며 공개를 원치 않으시는 경우 이 글이 삭제 될 수 있음을 알려드립니다.

출처 : https://crazythink.github.io/2018/05/25/socketchat/ - 습관적 프로그래밍

'채팅 프로그램 제작' 카테고리의 다른 글

최종완성 및 프로젝트 분석  (0) 2018.12.11
[Client]코드와 설명  (0) 2018.12.11
Makefile만들기  (0) 2018.11.24
[계획서]socket 통신을 이용한 채팅프로그램  (0) 2018.11.19

+ Recent posts