/* talk_client.c소스 */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <sys/wait.h>
#define MAXLINE 1024
void z_handler(); //시그널 처리함수
char *escapechar = "exit"; //종료문자열
int main(int argc, char *argv[])
{
char line[MAXLINE];
char sendline[MAXLINE];
char recvline[MAXLINE];
int n;
int size;
int comp;
int addr_size;
int fd[2]; // 파이프 함수를 위한 변수
pid_t fork_ret;
static int s;
static struct sockaddr_in server_addr;
//signal사용하기 위해 추가된 변수들
int state;
struct sigaction act;
if(argc != 3) //인자 갯수를 파일명,서버IP,포트번호 3개를 입력 안했을시
{
printf("Usage : ./%s serverIP serverPORT \n", argv[0]);
exit(0);
}
//파이프 생성
state = pipe(fd);
if(state == -1) // pipe 함수 부모와 자식 프로세스가 생성되지 않았을 시 발생
{
printf("pipe() error");
exit(1);
}
//signal설정
act.sa_handler = z_handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
state = sigaction(SIGCHLD, &act, 0); //자식 프로세스 종료시 발생하는 시그널 SIGCHLD이용
if(state != 0)
{
puts("sigaction() error. ");
exit(1);
}
if(argc != 3)
{
printf("Usage : %s serverIP serverPORT \n", argv[0]);
exit(0);
}
/* 소켓 생성 */
if( (s = socket(PF_INET, SOCK_STREAM, 0)) < 0)
{
printf("Client : can't open stream socket. \n");
exit(0);
}
/* 소켓주소 구조체에 접속할 서버 주소 세팅 */
bzero((char *)&server_addr, sizeof(server_addr)); //소켓주소 구조체 초기화
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = inet_addr(argv[1]);
server_addr.sin_port = htons(atoi(argv[2]));
/* 서버에 연결 요청 */
if(connect(s, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
{
printf("client: can't connect to server. \n");
exit(0);
}
fork_ret = fork(); // fork() 함수 호출
if(fork_ret > 0)
{
/* 부모 프로세스는 키보드 입력을 서버로 송신 */
while(fgets(sendline, MAXLINE, stdin) != NULL)
{
size = strlen(sendline);
if(write(s, sendline, strlen(sendline)) != size)
{
printf("Error in write. \n");
}
if(strstr(sendline, escapechar) != NULL) //종료문자열 입력시 처리
{
//종료메시지를 자식이 받아 볼수 있도록 파이프로 전송
write(fd[1], escapechar, strlen(escapechar));
printf("Good bye. \n");
close(s);
break;
}
}
while(1); //자식이 시그널이 발생시켜서 부모를 죽여줄때까지 돈다.
}
else if(fork_ret == 0)
{
/* 자식 프로세스는 서버로부터 수신된 메시지를 화면에 출력 */
while(1)
{
if( (size = read(s, recvline, MAXLINE)) < 0)
{
printf("Error if read. \n");
close(s);
exit(0);
}
recvline[size] = '\0';
if(size == 0)
{
read(fd[0], recvline, MAXLINE);
if(strstr(recvline, escapechar) != NULL)
{
break;
}
}
if(strstr(recvline, escapechar) != NULL) //종료문자열 입력시 처리
{
break;
}
printf("%s", recvline); //화면 출력
}
}
close(s);
return 0;
}
void z_handler()
{
int state;
waitpid(-1, &state, WNOHANG);
exit(0);
return ;
}
1.용어정리
int main( int argc, char *argv) // 옵션을 설정하는 것
라고 정의하게 되면 (argc인자의 개수)와 (argv인자 내용)을 가져올 수 있습니다.
ex)위의 소스관련 c:\>talk_client 127.0.0.1 9190
라고 실행시키게 되면,
argc 에는 3 가 들어갑니다. (talk_client,127.0.0.1,9190) 이렇게 3개죠.
argv 를 열어보면,
argv[0] = talk_client (실행파일)
argv[1] = 127.0.0. (서버IP)
argv[2] = 9190 (서버포트번호)
함수 원형 : int pipe(int filedes[2]);
헤더 : #include <unistd.h>
반환값 : 성공할경우 0을 실패했을경우에는 -1을 반환한다.
함수 원형 : int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
헤더 : #include <signal.h>
반환 값 : 성공하면 0을 실패하면 -1을 리턴한다.
설명 : sigaction() 시스템 호출은 특정 시그널의 수신에 대해서 취할 액션을 설정하거나 변경하기 위해
서 사용된다.
signum은 시그널을 명시한다. 본 소스에서는 sigchild를 사용하였다.
만약 act가 null이 아니라면 signum번호를 가지는 시그널에 대해서 act함수가 설치된다.
만약 oldact가 null이 아니라면 이전의 액션은 oldact에 저장된다.
sigaction의 구조체 : struct sigaction
{
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
}
헤더 : wait.h
원형 : pid_t waitpid(pid, int *status, int option);
인수 : pid_t pid = 감시할 자식 프로세스 ID
int *status = 자식 프로세스의 종료 상태 정보
int options = 대기를 위한 옵션
반환 : 정상 일 때, 자식 프로세스의 ID반환 실패시 -1 반환 WNOHANG을 사용했고 자식
프로세스가 종료되지 않았다면 0 반환.
설명 :
2. 소스 분석
while(fgets(sendline, MAXLINE, stdin) != NULL)
{
size = strlen(sendline);
if(write(s, sendline, strlen(sendline)) != size)
{
printf("Error in write. \n");
}
}
에러문구를 띄운다. (현재 클라이언트는 서버에 write 한 상태에서 크기를 비교한다.)
if(strstr(sendline, escapechar) != NULL) //종료문자열 입력시 처리
{
write(fd[1], escapechar, strlen(escapechar));
printf("Good bye. \n");
close(s);
break;
}
while(1);
while(1); 해놓은것은 시그널이 발생하기 전에 부모가 미리 죽는 것을 방지 하기 위해서 이다.
if( (size = read(s, recvline, MAXLINE)) < 0)
{
printf("Error if read. \n");
close(s);
exit(0);
}
if(size == 0)
{
read(fd[0], recvline, MAXLINE);
if(strstr(recvline, escapechar) != NULL)
{
break;
}
}
if(strstr(recvline, escapechar) != NULL)
{
break;
}
printf("%s", recvline); // 출력!
/* talk_server.c소스 */
/* talk_server.c */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#define MAXLINE 512
void z_handler(); //시그널 처리함수
char *escapechar = "exit"; //종료 문자열
int main(int argc, char *argv[])
{
int server_sock;
int client_sock;
int clntlen;
int num;
char sendline[MAXLINE];
char recvline[MAXLINE];
int size;
pid_t fork_ret;
struct sockaddr_in client_addr;
struct sockaddr_in server_addr;
//signal 사용하기 위해 추가된 변수들
int state;
int fd[2];
struct sigaction act;
if(argc != 2)
{
printf("Usage : %s PORT \n", argv[0]);
exit(0);
}
//파이프 생성
state = pipe(fd);
if(state == -1)
{
printf("pipe() error");
exit(1);
}
//signal설정
act.sa_handler = z_handler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
state = sigaction(SIGCHLD, &act, 0);
if(0 != state)
{
fprintf(stderr, "sigaction() error. \n");
exit(1);
}
/* 소켓 생성 */
if( (server_sock = socket(PF_INET, SOCK_STREAM, 0)) < 0)
{
printf("Server: can't open stream socket. \n");
exit(0);
}
/* 소켓주소 구조체에 주소 세팅 */
bzero((char *)&server_addr, sizeof(server_addr)); //소켓주소 구조체 초기화
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
server_addr.sin_port = htons(atoi(argv[1]));
/* 소켓에 서버 주소 연결 */
if(bind(server_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0)
{
printf("Server: can't bind local address. \n");
exit(0);
}
printf("Server started/ \nWaiting for client.. \n");
listen(server_sock, 1);
/* 클라이언트의 연결요청 수락 */
clntlen = sizeof(client_addr);
if( (client_sock = accept(server_sock, (struct sockaddr *)&client_addr, &clntlen)) < 0)
{
printf("Server: failend in accepting. \n");
exit(0);
}
fork_ret = fork();
if(0 < fork_ret)
{
//부모 프로세스는 키보드 입력을 클라이언트로 송신
while(fgets(sendline, MAXLINE, stdin) != NULL)
{
size = strlen(sendline);
if(write(client_sock, sendline, strlen(sendline)) != size)
{
printf("Error in write. \n");
}
if(strstr(sendline, escapechar) != NULL) //종료문자열 입력시 처리
{
write(fd[1],sendline, strlen(sendline)); //자식 프로세스의
printf("Good bye. \n");
close(client_sock);
break;
}
}
while(1); //자식이 죽기까지 부모가 기다림.
}
else if(fork_ret == 0)
{
/* 자식 프로세스는 클라이언트로부터 수신된 메시지를 화면에 출력*/
while(1)
{
if( (size = read(client_sock, recvline, MAXLINE)) < 0)
{
printf("Error if read. \n");
close(client_sock);
exit(0);
}
if(size == 0)
{
read(fd[0], recvline, MAXLINE);
close(client_sock);
break;
}
recvline[size] = '\0';
if(strstr(recvline, escapechar) != NULL) //종료문자열 입력시 처리
{
close(client_sock);
break;
}
printf("%s", recvline); //화면 출력
}
}
close(server_sock);
close(client_sock);
return 0;
}
//시그널 처리함수
void z_handler()
{
int state;
waitpid(-1, &state, WNOHANG); //자식의 죽음을 확인.
exit(0); //부모가 죽는다.
return ;
}
1.용어 정리
bind() 함수는 소켓에 IP주소와 포트번호를 지정해 줍니다. 이로서 소켓을 통신에 사용할 수 있도록 준비가 됩니다.
헤더 | #include <sys/types.h> #include <sys/socket.h> | |
원형 | int bind(int sockfd, struct sockaddr *myaddr, socklen_t addrlen); |
2.소스 분석
if(0 < fork_ret)
{
while(fgets(sendline, MAXLINE, stdin) != NULL)
{
size = strlen(sendline);
if(write(client_sock, sendline, strlen(sendline)) != size)
{
printf("Error in write. \n");
}
if(strstr(sendline, escapechar) != NULL)
{
write(fd[1],sendline, strlen(sendline));
printf("Good bye. \n");
close(client_sock);
break;
}
}
while(1); //자식이 죽기까지 부모가 기다림.
}
else if(fork_ret == 0)
{
while(1)
{
if( (size = read(client_sock, recvline, MAXLINE)) < 0)
{
printf("Error if read. \n");
close(client_sock);
exit(0);
}
if(size == 0)
{
read(fd[0], recvline, MAXLINE);
close(client_sock);
break;
}
recvline[size] = '\0';
if(strstr(recvline, escapechar) != NULL)
{
close(client_sock);
break;
}
printf("%s", recvline); //화면 출력
}
}
/* 서버에서 exit 한결과 */
<실행>
<종료>
서버와 클라이언트가 완전히 끊겼다는 것을 확인 할 수 있다.
/* 클라이언트에서 exit 한결과 */
<실행>
어느 한쪽에서 종료 하더라도 통신이 정상적으로 종료되는 것을 확인 할 수 있다.
더 궁금하신 사항은 수만,춘우,세선사마님께 문의 바람
'코스웨어 > 11년 내장형하드웨어' 카테고리의 다른 글
[내장형]김수만_2011년7월27일_일일보고서(선생님의 채팅프로그램 소스코드(chat_server.c, chat_client.c)의 전체적인 흐름과 분석, 네트워크프로젝트 계획), JAVA String Class, toString(), charAt(), equals(), substring() 메소드를 사용한 예제4개 (16) | 2011.07.27 |
---|---|
[내장형]이상만 7월 26일 일일보고서 (12) | 2011.07.26 |
[내장형]하드웨어 최성태 (10) | 2011.07.25 |
[내장형]윤병도_20110722 JAVA(클래스,getter/setter함수),멀티플렉싱(select함수) (17) | 2011.07.22 |
[내장형]김수만_배고픈 금붕어 관찰일지 >_< (19) | 2011.07.21 |
[내장형]백길남_2011년 7월 20일 일일 보고서 (15) | 2011.07.20 |
[내장형]이영진_7월 19일 일일보고서 (12) | 2011.07.20 |
[내장형]박춘우_7월18일 월요일 Daily Report (26) | 2011.07.18 |