2011년7월18일_fork( )를 사용한 멀티테스킹을 이용해 TCP/IP 서버와 클라이언트간 양방향 통신을 해보자.
● fork( )를 이용해 서버와 클라이언트에 각 자식프로세스를 생성.
서버와 클라이언트의 각 부모프로세스들은 send( )또는 write( )를 사용해 상대방에게 전송한다.
자식프로세스들은 recv( ) 또는 read( )를 사용해 상대방이 전송한 메세지를 수신한다.
프로세스가 따로 돌아가고 있으므로 하나의 프로세스로 통신을 할 때와 다르게,
수신과 송신을 동시에 할 수 있다.
부모프로세스와 자식프로세스는 서로 소켓을 공유한다.
● talk_server.c
1: // talk_server.c 2: #include <stdio.h>
3: #include <string.h> 4: #include <stdlib.h>
5: #include <sys/types.h>
6: #include <signal.h>
7: #include <sys/socket.h>
8: #include <netinet/in.h> 9: #include <arpa/inet.h>
10: #include <unistd.h>
11:
12: #define MAXLINE 512 13:
14: char *escapechar = "exit"; //종료문자열 15:
16: int main(int argc, char *argv[]) 17: {
18: int server_sock; 19: int client_sock; 20: int clntlen; 21: int num; 22: char sendline[MAXLINE]; 23: char buffer[MAXLINE]; 24: char recvline[MAXLINE]; 25: int size; 26: pid_t fork_ret;
27: struct sockaddr_in client_addr; 28: struct sockaddr_in server_addr; 29:
30: if(argc != 2) 31: {
32: printf("Usage : %s PORT \n", argv[0]); 33: exit(0);
34: }
35:
36: // 소켓 생성 37: if((server_sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) 38: {
39: printf("Server: can't open stream socket. \n"); 40: exit(0);
41: }
42:
43: // 소켓주소 구조체에 주소 세팅 44: bzero((char *)&server_addr, sizeof(server_addr)); //소켓주소 구조체 초기화 45: server_addr.sin_family = AF_INET;
46: server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
47: server_addr.sin_port = htons(atoi(argv[1]));
48:
49: // 소켓에 서버 주소 연결 50: if(bind(server_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) 51: {
52: printf("Server: can't bind local address. \n"); 53: exit(0);
54: }
55:
56: printf("Server started. \nWaiting for client..\n"); 57: listen(server_sock, 1);
58:
59: // 클라이언트의 연결요청 수락 60: clntlen = sizeof(client_addr); 61: if((client_sock = accept(server_sock, (struct sockaddr *)&client_addr, &clntlen)) < 0) 62: {
63: printf("Server: failed in acceptiong. \n"); 64: exit(0);
65: }
66:
67: if((fork_ret = fork()) > 0) 68: {
69: // 부모 프로세스는 키보드 입력을 클라이언트로 송신 70: while(fgets(sendline, MAXLINE, stdin) != NULL) 71: {
72: strcpy(buffer, "수만 : "); 73: strcat(buffer, sendline);
74: size = strlen(buffer);
75: if(write(client_sock, buffer, strlen(buffer)) != size) 76: {
77: printf("Error in write. \n"); 78: }
79:
80: if(strstr(sendline, escapechar) != NULL) //종료문자열 입력시 처리 81: {
82: printf("Good bye. \n"); 83: close(client_sock);
84: exit(0);
85: }
86: }
87: }
88: else if(0 == fork_ret) 89: {
90: // 자식 프로세스는 클라이언트로부터 수신된 메시지를 화면에 출력 91: while(1) 92: {
93: if((size = read(client_sock, recvline, MAXLINE)) < 0) 94: {
95: printf("Error if read. \n"); 96: close(client_sock);
97: exit(0);
98: }
99:
100: recvline[size] = '\0'; 101:
102: if(strstr(recvline, escapechar) != NULL) //종료문자열 입력시 처리 103: {
104: break; 105: }
106:
107: printf("%s", recvline); //화면 출력 108: }
109: }
110:
111: close(server_sock);
112: close(client_sock);
113:
114: return 0; 115: }
|
● talk_client.c
1: // talk_client.c 2: #include <stdio.h>
3: #include <string.h> 4: #include <stdlib.h>
5: #include <sys/types.h>
6: #include <signal.h>
7: #include <sys/socket.h>
8: #include <netinet/in.h> 9: #include <arpa/inet.h>
10:
11: #define MAXLINE 1024 12:
13: char *escapechar = "exit"; //종료문자열 14:
15: int main(int argc, char *argv[]) 16: {
17: char line[MAXLINE]; 18: char sendline[MAXLINE]; 19: char recvline[MAXLINE + 1]; 20: char buffer[MAXLINE]; 21: int n; 22: int size; 23: int comp; 24: int addr_size; 25: pid_t fork_ret;
26: static int s; 27: static struct sockaddr_in server_addr; 28:
29: if(argc != 3) 30: {
31: printf("Usage : ./%s serverIP serverPORT \n", argv[0]); 32: exit(0);
33: }
34:
35: // 소켓 생성 36: if((s = socket(PF_INET, SOCK_STREAM, 0)) < 0) 37: {
38: printf("Client: can't open stream socket. \n"); 39: exit(0);
40: }
41:
42: // 소켓주소 구조체에 주소 세팅 43: bzero((char *)&server_addr, sizeof(server_addr)); //소켓주소 구조체 초기화 44: server_addr.sin_family = AF_INET;
45: server_addr.sin_addr.s_addr = inet_addr(argv[1]);
46: server_addr.sin_port = htons(atoi(argv[2]));
47:
48: // 서버에 연결 요청 49: if(connect(s, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) 50: {
51: printf("Client: can't connect to server. \n"); 52: exit(0);
53: }
54:
55: fork_ret = fork();
56:
57: if(fork_ret > 0) 58: {
59: // 부모 프로세스는 키보드 입력을 클라이언트로 송신 60: while(fgets(sendline, MAXLINE, stdin) != NULL) 61: {
62: strcpy(buffer, "수만: "); 63: strcat(buffer, sendline);
64: size = strlen(buffer);
65: if(write(s, buffer, strlen(buffer)) != size) 66: {
67: printf("Error in write. \n"); 68: }
69:
70: if(strstr(sendline, escapechar) != NULL) //종료문자열 입력시 처리 71: {
72: printf("Good bye. \n"); 73: close(s);
74: exit(0);
75: }
76: }
77: }
78: else if(0 == fork_ret) 79: {
80: // 자식 프로세스는 클라이언트로부터 수신된 메시지를 화면에 출력 81: while(1) 82: {
83: if((size = read(s, recvline, MAXLINE)) < 0) 84: {
85: printf("Error if read. \n"); 86: close(s);
87: exit(0);
88: }
89:
90: recvline[size] = '\0'; 91:
92: if(strstr(recvline, escapechar) != NULL) //종료문자열 입력시 처리 93: {
94: break; 95: }
96:
97: printf("%s", recvline); //화면 출력 98: }
99: }
100:
101: close(s);
102:
103: return 0; 104: }
|
<실행결과>
이 TCP서버&클라이언트 소스코드의 문제점은,
① 내가 치고 있는 동안 상대방이 메시지를 보내면 내가 보낼 문자열 뒤에 수신문자열이 붙어 혼란스럽다.
② 1 대 1 접속밖에 되지 않는다.
③ 서버 & 클라이언트 모두 자식프로세스가 종료되지 않아 메시지 수신을 계속한다.
①번과 ②번은 조금 불편한 수준에 그치지만 ③은 조금 치명적인 에러가 아닌가 생각된다.
이와 같이 프로세스가 종료되지 않아 자원을 차지하고 있어 낭비이고,
메시지를 계속 수신하니 그 또한 문제이다.
프로세스가 실행 중이라는 사실을 모른체 계속 클라이언트나 서버를 실행하면,
상기와 같이 쓸데없이 자원을 차지하는 프로세스가 계속 불어난다.
문제해결은 집에 가서 작성하겠음. 바쁘다 바뻐 >_<”