네트워크 - 인터럽트(interrupt) -> sigaction()
내장형 하드웨어/TCP/IP / 2011. 10. 26. 15:01
- Client에서 종료메시지가 Server로 가고 close 되어 return 하게 한다.
- Server에서 종료메시지가 Client로 가고 close 되어 return 하게 한다.
이를 위해 sigaction()을 사용한다.
int sigaction(int signum, const struct sigaction *act, const struct sigaction *oldact);
// server.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<signal.h>
#define MAXCLIENT 3
int iCNum = 0; // 접속자 수(client number)
int iaClient[MAXCLIENT]; // 접속 가능한 인원 수 - 소켓 번호를 가지고 있다.
int iDs; // 디스크립터 저장
void socketClose(int one);
int main()
{
int maxDs; // 파일디스크립터 설정을 위한 변수
int iAccept; // accept 함수의 반환값 저장
unsigned char ucBuf[256];
fd_set fsStatus; // select()에 사용되는 소켓 식별자 셋
int iCounter; // 반복문을 위한 변수
int iAddSize;
int iRet;
struct sockaddr_in stAddr; // 서버 주소 구조체
struct sockaddr_in stAccept;
struct sigaction stFuncInfo; // 시그널 핸들러를 지정하는 sigaction 구조체
memset(&stFuncInfo, 0x00, sizeof(struct sigaction)); // 구조체를 0으로 초기화 한다.
stFuncInfo.sa_handler = socketClose; // socketClose를 핸들러 함수로 지정
sigaction(SIGINT, &stFuncInfo, NULL); // Ctrl + C가 등록된다.
iAddSize = sizeof(struct sockaddr_in);
bzero(&stAddr, iAddSize); // 0으로 구조체 초기화
// TCP를 사용하여 스트림 소켓을 연다.(반환형은 int)
// 첫번째 인자는 IP protocol family - 2층 선택
// 두번째 인자는 TCP설정, 세번째 인자는 Transmission Control Protocol - 3층
iDs = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(iDs<0) // 예외 처리 - 소켓 생성에 실패했을 때
{
perror("socket() failed");
close(iDs); // 열었던 소켓을 닫는다.
return -10;
}
stAddr.sin_family = AF_INET; // socket()의 첫번째 인자와 같다.
// 주소 변환
iRet = inet_pton(AF_INET, "192.168.10.49", &stAddr.sin_addr.s_addr);
if(iRet == 0)
{
printf("inet_pton() failed", "invalid address string");
close(iDs); // 열었던 소켓을 닫는다.
return -100;
}
else if(iRet < 0)
{
perror("inet_pton() failded");
close(iDs); // 열었던 소켓을 닫는다.
return -100;
}
// ip 출력
printf("IP : %s\n", inet_ntoa(stAddr.sin_addr));
// 서버 포트(포트 문을 열어준다.)
stAddr.sin_port = htons(3000); // 포트 3000번 연다.
// 여기까지가 기본 세팅이며 client와 동일하다.
// 여기부터 client와 달라진다.
// 지역 주소에 바인드(bind-소켓에 이름을 묶는다.)
// 소켓 세팅(윈도우 레지스트 함수랑 유사)
if(0>bind(iDs, (struct sockaddr *)&stAddr, iAddSize))
{
perror("bind() failed");
close(iDs); // 열었던 소켓을 닫는다.
return -100;
}
// 소켓이 들어오는 요청을 처리할 수 있도록 설정(listen)
if(0>listen(iDs, 5))
{
perror("listen() failed");
close(iDs);
return -100;
}
maxDs = iDs+1; // +1 하지 않으면 select가 한 비트 앞까지만 검사한다.
while(1)
{
printf("현재 접속자 수 %d\n", iCNum); // 접속자 수 표시
// 소켓 식별자 벡터를 '0'으로 초기화 하고 서버 소켓이 사용하도록 설정
FD_ZERO(&fsStatus); // select()가 호출된후 매번 '0'으로 초기화
// 랑데뷰 소켓(3번 셋트, 1로 체크해 준다.)
// 그룹은 총 랑데뷰, 키보드, 커뮤니케이션(아래쪽)
FD_SET(iDs, &fsStatus); // 디스크립터, 소켓식별자 벡터
FD_SET(0, &fsStatus); // select가 0번(서버의 키보드 입력)까지 감시한다.
// 접속 인원 체크
for(iCounter=0;iCNum>iCounter;++iCounter)
{
FD_SET(iaClient[iCounter], &fsStatus);
printf("iaClient[%d]=%d 사용자가 들어와 있습니다.\n", iCounter, iaClient[iCounter]);//
}
// 파일디스크립터, 입력, 출력, 에러, 타임아웃(감시하는 시간-NULL은 무한대기)
// 해당하는 소켓에 입력이 없으면 BLOCK 된다.
// accept는 select가 통과시켜 주어야 호출된다.
iRet = select(maxDs, &fsStatus, NULL, NULL, NULL);
printf("%d\n", iRet);
if(0 > select(maxDs, &fsStatus, NULL, NULL, NULL))
{
perror("select() error... ");
close(iDs);
return -100;
}
printf("Select End\n");
if(1 == FD_ISSET(0, &fsStatus)) // 서버의 키보드 입력 확인
{
iRet = read(0, ucBuf, sizeof(ucBuf));
ucBuf[iRet] = 0;
printf("%s\n", ucBuf);
for(iCounter=0;iCNum>iCounter;++iCounter)// 전체 접속 인원 검색
{
write(iaClient[iCounter], ucBuf, iRet);
}
continue;
}
// 클라이언트의 연결을 기다림(accept-소켓에 연결을 받아들인다.)
// accept는 소캣을 새로 만들어 소켓이 두개가 된다. 그리고 소켓에 번호(파일디스크립터)가 할당된다.
// 위에서 만든 소켓은 랑데뷰 소켓(분류만 담당한다. 실제 일은 하지 않는다.)
// 실제 통신이 시작된다. - 접속자가 늘어날때마다 소켓이 계속 생성된다.
// 랑데뷰 소켓은 3번, 좀더 정확하게는 iDs)
// 접속되지 않았던 유저가 접속하려하면 if문 안으로 들어간다.(accept 수행)
if(1 == FD_ISSET(iDs, &fsStatus)) // return 값이 1이면 참
{
iAccept = accept(iDs, (struct sockaddr *)&stAccept, &iAddSize);
if(0>iAccept)
{
perror("accept() error");
close(iDs); // 랑데뷰 함수를 닫는다.
// 들어온 접속자 만큼 닫는다.- 접속자가 없으면 돌지 않는다.
for(iCounter=0;iCNum>iCounter;++iCounter)
{
close(iaClient[iCounter]);
}
return -100;
}
// 접속 제한 인원보다 많이 들어 왔는가 확인
if(MAXCLIENT <= iCNum)
{
write(iAccept, "Server is full connection\n", sizeof("Server is full connection\n"));
close(iAccept);
continue ;
}
// 중간에 빈 파일디스크립터로 생성되었을 경우에는 더하면 안되므로
if(iAccept == maxDs)
{
maxDs = iAccept + 1;
}
iaClient[iCNum] = iAccept;
++iCNum;
// 접속이 오면 상대방 IP가 출력된다.
// 파일로 생각하면 완전히 파일이 열린 상태
printf("Client IP : [%s]\n", inet_ntoa(stAccept.sin_addr));
// 다시 처음으로 돌아간다.
continue ;
}
// 커뮤니케이션 소켓이면 위의 if문을 빠져나온다.
printf("Communication socket input\n");
// 들어온 쪽을 찾기 위해 열려진 부분을 모두 찾아야 한다.
for(iCounter=0;iCNum>iCounter;++iCounter)// 전체 접속 인원 검색
{
// 사용자 소켓 번호 체크
// 해당하는 Client에 1이 세팅되었는가(입력이 들어왔는가)
if(1==FD_ISSET(iaClient[iCounter], &fsStatus))
{
// 저수준으로 읽는다. ucBuf의 길이를 알기 위해 iRet로 반환값을 저장한다.
iRet = read(iaClient[iCounter], ucBuf, sizeof(ucBuf));
if(iRet == 0) // read의 반환값이 0이면 client 소켓이 종료된 것
{
close(iaClient[iCounter]);
printf("iaClient[%d]=%d 사용자가 탈퇴하였습니다.\n", iCounter, iaClient[iCounter]);//
--iCNum;
iaClient[iCounter] = iaClient[iCNum];// 제일 마지막 디스크립터를 빠진 자리에 넣는다.
if(iCounter == iaClient[iCNum]) // 제일 마지막 디스크립터 번호의 소켓이 나갔다면
{
--maxDs;
}
continue;
}
ucBuf[iRet] = 0;
printf("%s\n", ucBuf);
for(iCounter=0;iCNum>iCounter;++iCounter)// 전체 접속 인원 검색
{
write(iaClient[iCounter], ucBuf, iRet);
}
}
}
}
write(iAccept, "pong", sizeof("pong"));
close(iDs); // 열었던 소켓을 닫는다.
close(iAccept);
return 0;
}
void socketClose(int one)
{
int iCounter;
for(iCounter=0;iCNum>iCounter;++iCounter)// 전체 접속 인원 검색
{
write(iaClient[iCounter],"server가 연결을 종료합니다.",sizeof("server가 연결을 종료합니다."));
close(iaClient[iCounter]);
}
close(iDs);
exit(0);
}
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<signal.h>
#define MAXCLIENT 3
int iCNum = 0; // 접속자 수(client number)
int iaClient[MAXCLIENT]; // 접속 가능한 인원 수 - 소켓 번호를 가지고 있다.
int iDs; // 디스크립터 저장
void socketClose(int one);
int main()
{
int maxDs; // 파일디스크립터 설정을 위한 변수
int iAccept; // accept 함수의 반환값 저장
unsigned char ucBuf[256];
fd_set fsStatus; // select()에 사용되는 소켓 식별자 셋
int iCounter; // 반복문을 위한 변수
int iAddSize;
int iRet;
struct sockaddr_in stAddr; // 서버 주소 구조체
struct sockaddr_in stAccept;
struct sigaction stFuncInfo; // 시그널 핸들러를 지정하는 sigaction 구조체
memset(&stFuncInfo, 0x00, sizeof(struct sigaction)); // 구조체를 0으로 초기화 한다.
stFuncInfo.sa_handler = socketClose; // socketClose를 핸들러 함수로 지정
sigaction(SIGINT, &stFuncInfo, NULL); // Ctrl + C가 등록된다.
iAddSize = sizeof(struct sockaddr_in);
bzero(&stAddr, iAddSize); // 0으로 구조체 초기화
// TCP를 사용하여 스트림 소켓을 연다.(반환형은 int)
// 첫번째 인자는 IP protocol family - 2층 선택
// 두번째 인자는 TCP설정, 세번째 인자는 Transmission Control Protocol - 3층
iDs = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(iDs<0) // 예외 처리 - 소켓 생성에 실패했을 때
{
perror("socket() failed");
close(iDs); // 열었던 소켓을 닫는다.
return -10;
}
stAddr.sin_family = AF_INET; // socket()의 첫번째 인자와 같다.
// 주소 변환
iRet = inet_pton(AF_INET, "192.168.10.49", &stAddr.sin_addr.s_addr);
if(iRet == 0)
{
printf("inet_pton() failed", "invalid address string");
close(iDs); // 열었던 소켓을 닫는다.
return -100;
}
else if(iRet < 0)
{
perror("inet_pton() failded");
close(iDs); // 열었던 소켓을 닫는다.
return -100;
}
// ip 출력
printf("IP : %s\n", inet_ntoa(stAddr.sin_addr));
// 서버 포트(포트 문을 열어준다.)
stAddr.sin_port = htons(3000); // 포트 3000번 연다.
// 여기까지가 기본 세팅이며 client와 동일하다.
// 여기부터 client와 달라진다.
// 지역 주소에 바인드(bind-소켓에 이름을 묶는다.)
// 소켓 세팅(윈도우 레지스트 함수랑 유사)
if(0>bind(iDs, (struct sockaddr *)&stAddr, iAddSize))
{
perror("bind() failed");
close(iDs); // 열었던 소켓을 닫는다.
return -100;
}
// 소켓이 들어오는 요청을 처리할 수 있도록 설정(listen)
if(0>listen(iDs, 5))
{
perror("listen() failed");
close(iDs);
return -100;
}
maxDs = iDs+1; // +1 하지 않으면 select가 한 비트 앞까지만 검사한다.
while(1)
{
printf("현재 접속자 수 %d\n", iCNum); // 접속자 수 표시
// 소켓 식별자 벡터를 '0'으로 초기화 하고 서버 소켓이 사용하도록 설정
FD_ZERO(&fsStatus); // select()가 호출된후 매번 '0'으로 초기화
// 랑데뷰 소켓(3번 셋트, 1로 체크해 준다.)
// 그룹은 총 랑데뷰, 키보드, 커뮤니케이션(아래쪽)
FD_SET(iDs, &fsStatus); // 디스크립터, 소켓식별자 벡터
FD_SET(0, &fsStatus); // select가 0번(서버의 키보드 입력)까지 감시한다.
// 접속 인원 체크
for(iCounter=0;iCNum>iCounter;++iCounter)
{
FD_SET(iaClient[iCounter], &fsStatus);
printf("iaClient[%d]=%d 사용자가 들어와 있습니다.\n", iCounter, iaClient[iCounter]);//
}
// 파일디스크립터, 입력, 출력, 에러, 타임아웃(감시하는 시간-NULL은 무한대기)
// 해당하는 소켓에 입력이 없으면 BLOCK 된다.
// accept는 select가 통과시켜 주어야 호출된다.
iRet = select(maxDs, &fsStatus, NULL, NULL, NULL);
printf("%d\n", iRet);
if(0 > select(maxDs, &fsStatus, NULL, NULL, NULL))
{
perror("select() error... ");
close(iDs);
return -100;
}
printf("Select End\n");
if(1 == FD_ISSET(0, &fsStatus)) // 서버의 키보드 입력 확인
{
iRet = read(0, ucBuf, sizeof(ucBuf));
ucBuf[iRet] = 0;
printf("%s\n", ucBuf);
for(iCounter=0;iCNum>iCounter;++iCounter)// 전체 접속 인원 검색
{
write(iaClient[iCounter], ucBuf, iRet);
}
continue;
}
// 클라이언트의 연결을 기다림(accept-소켓에 연결을 받아들인다.)
// accept는 소캣을 새로 만들어 소켓이 두개가 된다. 그리고 소켓에 번호(파일디스크립터)가 할당된다.
// 위에서 만든 소켓은 랑데뷰 소켓(분류만 담당한다. 실제 일은 하지 않는다.)
// 실제 통신이 시작된다. - 접속자가 늘어날때마다 소켓이 계속 생성된다.
// 랑데뷰 소켓은 3번, 좀더 정확하게는 iDs)
// 접속되지 않았던 유저가 접속하려하면 if문 안으로 들어간다.(accept 수행)
if(1 == FD_ISSET(iDs, &fsStatus)) // return 값이 1이면 참
{
iAccept = accept(iDs, (struct sockaddr *)&stAccept, &iAddSize);
if(0>iAccept)
{
perror("accept() error");
close(iDs); // 랑데뷰 함수를 닫는다.
// 들어온 접속자 만큼 닫는다.- 접속자가 없으면 돌지 않는다.
for(iCounter=0;iCNum>iCounter;++iCounter)
{
close(iaClient[iCounter]);
}
return -100;
}
// 접속 제한 인원보다 많이 들어 왔는가 확인
if(MAXCLIENT <= iCNum)
{
write(iAccept, "Server is full connection\n", sizeof("Server is full connection\n"));
close(iAccept);
continue ;
}
// 중간에 빈 파일디스크립터로 생성되었을 경우에는 더하면 안되므로
if(iAccept == maxDs)
{
maxDs = iAccept + 1;
}
iaClient[iCNum] = iAccept;
++iCNum;
// 접속이 오면 상대방 IP가 출력된다.
// 파일로 생각하면 완전히 파일이 열린 상태
printf("Client IP : [%s]\n", inet_ntoa(stAccept.sin_addr));
// 다시 처음으로 돌아간다.
continue ;
}
// 커뮤니케이션 소켓이면 위의 if문을 빠져나온다.
printf("Communication socket input\n");
// 들어온 쪽을 찾기 위해 열려진 부분을 모두 찾아야 한다.
for(iCounter=0;iCNum>iCounter;++iCounter)// 전체 접속 인원 검색
{
// 사용자 소켓 번호 체크
// 해당하는 Client에 1이 세팅되었는가(입력이 들어왔는가)
if(1==FD_ISSET(iaClient[iCounter], &fsStatus))
{
// 저수준으로 읽는다. ucBuf의 길이를 알기 위해 iRet로 반환값을 저장한다.
iRet = read(iaClient[iCounter], ucBuf, sizeof(ucBuf));
if(iRet == 0) // read의 반환값이 0이면 client 소켓이 종료된 것
{
close(iaClient[iCounter]);
printf("iaClient[%d]=%d 사용자가 탈퇴하였습니다.\n", iCounter, iaClient[iCounter]);//
--iCNum;
iaClient[iCounter] = iaClient[iCNum];// 제일 마지막 디스크립터를 빠진 자리에 넣는다.
if(iCounter == iaClient[iCNum]) // 제일 마지막 디스크립터 번호의 소켓이 나갔다면
{
--maxDs;
}
continue;
}
ucBuf[iRet] = 0;
printf("%s\n", ucBuf);
for(iCounter=0;iCNum>iCounter;++iCounter)// 전체 접속 인원 검색
{
write(iaClient[iCounter], ucBuf, iRet);
}
}
}
}
write(iAccept, "pong", sizeof("pong"));
close(iDs); // 열었던 소켓을 닫는다.
close(iAccept);
return 0;
}
void socketClose(int one)
{
int iCounter;
for(iCounter=0;iCNum>iCounter;++iCounter)// 전체 접속 인원 검색
{
write(iaClient[iCounter],"server가 연결을 종료합니다.",sizeof("server가 연결을 종료합니다."));
close(iaClient[iCounter]);
}
close(iDs);
exit(0);
}
// client.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<signal.h>
#define MAXCLIENT 2
int iDs; // 디스크립터 저장
unsigned char ucBuf[256];
void socketClose(int );
int main()
{
int iaClient[MAXCLIENT]; // 접속 가능한 인원 수 - 소켓 번호를 가지고 있다.
fd_set fsStatus; // select()에 사용되는 소켓 식별자 셋
int iCounter; // 반복문을 위한 변수
int iDSize;
int iRet;
struct sockaddr_in stAddr; // 서버 주소 구조체
struct sigaction stFuncInfo; // 시그널 핸들러를 지정하는 sigaction 구조체
memset(&stFuncInfo, 0x00, sizeof(struct sigaction)); // 메모리를 0으로 채워 넣는다.
stFuncInfo.sa_handler = socketClose; // socketClose를 핸들러 함수로 지정
sigaction(SIGINT, &stFuncInfo, NULL); // Ctrl + C 가 등록된다.
iDSize = sizeof(struct sockaddr_in);
bzero(&stAddr, iDSize); // 0으로 구조체 초기화
// TCP를 사용하여 스트림 소켓을 연다.(반환형은 int)
// 첫번째 인자는 IP protocol family - 2층 선택
// 두번째 인자는 TCP설정, 세번째 인자는 Transmission Control Protocol - 3층
iDs = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(iDs<0) // 예외 처리 - 소켓 생성에 실패했을 때
{
perror("socket() failed");
close(iDs); // 열었던 소켓을 닫는다.
return -10;
}
stAddr.sin_family = AF_INET; // socket()의 첫번째 인자와 같다.
// 주소 변환
iRet = inet_pton(AF_INET, "192.168.10.49", &stAddr.sin_addr.s_addr);
if(iRet == 0)
{
printf("inet_pton() failed", "invalid address string");
close(iDs); // 열었던 소켓을 닫는다.
return -100;
}
else if(iRet < 0)
{
perror("inet_pton() failded");
close(iDs); // 열었던 소켓을 닫는다.
return -100;
}
// ip 출력
printf("IP : %s\n", inet_ntoa(stAddr.sin_addr));
// 서버 포트(포트 문을 열어준다.)
stAddr.sin_port = htons(3000); // 포트 3000번 연다.
// 여기까지가 기본 세팅이며 client와 동일하다.
if(connect(iDs, (struct sockaddr *)&stAddr, iDSize))
{
perror("connect() failed");
close(iDs);
return -10;
}
while(1)
{
// 소켓 식별자 벡터를 '0'으로 초기화 하고 서버 소켓이 사용하도록 설정
FD_ZERO(&fsStatus); // select()가 호출된후 매번 '0'으로 초기화
// 랑데뷰 소켓(3번 셋트, 1로 체크해 준다.)
// 그룹은 총 랑데뷰, 키보드, 커뮤니케이션(아래쪽)
FD_SET(iDs, &fsStatus); // 디스크립터, 소켓식별자 벡터
FD_SET(0, &fsStatus); // select가 0번(서버의 키보드 입력)까지 감시한다.
// 파일디스크립터, 입력, 출력, 에러, 타임아웃(감시하는 시간-NULL은 무한대기)
// 해당하는 소켓에 입력이 없으면 BLOCK 된다.
if(0 > select(iDs+1, &fsStatus, NULL, NULL, NULL))
{
perror("select() error... ");
close(iDs);
return -100;
}
// client가 server로 입력을 보낼 때
if(0 != FD_ISSET(0, &fsStatus)) // 키보드 입력 확인
{
iRet = read(0, ucBuf, sizeof(ucBuf));
ucBuf[iRet] = 0;
printf("%s\n", ucBuf);
write(iDs, ucBuf, iRet);
continue;
}
// server가 보낸 입력을 받을 때
if(0!=FD_ISSET(iDs, &fsStatus))
{
// 저수준으로 읽는다. ucBuf의 길이를 알기 위해 iRet로 반환값을 저장한다.
iRet = read(iDs, ucBuf, sizeof(ucBuf));
ucBuf[iRet] = 0;
printf("%s\n", ucBuf);
if(sizeof("server가 연결을 종료합니다.") == iRet) // 종료하라고 서버에서 문자열이 오면
{
if(0 == memcmp("server가 연결을 종료합니다.", ucBuf, iRet))
{
//write(iDs, "클라이언트가 종료되어 연결이 끊어집니다.", sizeof("클라이언트가 종료되어 연결이 끊어집니다."));
break;
}
}
}
}
close(iDs); // 열었던 소켓을 닫는다.
return 0;
}
void socketClose(int one)
{
write(iDs, "client가 연결을 종료합니다.", sizeof( "client가 연결을 종료합니다.")); // 소켓 번호
read(iDs, ucBuf, sizeof(ucBuf)); // 서버에서 응답이 와야 하므로 기다린다.
close(iDs);
exit(0); // 완전히 종료 시킨다.
}
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<signal.h>
#define MAXCLIENT 2
int iDs; // 디스크립터 저장
unsigned char ucBuf[256];
void socketClose(int );
int main()
{
int iaClient[MAXCLIENT]; // 접속 가능한 인원 수 - 소켓 번호를 가지고 있다.
fd_set fsStatus; // select()에 사용되는 소켓 식별자 셋
int iCounter; // 반복문을 위한 변수
int iDSize;
int iRet;
struct sockaddr_in stAddr; // 서버 주소 구조체
struct sigaction stFuncInfo; // 시그널 핸들러를 지정하는 sigaction 구조체
memset(&stFuncInfo, 0x00, sizeof(struct sigaction)); // 메모리를 0으로 채워 넣는다.
stFuncInfo.sa_handler = socketClose; // socketClose를 핸들러 함수로 지정
sigaction(SIGINT, &stFuncInfo, NULL); // Ctrl + C 가 등록된다.
iDSize = sizeof(struct sockaddr_in);
bzero(&stAddr, iDSize); // 0으로 구조체 초기화
// TCP를 사용하여 스트림 소켓을 연다.(반환형은 int)
// 첫번째 인자는 IP protocol family - 2층 선택
// 두번째 인자는 TCP설정, 세번째 인자는 Transmission Control Protocol - 3층
iDs = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if(iDs<0) // 예외 처리 - 소켓 생성에 실패했을 때
{
perror("socket() failed");
close(iDs); // 열었던 소켓을 닫는다.
return -10;
}
stAddr.sin_family = AF_INET; // socket()의 첫번째 인자와 같다.
// 주소 변환
iRet = inet_pton(AF_INET, "192.168.10.49", &stAddr.sin_addr.s_addr);
if(iRet == 0)
{
printf("inet_pton() failed", "invalid address string");
close(iDs); // 열었던 소켓을 닫는다.
return -100;
}
else if(iRet < 0)
{
perror("inet_pton() failded");
close(iDs); // 열었던 소켓을 닫는다.
return -100;
}
// ip 출력
printf("IP : %s\n", inet_ntoa(stAddr.sin_addr));
// 서버 포트(포트 문을 열어준다.)
stAddr.sin_port = htons(3000); // 포트 3000번 연다.
// 여기까지가 기본 세팅이며 client와 동일하다.
if(connect(iDs, (struct sockaddr *)&stAddr, iDSize))
{
perror("connect() failed");
close(iDs);
return -10;
}
while(1)
{
// 소켓 식별자 벡터를 '0'으로 초기화 하고 서버 소켓이 사용하도록 설정
FD_ZERO(&fsStatus); // select()가 호출된후 매번 '0'으로 초기화
// 랑데뷰 소켓(3번 셋트, 1로 체크해 준다.)
// 그룹은 총 랑데뷰, 키보드, 커뮤니케이션(아래쪽)
FD_SET(iDs, &fsStatus); // 디스크립터, 소켓식별자 벡터
FD_SET(0, &fsStatus); // select가 0번(서버의 키보드 입력)까지 감시한다.
// 파일디스크립터, 입력, 출력, 에러, 타임아웃(감시하는 시간-NULL은 무한대기)
// 해당하는 소켓에 입력이 없으면 BLOCK 된다.
if(0 > select(iDs+1, &fsStatus, NULL, NULL, NULL))
{
perror("select() error... ");
close(iDs);
return -100;
}
// client가 server로 입력을 보낼 때
if(0 != FD_ISSET(0, &fsStatus)) // 키보드 입력 확인
{
iRet = read(0, ucBuf, sizeof(ucBuf));
ucBuf[iRet] = 0;
printf("%s\n", ucBuf);
write(iDs, ucBuf, iRet);
continue;
}
// server가 보낸 입력을 받을 때
if(0!=FD_ISSET(iDs, &fsStatus))
{
// 저수준으로 읽는다. ucBuf의 길이를 알기 위해 iRet로 반환값을 저장한다.
iRet = read(iDs, ucBuf, sizeof(ucBuf));
ucBuf[iRet] = 0;
printf("%s\n", ucBuf);
if(sizeof("server가 연결을 종료합니다.") == iRet) // 종료하라고 서버에서 문자열이 오면
{
if(0 == memcmp("server가 연결을 종료합니다.", ucBuf, iRet))
{
//write(iDs, "클라이언트가 종료되어 연결이 끊어집니다.", sizeof("클라이언트가 종료되어 연결이 끊어집니다."));
break;
}
}
}
}
close(iDs); // 열었던 소켓을 닫는다.
return 0;
}
void socketClose(int one)
{
write(iDs, "client가 연결을 종료합니다.", sizeof( "client가 연결을 종료합니다.")); // 소켓 번호
read(iDs, ucBuf, sizeof(ucBuf)); // 서버에서 응답이 와야 하므로 기다린다.
close(iDs);
exit(0); // 완전히 종료 시킨다.
}
'내장형 하드웨어 > TCP/IP' 카테고리의 다른 글
네트워크 - 시리얼 통신(멀티플렉스) (0) | 2011.10.27 |
---|---|
네트워크 - Server, Client간 다중 접속(채팅) (0) | 2011.10.24 |
네트워크 - TCP 통신 (Client, Server) / 다중 Client 접속 (4) | 2011.10.21 |
TCP/IP - fork_test.c, 좀비 프로세스 (0) | 2011.07.14 |
TCP 프로토콜을 이용산 소켓 프로그래밍, udpecho_server/client (0) | 2011.07.05 |