socket 네트워크 프로 그래 밍 의 2 (리 턴 프로그램 인 스 턴 스)

8005 단어
내 이전 글 socket 네트워크 프로 그래 밍 중 하나 (TCP 소켓 API) 에서 ipv 4 의 소켓 데이터 구조 와 tcp 소켓 관련 API 를 소개 했다. 이 글 은 이전 에 소 개 된 API 를 이용 하여 서버 리 턴 프로그램 을 써 서 TCP 소켓 API 에 대한 이 해 를 강화 할 것 이다. 소스 주소
주의!github 의 소스 코드 는 제 가 UNIX 네트워크 프로 그래 밍 을 공부 하 는 과정 에서 책의 소스 코드 를 실현 하 는 것 입 니 다. 일련의 소스 코드 입 니 다. 보완 중 입 니 다. echo Program 폴 더 의 코드 와 이 블 로그 가 대응 합 니 다. 다운로드 한 후에 'README 파일' 을 먼저 보 세 요.코드 중의 일부 공공 함수, 예 를 들 어 오류 처리 와 소포 함 수 는 모두 Public 폴 더 아래 에 놓 여 있 습 니 다.패키지 함 수 는 일부 기본 함수 가 오류 처리 조작 을 포함 하고 있 으 므 로 여러분 은 한눈 에 알 수 있 습 니 다.
조심해!!!모든 소스 코드, 저 는 xcodeIDE 를 사용 하여 이 루어 졌 습 니 다. IOS 프로그램 원숭이 하 나 를 사용 할 수 없습니다. 여러분 은 다른 개발 환경 에서 뛰 어야 합 니 다. 스스로 이식 하 십시오!모든 소스 코드, 저 는 xcodeIDE 를 사용 하여 이 루어 졌 습 니 다. IOS 프로그램 원숭이 하 나 를 사용 할 수 없습니다. 여러분 은 다른 개발 환경 에서 뛰 어야 합 니 다. 스스로 이식 하 십시오!모든 소스 코드, 저 는 xcodeIDE 를 사용 하여 이 루어 졌 습 니 다. IOS 프로그램 원숭이 하 나 를 사용 할 수 없습니다. 여러분 은 다른 개발 환경 에서 뛰 어야 합 니 다. 스스로 이식 하 십시오!
클 라 이언 트 코드
 int sockfd;
 struct sockaddr_in sockaddr;

 sockfd = Socket(AF_INET, SOCK_STREAM, 0);
 bzero(&sockaddr, sizeof(sockaddr));
 sockaddr.sin_port = htons(9999);
 sockaddr.sin_family = AF_INET;
 inet_pton(AF_INET,"127.0.0.1",&sockaddr.sin_addr);
    
 Connect(sockfd, (struct sockaddr *)&sockaddr, sizeof(sockaddr));
 str_cli(stdin, socked);
  • 상기 코드 는 먼저 소켓 설명자 와 IPV 4 의 소켓 구 조 를 설명 한 다음 에 Socket 함 수 를 호출 하여 sockfd 에 값 을 부여 합 니 다.
  • bzero 는 sockaddr 의 값 을 0 으로 설정 하고 sockaddr 를 사용 하기 전에 bzero 를 호출 해 야 한다 고 밝 혔 다.
  • 서버 의 포트 번 호 를 9999 로 설정 하고 htons 는 호스트 의 서브 절 차 를 네트워크 서브 절 차 를 바 꾸 겠 다 고 밝 혔 다.설정 sinfamily 의 협의 족 은 AFINET. 점 10 진법 의 주 소 를 sockaddr. sin 으로 변환 합 니 다.addr 구조의 주소.이렇게 해서 우 리 는 서버 에 연결 할 소켓 설정 을 완성 했다.
  • connection 함 수 를 호출 하여 서버 에 연결 하고 connect 함수 가 성공 적 으로 돌아 오 면 TCP 세 번 의 악수 가 완료 되 었 음 을 나타 낸다.완 료 된 후에 통신 을 할 수 있 습 니 다. connect 함수 가 되 돌아 오지 않 으 면 프로그램 이 connect 호출 에 막 힐 것 입 니 다.
  • 마지막 으로 우리 호출, strcli 함수, 서버 와 통신, str 설명cli 함수.
  • void str_cli(FILE *fd,int sockfd){
        char sendline[MAXLINE],recvline[MAXLINE];
        ssize_t status;
        while ( Fgets(sendline, MAXLINE, fd)!=NULL  ) {
            Writen(sockfd, sendline, strlen(sendline));
            status = read(sockfd, recvline, MAXLINE);
            if (  status< 0  ) {
                err_sys("read error");
            }
            puts(recvline);
        }
    }
    
  • 우 리 는 while 순환 에서 말 하면 while 에서 먼저 fgets 함 수 를 호출 하여 사용자 가 표준 입력 을 기다 리 고 있 습 니 다. 만약 에 사용자 가 계속 입력 하지 않 으 면 프로그램 이 막 힐 것 입 니 다. 만약 에 사용자 가 한 줄 을 입력 하면 Enter 키 로 끝나 고 fget 함수 가 차단 을 해제 한 다음 에 돌아 갑 니 다.
  • fget 함수 가 입력 을 받 으 면 입력 한 데 이 터 는 sendline 배열 에 존재 합 니 다. write 함 수 를 호출 하면 데 이 터 를 보 냅 니 다. 이때 write 함수 가 막 히 고 발송 이 완료 되면 차단 을 해제 하고 함수 가 돌아 갑 니 다.
  • 데이터 전송 에 성공 하면 read 함 수 를 호출 하여 서버 에서 보 낸 데 이 터 를 받 는 데 사 용 됩 니 다. 이때 도 프로그램 이 막 히 고 데 이 터 를 받 으 면 데 이 터 를 인쇄 합 니 다.

  • 이로부터 strcli 함수 가 완성 되 었 습 니 다. 정상 적 인 상황 에서 이 프로그램 은 아무런 문제 가 없 지만 예외 적 인 상황 이 있 습 니 다. 프로그램 이 fgets 함수 에 막 히 는 동안 서버 프로그램 이 무 너 지면 서버 프로그램 은 연결 을 닫 아 달 라 는 요청 을 보 냅 니 다. 이때 클 라 이언 트 프로그램 은 모 릅 니 다.그 다음 에 사용자 가 텍스트 를 입력 하면 fgets 가 차단 을 해제 하고 write 함 수 를 호출 합 니 다. 서버 가 연결 을 닫 았 기 때문에 write 함 수 는 기록 에 성공 하지 못 할 것 입 니 다. 이렇게 찍 은 문 제 는 클 라 이언 트 가 fgets 에 차단 되 어 서버 의 상황 을 제때에 알 지 못 하기 때문에 이렇게 쓴 프로그램 에 문제 가 있 습 니 다. 따라서 우 리 는 selece 함수 로 이 문 제 를 해결 하 는 것 을 고려 합 니 다.다음은 strcli 의 select 버 전.
     void str_cli(FILE *fd,int sockfd){
        int maxfdp1;
        fd_set rset;
        char sendline[MAXLINE],recvline[MAXLINE];
        ssize_t readlen;
        
        // fd_set     0
        FD_ZERO(&rset);
        
        for (; ; ) {
            //FD_SET            
            FD_SET(fileno(fd),&rset);
            FD_SET(sockfd,&rset);
            // maxfdp1      +1          0   
            maxfdp1 = ((int)fmaxf(fileno(fd), sockfd)) + 1;
            Select(maxfdp1, &rset, NULL, NULL, NULL);
            
            //FD_ISSET     ,  sockfd    
            if ( FD_ISSET(sockfd,&rset) ) {
                readlen = read(sockfd, recvline, MAXLINE);
                //         0             
                if ( readlen==0 ) {
                    err_quit("server terminal");
                }
                if ( readlen < 0 ) {
                    err_sys("read error");
                }
                
                //          
                puts(recvline);
            }
            
            if ( FD_ISSET(fileno(fd),&rset) ) {
                if ( Fgets(sendline, MAXLINE, fd)==NULL ) {
                    return;
                }
                
                Writen(sockfd, sendline, MAXLINE);
            }
        }
    }
         
    
  • 우선 FD 호출ZERO 함수, rset 를 0 으로 설정 합 니 다. fd 를 사용 하고 있 습 니 다.set 구 조 를 구성 할 때 이 함 수 를 호출 해 야 합 니 다.
  • for 순환 에서 우 리 는 FD 를 호출 합 니 다.SET 함수, 우리 가 관심 을 가 져 야 할 설명 자 를 설정 합 니 다.
  • 그 다음 에 select 함 수 를 호출 하여 I \ O 재 활용 을 합 니 다. select 함 수 를 주의 하고 설명 자 를 전달 할 때 반드시 최대 설명자 + 1, select 의 마지막 매개 변 수 는 기다 리 는 시간 입 니 다. 기다 리 는 시간 에 프로그램 이 막 혔 습 니 다.NULL 에 들 어 오 면 무한 기다 림 을 표시 합 니 다.
  • FD_ISSET 는 어떤 설명자 가 활성화 되 었 는 지 판단 하 는 데 사 용 됩 니 다. sockfd 라면 읽 기 를 호출 합 니 다. 서버 에서 연결 을 닫 는 것 을 보 내 면 바로 감지 할 수 있 습 니 다.

  • 우 리 는 select 함수 로 해결 할 수 있 습 니 다. fgets 함수 가 차단 상태 에서 서버 가 연결 을 닫 았 을 때 클 라 이언 트 프로그램 이 즉시 알 수 없 는 상황 입 니 다.
    서버 코드
        int listenfd,connfd;
        pid_t childPid;
        socklen_t clilen;
        struct sockaddr_in cliaddr,seraddr;
        
        listenfd = socket(AF_INET, SOCK_STREAM, 0);
        
        bzero(&seraddr, sizeof(seraddr));
        seraddr.sin_port = htons(9999);
        seraddr.sin_addr.s_addr = htonl(INADDR_ANY);
        seraddr.sin_family = AF_INET;
        
        Bind(listenfd, (struct sockaddr *)&seraddr, sizeof(seraddr));
        
        Listen(listenfd,LISTENQ);
        
        Sigal(SIGCHLD, sig_child);
        
        for ( ; ; ) {
            clilen = sizeof(cliaddr);
            connfd = Accept(listenfd, (struct sockaddr *)&cliaddr, &clilen);
          
            //  0     
            if ( ( childPid = Fork() ) == 0 ) {
                printf("     :%d",getpid());
                Close( listenfd );
                str_echo(connfd);
                exit(0);
            }
            
            Close(connfd); /*           */
        }
    
  • 서버 프로그램 은 먼저 bind 함 수 를 호출 하여 서버 의 포트 번 호 를 설정 하고 ip 주 소 는 INADDR 로 설정 합 니 다.ANY 는 IP 주 소 를 표시 합 니 다.
  • 그리고 listen 함 수 를 호출 하여 감청 에 사용 합 니 다. listen 함 수 는 지난 글 에서 소개 되 었 습 니 다. 더 이상 말 하지 않 습 니 다.
  • signal 함 수 를 호출 하여 신 호 를 처리 합 니 다. 이 함 수 를 사용 해 야 하 는 이 유 는 하위 프로 세 스 가 끝 날 때 커 널 이 프로그램 에 중단 신 호 를 보 내 고 프로그램 에 그의 하위 프로 세 스 가 종료 되 었 음 을 알려 줍 니 다. 우 리 는 이 신 호 를 포착 하여 하위 프로 세 스 의 자원 을 회수 해 야 합 니 다. 그렇지 않 으 면 자원 의 낭비 로 비 출 수 있 습 니 다.
  • for 순환 에서 accept 함 수 는 되 돌아 오기 전에 계속 막 혔 습 니 다. 이 함 수 는 느 린 시스템 호출 에 속 합 니 다. 느 린 시스템 호출 은 영원히 막 힐 수 있다 는 뜻 입 니 다. 이때 하위 프로 세 스 가 종료 되면 신호 처리 함수 가 없 으 면 EINTR 의 오 류 를 되 돌려 줍 니 다.accept 가 돌아 온 후 세 번 의 악수 가 끝났다 고 밝 혔 다.
  • 우리 의 모든 연결 은 하위 프로 세 스 로 처 리 됩 니 다. 하위 프로 세 스 는 부모 프로 세 스 의 복사 이기 때문에 소켓 설명 자 를 닫 고 str 를 호출 해 야 합 니 다.echo 함수, 클 라 이언 트 가 보 낸 데 이 터 를 처리 하 는 데 사 용 됩 니 다. 다음은 str 입 니 다.echo 함수.
  • void str_echo(int sockfd)
    {
        ssize_t n;
        char buf[MAXLINE];
        
    again:
        while ( (n = read(sockfd, buf, MAXLINE)) > 0 ) {
            Writen(sockfd, buf, n);
        }
        
        if ( n < 0 && errno == EINTR )
            goto again;
        else if (n < 0)
            err_sys("str_echo: read error");
    }
    
  • 세 번 의 악수 가 끝 난 후에 우 리 는 먼저 read 함 수 를 호출 하여 클 라 이언 트 에서 보 낸 데 이 터 를 읽 고 데 이 터 를 읽 으 면 데 이 터 를 그대로 보 냅 니 다.이것 이 바로 프로그램의 리 턴 기능 이다.signal 함수 집합, 그리고 sigchild 가 말 하지 않 았 습 니 다.
  • void sig_child(int signo)
    {
        pid_t pid;
        int stat;
        
        printf("      :%d",getpid());
        
        printf("signal num = %d",signo);
        
        //pid = wait(&stat);
        //waitpid  
        //printf("child %d terminated
    ",pid); while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0 ) { printf("child %d terminated
    ",pid); } }
  • 커 널 이 프로 세 스에 신 호 를 보 내 면 sigchild 함 수 는 바로 우리 의 신호 처리 함수 의 리 셋 함수 입 니 다. waitpid 함 수 를 호출 하여 하위 프로 세 스 를 회수 합 니 다.
  • 좋은 웹페이지 즐겨찾기