C++Builder Programming Forum
C++Builder  |  Delphi  |  FireMonkey  |  C/C++  |  Free Pascal  |  Firebird
볼랜드포럼 BorlandForum
 경고! 게시물 작성자의 사전 허락없는 메일주소 추출행위 절대 금지
C++빌더 포럼
Q & A
FAQ
팁&트릭
강좌/문서
자료실
컴포넌트/라이브러리
메신저 프로젝트
볼랜드포럼 홈
헤드라인 뉴스
IT 뉴스
공지사항
자유게시판
해피 브레이크
공동 프로젝트
구인/구직
회원 장터
건의사항
운영진 게시판
회원 메뉴
북마크
볼랜드포럼 광고 모집

C++빌더 Q&A
C++Builder Programming Q&A
[13634] 팁은 [Tip'n Tricks]로...
김백일 [] 1458 읽음    2001-12-13 11:50
유용한 글이네요.
근데 여기다 올리시면 하루만 지나도 다음 페이지로 넘어갈텐데요.
[Tip'n Tricks]에다 올려서 길이 보전하시길... ^^;

나그네 님이 쓰신 글 :
: 이거는 하이텔 비쥬얼동에서 가져왔습니다..
: 문제 있으면 삭제해 주세요..ㅅㅅ;
:
: [ 팁 ] 소켓 접속이 끊어졌을 때 다시 연결하기
:
: 세번째로 올라가는 팁입니다.
: 이번 내용은 TCP/IP를 이용한 소켓 통신을 할 때 서버 소켓이 어떤
: 이유로 인해 접속 중인 클라이언트 소켓과의 접속을 모두 끊었을 때
: 클라이언트 소켓이 서버에 다시 접속할 수 있는 방법입니다. 처음엔
: OnDisconnected 이벤트가 발생할 때 소켓의 Active를 다시 true로
: 해 주면 될 것이 아닌가 생각했는데, 해 보니까 절대 안 되더군요.
: 왜 안되었는지 그 이유와, 안되는걸 되게 하는 방법을 이제 설명드
: 리겠습니다.
:
: 먼저 소켓 통신을 테스트 해 볼 프로그램을 만들어야겠죠? 원래 제
: 대로 할려면 실제 통신을 통해 전송할 패킷부터 만들어서 통신하다
: 가 접속이 끊어질 때 다시 접속을 시도하게 해야 되는데.. 여기서는
: 그런 귀찮은 거 다 빼고 접속/해제/재시도의 과정만 테스트하도록
: 하겠습니다. 아.. 참고로 소켓 통신 프로그램을 만들려면 BCB 3.0
: C/S 슈트가 필요합니다. 스탠다드에서 테스트 하시려면 통신망에 돌
: 아다니는 적당한 소켓 컴포넌트 구해서 쓰세요. 그러나 이 팁에서
: 설명하는 과정과 틀린 설치 방법은 제가 책임 못 집니다. -_-
:
: 두개의 프로젝트가 필요합니다. 하나는 서버 프로젝트이고 다른 하나
: 는 클라이언트 프로젝트입니다. 서버 프로젝트에는 ServerSocket,
: Button, ListBox 컴포넌트를 각각 갖다 놓습니다. ServerSocket의 속
: 성 중 Active=false, Port=4000(아니면 적당한 다른 숫자)으로 각각
: 설정합니다. Active는 서버소켓이 동작 중인지 나타내는 플래그이고,
: Port는 서버 소켓이 통신할 포트 번호입니다. 포트 번호는 다른 소켓
: 프로그램이 사용하는 포트 번호와 중복되는 안되니까 1000보다 큰 값
: 중 아무거나 적어주면 재수없게 다른 프로그램이 사용하는 포트 번호
: 를 적지 않는 한 제대로 동작합니다. 다른 속성은 묻지 마세요. 저도
: 잘 모릅니다.. 아~ 오늘 다 드러나는 승이의 무식~무식~무식~ ㅠ.ㅠ
:
: 버튼의 OnClick 이벤트에 다음과 같은 코드를 적어줍니다.
: void __fastcall TServerForm::ActiveBtnClick(TObject *Sender)
: {
: ServerSocket1->Active = !ServerSocket1->Active;
: if (ServerSocket1->Active)
: ListBox1->Add("서버를 활성화시켰습니다.");
: else
: ListBox1->Add("서버를 비활성화시켰습니다.");
: }
:
: 버튼의 기능이 뭔지 이제 눈치까셨죠? 서버 소켓을 활성/비활성 상태
: 로 만들어 주는 기능입니다. 서버 소켓의 Active가 true로 설정되면
: 자동으로 주어진 포트번호에 대해 Listen 상태로 동작합니다.
: 여기서 Listen 상태란 클라이언트가 서버에 접속을 요청할 때 요청을
: 수락할 수 있는 상태를 말합니다. Active=true만 해 주면 서버가 다
: 알아서 해 준다고 생각하시면 되겠습니다.
:
: 이제 ServerSocket의 OnAccept 이벤트에 대해 다음과 같은 코드를 추
: 가합니다.
: void __fastcall TServerForm::ServerSocket1Accept(TObject *Sender,
: TCustomWinSocket *Socket)
: {
: ListBox1->Add(Socket->RemoteAddress+"에서 접속했습니다.");
: }
: 위의 기능은 클라이언트가 접속을 요청해서 요청을 받아줬을 때 클라
: 이언트가 접속한 주소를 표시하는 기능입니다.
:
: 마지막으로 클라이언트가 접속을 끊은 경우에 대한 표시입니다.
: void __fastcall TServerForm::ServerSocket1ClientDisconnect(TObject *Sender,
: TCustomWinSocket *Socket)
: {
: ListBox1->Add(Socket->RemoteAddress+"에서 접속을 끊었습니다.");
: }
: 이 기능은 클라이언트가 접속을 끊었을 때 접속한 주소에서 끊었음을
: 알려주는 기능입니다. 쉽죠? ^_^
:
: 자, 이제 클라이언트 프로젝트입니다. ClientSocket, Button, ListBox
: 컴포넌트를 폼 위에 이쁘게(서버는 못 생겨도 상관없다. 그러나
: 클라이언트가 못 생기면..? 보는 내가 눈이 아프다) 배치합니다. 그리
: 고 ClientSocket의 속성 중 Port는 서버 프로젝트의 ServerSocket에 설
: 정해 준 Port와 같은 값을 씁니다. 그리고 이제 주소를 적어 줘야 되는
: 데.. 여기서 문제가 생깁니다. 내 PC는 네트워크 안되는데 어떻게 해야
: 테스트 할 수 있느냐? 아시는 분은 다 아시겠지만 모르는 분들을 위해
: 네트워크 안되도 서버에 접속할 수 있는 주소를 알려 드리겠습니다.
: 어차피 서버가 실행되는 컴퓨터도 자기 꺼, 클라이언트가 실행되는 컴
: 퓨터도 자기 꺼입니다 --;;; 그러면 자기 PC에서 자기 PC로 네트워크
: 접속하는 법은? 127.0.0.1, 즉 localhost를 가리키는 주소로 접속하면
: 됩니다. 따라서 ClientSocket이 접속해야 할 Address에 127.0.0.1을 입
: 력하시면 자기가 만든 서버에 접근할 수 있습니다.
:
: 이제 클라이언트에서 서버에 접속하게 하는 부분입니다. Button의
: OnClick 이벤트에 다음의 코드를 추가합니다.
: void __fastcall TClientForm::ActiveBtnClick(TObject *Sender)
: {
: ClientSocket1->Active = !ClientSocket1->Active;
: if (ClientSocket1->Active)
: ListBox1->Add("서버에 접속 시도합니다.");
: else
: ListBox1->Add("서버와 연결이 끊어졌습니다.");
: }
:
: 그리고 실제로 접속 시도 중인지 알아 보기 위해 ClientSocket의
: OnConnecting 이벤트에 다음의 코드를 추가합니다.
: void __fastcall TClientForm::ClientSocket1Connecting(TObject *Sender,
: TCustomWinSocket *Socket)
: {
: ListBox1->Add("서버에 접속 시도 중입니다.");
: }
:
: 클라이언트가 서버에 완전히 접속 되었는지 알아 보기 위해 ClientSocket의
: OnConnected 이벤트에 다음의 코드를 추가합니다.
: void __fastcall TClientForm::ClientSocket1Connect(TObject *Sender,
: TCustomWinSocket *Socket)
: {
: ListBox1->Add("서버에 접속했습니다.");
: }
:
: 마지막으로 서버와의 접속이 끊어졌는지 알아보기 위해 ClientSocket의
: OnDisconnected 이벤트에 다음의 코드를 추가합니다.
: void __fastcall TClientForm::ClientSocket1Disconnect(TObject *Sender,
: TCustomWinSocket *Socket)
: {
: ListBox1->Add("서버와 연결이 끊어졌습니다.");
: }
:
: 이제 서버와 클라이언트의 기본적인 디자인이 끝났습니다. 서버 프로그
: 램과 클라이언트 프로그램을 각각 실행시키고 서버의 Active 버튼을 먼
: 저 눌러 서버를 활성화 시키고, 클라이언트의 Active 버튼을 눌러 서버
: 로 접속을 시도해 보십시오. 그리고 Active 버튼을 다시 눌러 접속을
: 끊어 보십시오. 접속/해제 메시지가 리스트박스에 이쁘게 나올 겁니다.
:
: 여기까지는 앞으로 할 내용의 기본단계이고.. 이제 오늘 내용의 주제인
: 자동으로 다시 접속하는 법입니다. 클라이언트가 접속 끊으면 별 상관
: 없는데 서버가 죽으면 클라이언트는 다시 접속하려고 하지요. 이걸 자
: 동으로 하게 하는 방법입니다.
:
: 서버가 접속을 끊던 클라이언트가 스스로 접속을 끊던 ClientSocket에는
: OnDisconnected 이벤트가 발생합니다. 자, 이제 단순한 방법 생각해 볼
: 수 있겠죠? '접속이 끊어졌으니 다시 접속시켜 주면 된다. 다시 접속하
: 려면? ClientSocket의 Active=true로 다시 해 주면 된다' 이런 결론이
: 나오게 됩니다. 그래서 다음의 코드와 같이 코딩하게 됩니다.
: void __fastcall TClientForm::ClientSocket1Disconnect(TObject *Sender,
: TCustomWinSocket *Socket)
: {
: ListBox1->Add("서버와 연결이 끊어졌습니다.");
: ClientSocket1->Active = true;
: }
:
: 그러나 이 코딩의 결과를 말씀드리자면 백날 해도 접속이 안 됩니
: 다. 물론 Active=true로 설정하는 것 자체야 되죠. 그러나 그렇게 해도
: 접속은 안 됩니다(접속 시도 중 메시지는 뜹니다). 왜 안되느냐?
:
: 볼랜드 온라인 도움말에서 OnDisconnedted 이벤트에 대해 찾아 보면
: OnDisconnected 이벤트는 클라이언트 소켓이 서버 소켓과의 연결을 닫기
: 전에 발생한다고 되어 있습니다. 이때 소켓 프로퍼티 Active=false로
: 설정된 뒤에 발생하지만 연결 그 자체는 아직 완전히 닫히지 않은 상태
: 라고 설명되어 있습니다. 즉 OnDisconnected 이벤트 핸들러 함수를 완
: 전히 종료한 뒤에야 연결을 완전히 닫아 버리기 때문에 OnDisconnect 안
: 에서 아무리 Active=true를 해 줘도 OnDisconnect 종료 순간 다시 닫혀
: 버리는 것입니다.
:
: 여기서 이렇게 생각해 볼 수도 있습니다. '지가 연결 닫는 걸 기다리지
: 말고 내가 먼저 연결 닫으면 안되느냐. Socket의 메써드 중에 Close()가
: 있던데 Active=true로 하기 전에 Socket->Close()를 실행하면 될 것 아닌
: 가'. 아주 좋은 아이디어입니다. 박수~~ (캬캬.. 사실은 제가 해 본 방법
: 이죠) 이 코드는 아마 다음과 같겠죠?
: void __fastcall TClientForm::ClientSocket1Disconnect(TObject *Sender,
: TCustomWinSocket *Socket)
: {
: ListBox1->Add("서버와 연결이 끊어졌습니다.");
: Socket->Close(); // 또는 Socket->Connected = false;
: ClientSocket1->Active = true;
: }
:
: 그런데 이 코드 역시 결과를 말씀드리자면 안됩니다(으하하.. 좋은 아이
: 디어라고 박수 쳐 놓고 안된다고 뻔뻔스럽게 말하는 나는 누구일까..)
: Socket->Close()를 실행하면 Socket의 OnDisconnected 이벤트가 다시 발
: 생합니다(왜 다시 발생하는지 자세한 과정은 생략합니다. 저도 컴포넌트
: 제작을 공부하면서 알게 된 것이기에 추측만 할 수 있을 뿐입니다).
: Socket->Close() 하자 마자 OnDisconnected 발생해서 또 Socket->Close(),
: 또 OnDisconnected 발생, Socket->Close().. 이런식으로 무한히 Close()
: 만 실행하다가 결국엔 리소스 부족으로 PC가 맛이 가게 됩니다.
:
: 그래서 다른 방법을 시도해 봐야겠지요. 저는 이 사태를 해결하기 위해
: 타이머를 사용했습니다(물론 제가 사용한 방법이 최선이라고 생각지는
: 않습니다. 더 좋은 방법을 알고 계시는 분이 계시면 정보 교류를 위해
: 수고를 감수하시고 제시해 주시면 고맙겠습니다).
:
: 타이머를 클라이언트 폼 위에 갖다 놓고 이름을 RetryTimer로 바꿉니다.
: 주기는 빨라도 상관없지만 1초(Interval=1000)면 적당합니다. 그리고
: 타이머의 Enabled=false로 해 주고, 이벤트에 다음의 코드를 추가합니다.
: void __fastcall TClientForm::RetryTimerTimer(TObject *Sender)
: {
: if (!ClientSocket1->Socket->Connected)
: {
: RetryTimer->Enabled = false;
: ClientSocket1->Active = true;
: }
: }
:
: OnDisconnected 이벤트 핸들러 함수에 대해서도 수정해야겠지요?
: void __fastcall TClientForm::ClientSocket1Disconnect(TObject *Sender,
: TCustomWinSocket *Socket)
: {
: ListBox1->Add("서버와 연결이 끊어졌습니다.");
: RetryTimer->Enabled = true;
: }
:
: 타이머 이벤트에서 if 조건문을 사용한 것은 혹시라도 있을지 모르는
: 타이머 이벤트가 발생했을 때 아직 접속 중인 상태에서 접속 시도하려
: 고 하는 것을 피하기 위해서입니다. 아직 접속 중이라면 접속이 완전히
: 해제될 때까지 계속 타이머 이벤트를 발생시키다가, 접속이 완전히 끊
: 어지면 타이머를 중단시키고 재접속하려고 합니다.
:
: 이제 자동 재접속 기능이 만들어졌습니다. 프로젝트를 저장하고 다시
: 컴파일해서 실행해 봅니다. 서버를 동작시키고 클라이언트를 서버에 접
: 속시킨 후 서버를 비활성화 시킵니다. 그러면 클라이언트가 서버에 재
: 접속을 시도해야 하는데.. Asynchronous 에러가 한번 뜨고는 더이상 접
: 속을 시도하지 않을 겁니다. 뭐가 문제일까요?
:
: 서버가 죽은 후 다시 살아나기까지는 시간이 좀 걸립니다. 그런데 클라
: 이언트에서는 서버와의 접속이 끊어진 후 다시 접속시도하기까지는 길
: 어도 절대 2초를 넘지 않습니다. 그 시간에 서버가 살아나면 다행이지
: 대개의 경우 여전히 죽어 있는 상태죠. 그래서 서버와의 연결이 실패하
: 면 다시 접속을 해 주어야 하는데.. 지금까지의 코딩 내용에는 그런 것
: 이 없었지요? OnDisconnected는 연결되어 있는 것이 끊어졌을 때 발생
: 하는 것이니까 안되고..
:
: 자, 그래서 ClientSocket의 OnError 이벤트를 건드리는 겁니다. 다음의
: 내용은 OnError 이벤트의 코드입니다.
: void __fastcall TMonitorMainForm::ClientSocket1Error(TObject *Sender,
: TCustomWinSocket *Socket, TErrorEvent ErrorEvent, int &ErrorCode)
: {
: if ( (ErrorEvent == eeConnect) )
: {
: ListBox1->Add("접속시도가 실패했습니다.");
: ErrorCode = 0;
: RetryTimer->Enabled = true;
: }
: }
:
: 소켓을 사용하던 중에 에러가 발생하는 ErrorEvent에 에러가 발생한
: 이벤트가 저장되어 오고, ErrorCode에 에러의 코드가 넘겨져 옵니다.
: 클라이어트가 서버에 재접속할 때 발생했던 Asynchronous 에러는 클
: 라이언트가 서버에 접속을 할 때 발생한 에러입니다. 그래서 ErrorEvent
: 에는 eeConnect라는 값이 저장되어 있습니다. 자세한 내용은 도움말
: 을 참조하시기 바랍니다. 따라서 접속시도 중에 에러가 발생한 경우
: 에러 이벤트가 eeConnect인지 검사해 보고 만일 맞으면 재접속 시도
: 를 다시 하면 됩니다(RetryTimer를 다시 동작시키는 거죠). 이때
: ErrorCode에 에러의 ID가 저장되어 있는데 그냥 둘 경우 화면에 계속
: Asynchronous 에러 대화상자가 뜹니다. 이것을 막기 위해서 '에러가
: 발생했을 때의 처리를 해 줬으니 에러코드를 무시해라'라는 뜻으로
: ErrorCode=0으로 해 줍니다. 그러면 더이상 에러 대화상자가 뜨지
: 않을 겁니다.
:
: 이제 소켓이 끊어진 경우의 자동 재접속에 대한 내용을 모두 설명드
: 렸습니다. 다시 테스트 해 봅시다.
: 1. 서버, 클라이언트 프로그램을 각각 실행
: 2. 서버 소켓을 활성화 한 후 클라이언트 소켓을 활성화
: 3. 서버 소켓을 비활성화하고 클라이언트에서 접속시도 실패 메시
: 지가 발생하는지 확인
: 4. 클라이언트에서 접속시도 실패 메시지가 발생하면 서버 소켓을
: 다시 활성화 한 후 클라이언트에서 재접속했는지 확인
: 5. 모두 잘 되었으면 박수~
:
: 남은 것은 재접속 기능을 좀 더 깔끔하게 다듬는 것입니다. 단순히
: 재접속 타이머만 동작시켜 바로 재접속시키는 것은 사용자가 더이
: 상 접속을 하고 싶지 않다는 의사를 무시하는 것이 됩니다. 그래서
: 자동 재접속을 하기 전에 'x초 후에 재접속합니다' 메시지박스를
: 화면에 보여주고 재접속 의사를 물어 보는 것이 좋죠.
: 어떻게 다듬느냐는 여러분의 미적 감각에 달렸습니다. ^_^
:
: 제가 올리는 팁의 내용은 별로 어렵지 않고 전문가 분들이 쉽게 구현
: 할 수 있는 내용들입니다(거뤠~! 무식을 팍팍 드러내는 거야~ T.T).
: 때문에 제가 올리는 내용이 최선의 방법이라는 보장은 절대 없으니까
: 제가 알려 드리는 내용에 구애받지 마시고, 더 좋은 방법이 있으면
: 그 방법을 쓰시고 같이 정보를 교류하였으면 합니다.
:

+ -

관련 글 리스트
13632 [팁 ] 소켓 접속이 끊어졌을 때 다시 연결 나그네 2606 2001/12/13
13634     팁은 [Tip'n Tricks]로... 김백일 1458 2001/12/13
13637         팁란에도 올렸어요..(냉무) 나그네 763 2001/12/13
Google
Copyright © 1999-2015, borlandforum.com. All right reserved.