1. 程式人生 > >UNP --- 第一章 介紹

UNP --- 第一章 介紹

pad fig 完成後 應用 puts 主機 定義 turn 等待

一個簡單的時間獲取客戶端程序

 1 #include "unp.h"
 2 
 3 int main(int argc, char **argv)
 4 {
 5     int sockfd, n;
 6     char recvline[MAXLINE + 1];
 7     struct sockaddr_in servaddr;
 8 
 9     if (argc != 2)
10     {
11         err_quit("usage: a.out <IPaddress>");
12     }
13     if ((sockfd = socket(AF_INET, SOCK_STREAM, 0
)) < 0) 14 { 15 err_sys("socket error"); 16 } 17 18 bzero(&servaddr, sizeof(servaddr)); 19 servaddr.sin_family = AF_INET; 20 servaddr.sin_port = htons(13); 21 if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0) 22 { 23 err_quit("inet_pton error for %s
", argv[1]); 24 } 25 if (connect(sockfd, (SA *)&servaddr, sizeof(servaddr)) < 0) 26 { 27 err_sys("connect error"); 28 } 29 30 while ((n = read(sockfd, recvline, MAXLINE)) > 0) 31 { 32 recvline[n] = 0; 33 if (fputs(recvline, stdout) == EOF) 34 {
35 err_sys("fputs error"); 36 } 37 } 38 if (n < 0) 39 { 40 err_sys("read error"); 41 } 42 }
  1. socket函數創建一個網際(AF_INET)字節流(SOCK_STREAM)套接字,即TCP套接字(TCP端點)
  2. 需要將服務器的IP地址和端口號填入sockaddr_in結構變量中,用以指定服務器套接字相關信息。該變量的地址族為AF_INET,端口號為13(時間獲取服務器的知名端口)。
  3. htons用來轉換二進制端口號,inet_pton將IP地址字符串轉換為數值。
  4. connect函數應用於一個TCP套接字時,將與它參數中套接字指定的服務器建立TCP連接。
  5. 使用read循環讀取服務器的應答。因為TCP是一個沒有記錄邊界的字節流協議,所以daytime服務器的26個字節可以有多種返回方式,不能確保一次read就能完成所以讀取。當read返回0(表示對端關閉連接)或負數(表示發生錯誤)時,終止循環。

錯誤處理:包裹函數

對於每個函數調用,都必須檢查是否返回錯誤。當發生錯誤時,就使用函數輸出錯誤信息並終止程序運行。通過定義包裹函數來完成實際的函數調用,檢查返回值,並在發生錯誤時終止程序。如:

int Socket(int family, int type, int protocol)
{
    int n;
    if((n = socket(family, type, protocol)) < 0)
        err_sys("socket error");
    return n;
}

一個簡單的時間獲取服務器程序

#include "unp.h"
#include <time.h>

int main(int argc, char* argv[])
{
    int listenfd, connfd;
    struct sockaddr_in servaddr;
    char buff[MAXLINE + 1];
    time_t ticks;

    listenfd = Socket(AF_INET, SOCK_STREAM, 0);
    bzero(&servaddr, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    servaddr.sin_port = htons(13);

    Bind(listenfd, (SA*)&servaddr, sizeof(servaddr));
    Listen(listenfd, LISTENQ);

    while(1) {
        connfd = Accept(listenfd, NULL, NULL);
        ticks = time(NULL);
        snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
        Write(connfd, buff, strlen(buff));
        Close(connfd);
    }
}
  1. 調用bind函數,服務器的知名端口(13)被綁定到套接字中。IP地址為INADDR_ANY,這樣要是服務器主機有多個網絡接口,服務器進程就可以在任意網絡接口上接受客戶連接。
  2. 調用listen函數把套接字變為監聽套接字。
  3. 服務器進程在accept函數中被投入休眠,等待某個客戶的連接到達並被內核接受。TCP使用三次握手建立連接,建立完成後accept返回,返回值是已連接描述符,用於與新連接的客戶通信。
  4. time返回秒數,ctime將該整數值變為直觀可讀的格式。
  5. snprintf的第二個參數指定緩沖區的大小。

網絡拓撲相關命令

  1. netstat -i提供網絡接口的信息。
  2. netstat -r展示路由表。
  3. ifconfig + 接口名字,可獲得各接口的詳細信息。
  4. 為了找出本地網絡眾多主機IP地址,可ping本地接口的廣播地址。

UNP --- 第一章 介紹