CSAPP(11)Network Programming

글 목록
  • The Client-Server Programming Model
  • Networks
  • The Global IP Internet
  • DNS
  • The Sockets Interface
  • client
  • server
  • accept
  • Web Servers
  • The Tiny Web Server

  • The Client-Server Programming Model
    Networks
    hub 는 받 은 frame 을 모든 port 에 보 냅 니 다. 그러나 bridge 는 학습 을 통 해 데 이 터 를 목적 포트 에 만 보 냅 니 다 (또는 버 립 니 다).
    The Global IP Internet
    네트워크 에 서 는 일반적으로 big - endian 으로 데 이 터 를 전송 합 니 다.
    DNS
    struct{
    	char	*h_name;//official domain name
    	char	**h_aliases;//Null-terminated array of domain names
    	int		h_addrtype;//host address type
    	int		h_length;
    	char	**h_addr_list;
    }
    
    #include 
    // return non-NULL pointer is OK,NULL pointer on error with h_error set
    struct hostent *gethostbyname(const char *name);
    struct hostent *gethostbyaddr(const char *addr,int len,0);
    

    The Sockets Interface
    client
    클 라 이언 트 에서 서버 에 연결 할 때 socket 을 사용 하여 만 듭 니 다.
    #include 
    #include 
    //return nonnegative descriptor if OK,-1 on error
    int socket(int domain,int type,int protocol);
    //return 0 if ok,-1 on error
    int connect(int sockfd,struct sockaddr *serv_addr,int addrlen);
    

    클 라 이언 트 가 서버 로 연결 하 는 여러 단계 가 함께 포 장 될 수 있 습 니 다.
    int open_clientfd(char *hostname,int port){
    	int clientfd;
    	struct hostent *hp;
    	struct socketaddr_in serveraddr;
    	if((clientfd==socket(AF_INET,SOCK_STREAM,0))<0)
    		return -1;//check errno for cause
    	if((hp=gethostbyname(hostname))==NULL)
    		return -2;
    	bzero((char *)&serveraddr,sizeof(serveraddr));
    	serveraddr.sin_family=AF_INET;
    	bcopy((char *)hp->h_addr_list[0],(char *)&serveraddr.sin_addr.s_addr,hp->h_length);
    	serveraddr.sin_port=htons(port);//  big-endian
    	if(connect(clientfd,(SA *)&serveraddr,sizeof(serveraddr))<0)
    		return -1;
    	return clientfd;
    }
    

    server
    서버 에 서 는 일반적으로 bind 를 사용 하여 포트 를 지정 합 니 다 (addrlen 의 값 은 size of (socketadr in). listen 을 통 해 active socket 을 listening socket 으로 전환 합 니 다 (그 중에서 backlog 는 최대 대기 수 를 말 합 니 다).
    #include 
    int bind(int sockfd,struct sockaddr *my_addr,int addrlen);
    int listen(int sockfd,int backlog);
    

    마찬가지 로 서버 감청 포트 도 하나의 함수 로 합 칠 수 있다.
    int open_listenfd(int port){
    	int listenfd,optval=1;
    	struct sockaddr_in serveraddr;
    	if((listenfd=socket(AF_INET,SOCK_STREAM,0))<0)
    		return -1;
    	if(setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,(const void *)&optval,sizeof(int))<0)
    		return -1;
    	bzero((char *)&serveraddr,sizeof(serveraddr));
    	serveraddr.sin_family=AF_INET;
    	serveraddr.sin_addr.s_addr=htonl(INADDR_ANY);//           
    	serveraddr.sin_port=htons((unsigned short)port);
    	if(bind(listenfd,(SA*)&serveraddr,sizeof(serveraddr))<0)
    		return -1;
    	if(listen(listenfd,LISTENQ)<0)
    		return -1;
    	return listenfd;
    }
    

    accept
    위 는 server 에 있어 서 자원 을 분배 한 셈 입 니 다. 진정 으로 client 가 server 를 연결 할 수 있 도록 하려 면 server 가 아래 의 accept 함 수 를 호출 해 야 합 니 다.
    #include 
    //return nonnegative connected descriptor if ok,-1 on error
    int accept(int listenfd,struct sockaddr *addr,int *addrlen);
    

    server 가 accept 를 호출 하면 대기 상태 (아래 그림 1) 에 들 어가 클 라 이언 트 가 요청 (아래 그림 2) 을 한 다음 연결 을 만 듭 니 다. server 의 accept 와 client 의 connect 는 모두 돌아 갑 니 다 (아래 그림 3). 각각 descriptor 를 받 아 다음 작업 에 사용 합 니 다.
    #include 
    #include 
    #include 
    #include 
    
    int Open_listenfd(int port){
        int out,optval=1;
        struct sockaddr_in serveraddr;
        if((out=socket(AF_INET,SOCK_STREAM,0))<0){
            return -1;
        }
        if(setsockopt(out,SOL_SOCKET,SO_REUSEADDR,(const void *)&optval,sizeof(int ))<0){
            return -1;
        }
        bzero((char *)&serveraddr,sizeof(serveraddr));
        serveraddr.sin_family=AF_INET;
        serveraddr.sin_addr.s_addr=htonl(INADDR_ANY);
        serveraddr.sin_port=htons((unsigned short )port);
        if(bind(out,(__CONST_SOCKADDR_ARG)&serveraddr,sizeof(serveraddr))<0){
            return -1;
        }
        if(listen(out,LISTENQ)<0)
            return -1;
        return out;
    }
    void doit(int fd){
        int is_static;
        struct stat sbuf;
        char buf[MAXLINE],method[MAXLINE],uri[MAXLINE],version[MAXLINE];
        char filename[MAXLINE],cgiargs[MAXLINE];
        rio_t rio;
        rio_readinitb(&rio,fd);
        rio_readlineb(&rio,buf,MAXLINE);
        sscanf(buf,"%s %s %s",method,uri,version);
        if(strcasecmp(method,"GET")){
            clienterror(fd,method,"501","Not Implemented","does not implement this method")
        }
    }
    int main(int argc,char **argv) {
        int listenfd,connfd,port,clientlen;
        struct sockaddr_in clientaddr;
        if(2!=argc){
            fprintf(stderr,"usage:%s 
    "
    ,argv[0]); return 1; } port=atoi(argv[1]); listenfd=Open_listenfd(port); while (1){ clientlen=sizeof(clientaddr); connfd=accept(listenfd,&clientaddr,&clientlen); doit(connfd); close(connfd); } }

    Web Servers
    네트워크 서 비 스 를 디 버 깅 할 때 telnet 명령 을 통 해 클 라 이언 트 가 http header 에 Host 가 있 는 것 을 모 의 할 수 있 습 니 다. 이것 은 프 록 시 서버 가 정적 자원 에 대한 캐 시 사용 여 부 를 쉽게 찾기 위해 서 입 니 다. server 는 동적 자원 에 대해 서 는 Common Gateway Interface 를 사용 하여 처리 합 니 다. 이 때 server 는 fork 프로 세 스 를 사용 하여 dup 2 를 사용 하여 재 설정 합 니 다.standard output (cgi 가 출력 하 는 곳 이기 때문에) 에 환경 변 수 를 설정 한 다음 execve 를 호출 하여 * / cgi - bin / adder 를 실행 합 니 다. cgi 는 getenv * 를 통 해 설정 한 환경 변 수 를 가 져 옵 니 다. 이것 이 바로 CGI 표준 입 니 다.
  • QUERY_STRING
  • SERVER_PORT
  • REQUEST_METHOD
  • REMOTE_HOST
  • REMOTE_ADDR
  • CONTENT_TYPE
  • CONTENT_LENGTH

  • cgi 프로그램 으로서 콘 텐 츠 를 되 돌려 주 는 것 외 에 콘 텐 츠 - type, 콘 텐 츠 - length 등 정 보 를 되 돌려 주어 야 합 니 다. sprintf 를 사용 하여 데 이 터 를 저장 하고 printf 를 사용 하여 stantard out 을 기록 하 며 마지막 으로 fflush 캐 시 를 사용 할 수 있 습 니 다.
    The Tiny Web Server
    글 에서 예 시 를 제 시 했 지만 진정한 server 에 있어 각종 의외 의 상황 을 처리 해 야 한다 고 지적 했다. 예 를 들 어 클 라 이언 트 가 링크 를 끊 어서 발생 하 는 SIGPIPE 등 이다.

    좋은 웹페이지 즐겨찾기