<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    jinfeng_wang

    G-G-S,D-D-U!

    BlogJava 首頁 新隨筆 聯系 聚合 管理
      400 Posts :: 0 Stories :: 296 Comments :: 0 Trackbacks

      當你進入 UNIX 的神秘世界后,立刻會發現越來越多的東西難以理解。對于大多數人來說,BSD socket 的概念就是其中一個。這是一個很短的教程來解釋他們是什么、他們如何工作并給出一些簡單的代碼來解釋如何使用他們。

    類比 (什么是 socket ?) socket 是進行程序間通訊(IPC)的 BSD 方法。這意味著 socket 用來讓一個進程和其他的進程互通信息,就象我們用電話來和其他的人交流一樣。

    用電話來比喻是很恰當的,我們在后面將一直用電話這個概念來描敘 socket 。

    裝上你的新電話(怎樣偵聽?) 一個人要能夠收到別人打給他的電話,首先他要裝上一門電話。同樣,你必須先建立 socket 以偵聽線路。這個過程包含幾個步驟。首先,你要建立一個新的 socket,就象先裝上電話一樣。socket() 命令就完成這個工作。
    因為 sockets 有幾種類型,你要注明你要建立什么類型的。你要做一個選擇是 socket 的地址格式。如同電話有音頻和脈沖兩種形式一樣,socket 有兩個最重要的選項是 AF_UNIX 和 IAF_INET。AF_UNIX 就象 UNIX 路徑名一樣識別 sockets。這種形式對于在同一臺機器上的 IPC 很有用。而 AF_INET 使用象 192.9.200.10 這樣被點號隔開的四個十進制數字的地址格式。除了機器地址以外,還可以利用端口號來允許每臺機器上的多個 AF_INET socket。我們這里將著重于 AF_INET 方式,因為他很有用并廣泛使用。

    另外一個你必須提供的參數是 socket 的類型。兩個重要的類型是 SOCK_STREAM 和 SOCK_DGRAM。SOCK_STREAM 表明數據象字符流一樣通過 socket 。而 SOCK_DGRAM 則表明數據將是數據報(datagrams)的形式。我們將講解 SOCK_STREAM sockets,他很常見并易于使用。

    在建立 socket 后,我們就要提供 socket 偵聽的地址了。就象你還要個電話號碼來接電話一樣。bind() 函數來處理這件事情。

    SOCK_STREAM sockets 讓連接請求形成一個隊列。如果你忙于處理一個連接,別的連接請求將一直等待到該連接處理完畢。listen() 函數用來設置最大不被拒絕的請求數(一般為5個)。一般最好不要使用 listen() 函數。

    下面的代碼說明如何利用 socket()、 bind() 和 listen() 函數建立連接并可以接受數據。


    /* code to establish a socket; originally from bzs@bu-cs.bu.edu
    */

    int establish(unsigned short portnum)
    { char myname[MAXHOSTNAME+1];
    int s;
    struct sockaddr_in sa;
    struct hostent *hp;

    memset(&sa, 0, sizeof(struct sockaddr_in)); /* clear our address */
    gethostname(myname, MAXHOSTNAME); /* who are we? */
    hp= gethostbyname(myname); /* get our address info */
    if (hp == NULL) /* we don't exist !? */
    return(-1);
    sa.sin_family= hp->h_addrtype; /* this is our host address */
    sa.sin_port= htons(portnum); /* this is our port number */
    if ((s= socket(AF_INET, SOCK_STREAM, 0)) < 0) /* create socket */
    return(-1);
    if (bind(s,&sa,sizeof(struct sockaddr_in)) < 0) {
    close(s);
    return(-1); /* bind address to socket */
    }
    listen(s, 3); /* max # of queued connects */
    return(s);
    }
    在建立完 socket 后,你要等待對該 socket 的調用了。accept() 函數為此目的而來。
    調用 accept() 如同在電話鈴響后提起電話一樣。Accept() 返回一個新的連接到調用方
    的 socket 。

    下面的代碼演示使用是個演示。


    /* wait for a connection to occur on a socket created with establish()
    */
    int get_connection(int s)
    { int t; /* socket of connection */

    if ((t = accept(s,NULL,NULL)) < 0) /* accept connection if there is one */
    return(-1);
    return(t);
    }
    和電話不同的是,在你處理先前的連接的時候,你還可以接受調用。為此,一般用 fork
    來處理每個連接。下面的代碼演示如何使用 establish() 和 get_connection() 來處理
    多個連接。


    #include /* obligatory includes */
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include

    #define PORTNUM 50000 /* random port number, we need something */

    void fireman(void);
    void do_something(int);

    main()
    { int s, t;

    if ((s= establish(PORTNUM)) < 0) { /* plug in the phone */
    perror("establish");
    exit(1);
    }

    signal(SIGCHLD, fireman); /* this eliminates zombies */

    for (;;) { /* loop for phone calls */
    if ((t= get_connection(s)) < 0) { /* get a connection */
    if (errno == EINTR) /* EINTR might happen on accept(), */
    continue; /* try again */
    perror("accept"); /* bad */
    exit(1);
    }
    switch(fork()) { /* try to handle connection */
    case -1 : /* bad news. scream and die */
    perror("fork");
    close(s);
    close(t);
    exit(1);
    case 0 : /* we're the child, do something */
    close(s);
    do_something(t);
    exit(0);
    default : /* we're the parent so look for */
    close(t); /* another connection */
    continue;
    }
    }
    }

    /* as children die we should get catch their returns or else we get
    * zombies, A Bad Thing. fireman() catches falling children.
    */
    void fireman(void)
    {
    while (waitpid(-1, NULL, WNOHANG) > 0)
    ;
    }

    /* this is the function that plays with the socket. it will be called
    * after getting a connection.
    */
    void do_something(int s)
    {
    /* do your thing with the socket here
    :
    :
    */
    }
    撥號 (如何調用 socket) 現在你應該知道如何建立 socket 來接受調用了。那么如何調用呢?和電話一樣,你要先有個電話。用 socket() 函數來完成這件事情,就象建立偵聽的 socket 一樣。

    在給 socket 地址后,你可以用 connect() 函數來連接偵聽的 socket 了。下面是一段代碼。


    int call_socket(char *hostname, unsigned short portnum)
    { struct sockaddr_in sa;
    struct hostent *hp;
    int a, s;

    if ((hp= gethostbyname(hostname)) == NULL) { /* do we know the host's */
    errno= ECONNREFUSED; /* address? */
    return(-1); /* no */
    }

    memset(&sa,0,sizeof(sa));
    memcpy((char *)&sa.sin_addr,hp->h_addr,hp->h_length); /* set address */
    sa.sin_family= hp->h_addrtype;
    sa.sin_port= htons((u_short)portnum);

    if ((s= socket(hp->h_addrtype,SOCK_STREAM,0)) < 0) /* get socket */
    return(-1);
    if (connect(s,&sa,sizeof sa) < 0) { /* connect */
    close(s);
    return(-1);
    }
    return(s);
    }
    這個函數返回一個可以流過數據的 socket 。

    談話(如何通過 sockets 交談)
    好了,你在要傳輸數據的雙方建立連接了,現在該傳輸數據了。read() 和 write() 函數來
    處理吧。除了在 socket 讀寫和文件讀寫中的一個區別外,和處理一般的文件一樣。區別是
    你一般不能得到你所要的數目的數據。所以你要一直循環到你需要的數據的到來。一個簡單
    的例子:將一定的數據讀到緩存。


    int read_data(int s, /* connected socket */
    char *buf, /* pointer to the buffer */
    int n /* number of characters (bytes) we want */
    )
    { int bcount; /* counts bytes read */
    int br; /* bytes read this pass */

    bcount= 0;
    br= 0;
    while (bcount < n) { /* loop until full buffer */
    if ((br= read(s,buf,n-bcount)) > 0) {
    bcount += br; /* increment byte counter */
    buf += br; /* move buffer ptr for next read */
    }
    else if (br < 0) /* signal an error to the caller */
    return(-1);
    }
    return(bcount);
    }
    相同的函數也可以寫數據,留給我們的讀者吧。

    掛起(結束) 和你通過電話和某人交談后一樣,你要在 socket 間關閉連接。一般 close() 函數用來關閉每邊的 socket 連接。如果一邊的已經關閉,而另外一邊卻在向他寫數據,則返回一個錯誤代碼。

    世界語(交流的語言很重要) 現在你可以在機器間聯絡了,可是要小心你所說的話。許多機器有自己的方言,如 ASCII 和EBCDIC。更常見的問題是字節順序問題。除非你一直傳輸的都是文本,否則你一定要注意這個問題。幸運的是,人們找出了解決的辦法。

    在很久以前,人們爭論哪種順序更“正確”。現在必要時有相應的函數來轉換。其中有
    htons()、ntohs()、htonl() 和 ntohl()。在傳輸一個整型數據前,先轉換一下。


    i= htonl(i);
    write_data(s, &i, sizeof(i));
    在讀數據后,再變回來。


    read_data(s, &i, sizeof(i));
    i= ntohl(i);
    如果你一直堅持這個習慣,你將比別人少出錯的機會。

    未來在你的掌握了(下一步?) 就用我們剛才討論的東西,你就可以寫自己的通訊程序了。和對待所有的新生事物一樣,最好還是看看別人已經做了些什么。這里有許多關于 BSD socket 的東西可以參考。

    請注意,例子中沒有錯誤檢查,這在“真實”的程序中是很重要的
    posted on 2007-08-22 14:04 jinfeng_wang 閱讀(1299) 評論(0)  編輯  收藏 所屬分類: ZZLinux
    主站蜘蛛池模板: 亚洲日韩精品射精日| 亚洲AV日韩AV永久无码下载| 国产在线播放免费| 国产精品亚洲片在线观看不卡| 亚洲制服丝袜一区二区三区| 国产A∨免费精品视频| 成**人免费一级毛片| 精品亚洲国产成AV人片传媒| 人成电影网在线观看免费| 一二三四免费观看在线视频中文版| 久久久久久A亚洲欧洲AV冫| 亚洲av无码成人精品区一本二本| 永久黄色免费网站| 亚洲a一级免费视频| 最近中文字幕国语免费完整| 成人午夜亚洲精品无码网站| 久久久久亚洲AV无码去区首| 亚洲国产一级在线观看| 亚洲欧美黑人猛交群| 无限动漫网在线观看免费| 校园亚洲春色另类小说合集| 天天拍拍天天爽免费视频| 男人j进女人p免费视频| 国产精品国产免费无码专区不卡| 人人爽人人爽人人片A免费| 亚洲香蕉网久久综合影视| 永久看日本大片免费35分钟| 亚洲精品亚洲人成在线| 国产精品亚洲αv天堂无码| 国产99久久久国产精免费| 亚洲视屏在线观看| 曰批视频免费40分钟试看天天| 久久综合久久综合亚洲| 成人免费视频网址| 亚洲乱妇熟女爽到高潮的片 | 亚洲第一网站免费视频| 久久亚洲精品11p| 亚洲成a人片在线观看中文动漫 | 国产男女爽爽爽免费视频| 久久亚洲国产成人精品无码区| 95老司机免费福利|