第四篇:一對經典的時間獲取客戶/伺服器程式

穆晨發表於2017-01-29

前言

       本文通過一對經典的時間獲取客戶/伺服器程式,展現了Linux網路程式設計的大體框架,為以後更深入的學習打下基礎。

客戶伺服器模式網路程式設計的大體框架

       

客戶端程式碼

 1 #include <stdio.h>
 2 // 下標頭檔案包含socket(), bind()等套接字通訊必須的函式。
 3 #include <sys/socket.h>
 4 // 下標頭檔案包含網際套接字變數的型別定義等。
 5 #include <netinet/in.h>
 6 // 下標頭檔案包含bzero()函式等
 7 #include <string.h>
 8 // 下標頭檔案包含errno變數等
 9 #include <errno.h>
10 
11 #define MAXLINE 100 
12 
13 int main (int argc, char ** argv)
14 {
15     // 定義套接字描述符變數sockfd
16     // 定義變數n存放read函式返回值
17     int sockfd, n;
18     // 定義字元陣列recvline存放時間查詢結果
19     char recvline[MAXLINE + 1];
20     // 定義套接字變數servaddr( 注意這裡存放的是伺服器端的資訊 )
21     struct sockaddr_in servaddr;
22 
23     // 檢查命令格式是否正確
24     if (argc != 2) {
25         puts("命令格式錯誤\n");
26         return 1;
27     }
28     
29     // 建立一個網際位元組流套接字
30     if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) <0) {
31         puts("網際套接字建立失敗\n");
32         return 2;
33     }
34 
35     // 將套接字變數servaddr清零
36     bzero(&servaddr, sizeof(servaddr));
37     // 給套接字變數的協議段賦值( AF_INET表示IPv4協議 )
38     servaddr.sin_family = AF_INET;
39     // 給套接字變數的埠段賦值( 13是時間伺服器埠 )
40     servaddr.sin_port = htons(13);
41     // 給套接字變數的IP段賦值( argv[1]存放伺服器IP )
42     if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0) {
43         puts("IP地址格式轉換失敗");
44         return 3;
45     }
46 
47     // 按照先前socket函式指定的協議型別與伺服器進行連線
48     if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) {
49         puts("連線失敗\n");
50         return 4;
51     }
52 
53     // 從緩衝區讀取時間查詢結果並放進recvline陣列中
54     if ( (n = read(sockfd, recvline, MAXLINE)) > 0 ) {
55         recvline[n] = 0;
56         puts(recvline);
57     }
58     if (n < 0) {
59         puts("讀取資料發生錯誤");
60     }
61 
62     return 0;
63 }

伺服器端程式碼

 1 #include <stdio.h>
 2 
 3 // 下標頭檔案包含socket(), bind()等套接字通訊必須的函式。
 4 #include <sys/socket.h>
 5 // 下標頭檔案包含網際套接字變數的型別定義等。
 6 #include <netinet/in.h>
 7 // 下標頭檔案包含bzero()函式等
 8 #include <string.h>
 9 // 下標頭檔案包含errno變數等
10 #include <errno.h>
11 // 下標頭檔案包含一些時間函式
12 #include <time.h>
13 
14 #define MAXLINE 100 
15 #define LISTENQ 5
16 
17 int main(int argc, char **argv)
18 {
19     // 定義監聽套接字描述符listenfd
20     // 定義連線套接字描述符
21     int listenfd, connfd;
22     // 定義套接字變數servaddr( 注意這裡存放的是客戶端的資訊 )
23     struct sockaddr_in servaddr;
24     // 定義字元陣列buff暫存時間
25     char buff[MAXLINE];
26 
27     // 定義時間相關變數
28     time_t ticks;
29     struct tm *ptm;
30 
31     // 建立一個網際位元組流套接字
32     if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
33         puts("建立網際套接字失敗\n");
34         return 1;
35     }
36 
37     // 將套接字變數servaddr清零
38     bzero(&servaddr, sizeof(servaddr));
39     // 給套接字變數的協議段賦值( AF_INET表示IPv4協議 )
40     servaddr.sin_family = AF_INET;
41     // 給套接字變數的埠段賦值( 13是時間伺服器埠 )
42     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
43     // 給套接字變數的IP段賦值( argv[1]存放伺服器IP )
44     servaddr.sin_port = htons(13);
45 
46     // 把指定的協議地址繫結到套接字
47     if (bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0 ) {
48         puts("繫結服務埠失敗\n");
49         return 2;
50     }
51     // 將套接字轉換為監聽套接字型別
52     if (listen(listenfd, LISTENQ) < 0) {
53         puts("建立監聽套接字失敗\n");
54         return 3;
55     }
56 
57     while (1) {
58         // 等待客戶呼叫,若收到呼叫則返回一個已連線套接字。
59         connfd = accept(listenfd, (struct sockaddr *)NULL, NULL);
60 
61         /*
62          * 獲取時間並存放進buff陣列
63         */
64         ticks = time(NULL);
65         ptm = localtime(&ticks);
66         snprintf(buff, sizeof(buff), "現在是北京時間:\n%d年 %d月 %d日 %d時 %d分", ptm->tm_year+1900, ptm->tm_mon+1, ptm->tm_mday, ptm->tm_hour, ptm->tm_min);
67 
68         // 往客戶端緩衝區中寫入時間
69         while (write(connfd, buff, strlen(buff)) < 0) {
70             puts("寫入資料失敗\n");
71             return  4;
72         }
73         // 關閉連線
74         if (close(connfd)) {
75             puts("關閉套接字失敗\n");
76             return 5;
77         }
78     }
79 }

執行測試

       1. 在一個終端用超級使用者許可權啟動伺服器:

       

       2. 在另一個終端中啟動客戶端並輸進本機IP或者127.0.0.1:

       

相關文章