Google Code Prettify

顯示具有 Linux 標籤的文章。 顯示所有文章
顯示具有 Linux 標籤的文章。 顯示所有文章

2022年3月27日 星期日

RHEL8 無法安裝套件 No match for argument: xxx

以前測試時使用的 Linux 都是 CentOS,但是 CentOS 被 Red Hat 殺死後,今天想改用 RHEL8,結果,在安裝任何套件時都會出現 

No match for argument: 套件名稱 的錯誤訊息,像是安裝 OpenJDK 11 會出現如下錯誤 …

No match for argument: java-11-openjdk
No match for argument: java-11-openjdk-devel

後來發現是要先向 Red Hat 訂閱 repository,才可以拿到這些套件 …

  1. 在 Red Hat 網站註冊一個帳號,我註冊了一個 steventw 的帳號。
  2. 在 RHEL8 中下如下指令進行訂閱:
    sudo subscription-manager register --username=steventw --password=xxxxxxxx
    sudo subscription-manager attach --auto
    
這樣就可以開始正常安裝套件了!

2020年5月1日 星期五

Ubuntu 上的 PPPoE 設定

中華電信的光世代 ADSL,沒有特別申請的話,連接上去是動態 IP,如果想要固定 IP 可以到官網申請,申請核可後,電腦需以 PPPoE 的方式撥接上去取得該固定 IP,在 Ubuntu 中,方法如下:
  1. 安裝
  2. sudo apt install net-tools
    sudo apt-get install pppoeconf
    
  3. 設定
  4. sudo pppoeconf
    
    除了帳號、密碼外,每個選項都用預設值,直接按【ENTER】,帳號的部份要特別注意,中華電信給的帳號可能為 74669091,在這裡要輸入 74669091@ip.hinet.net
  5. 撥接
  6. sudo pon dsl-provider
    
  7. 中斷連線
  8. sudo poff -a
    
  9. 觀察
  10. 撥接上去後,透過 ifconfig -a 可以看到類似下面的內容,顯示有張網卡綁定了固定 IP。
    另外在 /etc/ppp 目錄下,chap-secrets 和 pap-secrets 兩個檔案裡,可以查到撥接的帳號、密碼。

2017年12月22日 星期五

在 linux 中無法切換身份? (su: Permission denied)

Linux 預設,只有 root 可以切換到別的身份 (帳號),如果要取消這個限制,怎麼做? 如下:

1. 變更授權規則
    編輯 /etc/pam.d/su 檔,找到 auth required 這個項目,原來的值可能如下:
auth            required        pam_wheel.so trust group=wheel
    將它改成如下:
auth            required        pam_wheel.so use_uid
2. 將要能切換到別的身份的帳號加入 wheel 群組
    假設想要 user1 和 user2 可以相互切換,那麼執行如下指令:
usermod -a -G wheel user1
usermod -a -G wheel user2 
再測試看看,應該沒問題了。

2017年9月4日 星期一

Installing and Configuring Docker (Scientific Linux 7.3)

為什麼 Docker 官網所列出來有支援的作業系統沒有 Scientific Linux ? 但實際上是安裝的起來的,只是方法似乎比較不一樣,在 Adaptive Computing 這家公司的網站反而有提供方法,如下:
  1. Make sure existing yum packages are up-to-date.
  2. yum update
  3. Install the Docker package.
  4. yum install docker
  5. Edit /etc/sysconfig/docker by replacing the existing OPTIONS line with the following.
  6. OPTIONS='-s devicemapper --storage-opt dm.fs=xfs --exec-opt native.cgroupdriver=cgroupfs'
  7. Start/restart Docker.
    systemctl restart docker.service
  8. Set Docker to start after reboot.
    systemctl enable docker.service
  9. Verify the installation.
    docker run dockerinaction/hello-world
【補充】
移除 docker:
    yum remove docker




【Linux 指令】
安裝軟體前常常需要先確認一下自己的 Linux 的版本,這裡提供兩個常用的指令 ...
  • lsb_release -a : 查看 Linux 的發行版本資訊
[steven@localhost ~]$ lsb_release -a
LSB Version:    :core-4.1-amd64:core-4.1-noarch:cxx-4.1-amd64:cxx-4.1-noarch:desktop-4.1-amd64:desktop-4.1-noarch:languages-4.1-amd64:languages-4.1-noarch:printing-4.1-amd64:printing-4.1-noarch
Distributor ID: Scientific
Description:    Scientific Linux release 7.3 (Nitrogen)
Release:        7.3
Codename:       Nitrogen
  • uname -a: 查看 Linux core 的版本
[steven@localhost ~]$ uname -a 
Linux localhost.localdomain 3.10.0-693.1.1.el7.x86_64 #1 SMP Tue Aug 15 08:36:44 CDT 2017 x86_64 x86_64 x86_64 GNU/Linux

2015年7月22日 星期三

NIO.2: 目錄的處理

檔案系統的 I/O,除了前幾篇所說明的檔案相關處理外,另一個主題就是目錄的處理 - 讀取、走訪、過濾檔案等,說明如下:
  • 取得根目錄
 1 package idv.steven.nio2.filedir;
 2 
 3 import static java.lang.System.out;
 4 import java.nio.file.FileSystems;
 5 import java.nio.file.Path;
 6 import java.util.ArrayList;
 7 
 8 public class ListRoots {
 9 
10     public static void main(String[] args) {
11         Iterable<Path> dirs = FileSystems.getDefault().getRootDirectories();
12         
13         ArrayList<Path> list = new ArrayList<Path>();
14         for(Path name : dirs) {
15             list.add(name);
16         }
17         Path[] array = new Path[list.size()];
18         list.toArray(array);
19         
20         for(Path path : array) {
21             System.out.println(path);
22         }
23         
24         //dirs.forEach(out::println); //Java 8
25     }
26 }
如上的程式,第 11 行可以取得檔案系統的根目錄,在單根檔案系統,例如 Unix/Linux,當然就只會輸出 /,在 Windows 上執行,就看作業系統裡有幾個磁碟,像是我的電腦,執行出來就會是 .....
C:\
D:\
E:\
F:\
H:\
程式 13~22 行是傳統 Java 的寫法,使用 Lambda 的話,只要第 24 行這樣一行程式就可以了。
  • 檢查檔案或目錄是否存在? 
 1 package idv.steven.nio2.filedir;
 2 
 3 import java.nio.file.FileSystems;
 4 import java.nio.file.Files;
 5 import java.nio.file.LinkOption;
 6 import java.nio.file.Path;
 7 
 8 public class CheckExist {
 9     public static void main(String[] args) {
10         Path path = FileSystems.getDefault().getPath("D:/TEST");
11         boolean isNotExist = Files.notExists(path, new LinkOption[] {LinkOption.NOFOLLOW_LINKS});
12         System.out.println(isNotExist);
13     }
14 }
不管是檢查檔案或目錄是否存在,使用的方法是一樣的,上面的程式是檢查 D:/ 下是否有 TEST 這個子目錄,注意看一下第 11 行,這裡使用的是 notExists 這個 method,Files 類別也提供有 exists method,notExists 就等於 !exists。
  • 讀取目錄
 1 package idv.steven.nio2.filedir;
 2 
 3 import java.io.IOException;
 4 import java.nio.file.DirectoryStream;
 5 import java.nio.file.Files;
 6 import java.nio.file.Path;
 7 import java.nio.file.Paths;
 8 
 9 public class ListDirectory {
10 
11     public static void main(String[] args) {
12         Path path = Paths.get("/home/steven/TEST");
13         try (DirectoryStream<Path> ds = Files.newDirectoryStream(path, "[rR]eadme*.txt")) {
14             for(Path file : ds) {
15                 System.out.println(file.getFileName());
16             }
17         }
18         catch (IOException e) {
19             System.err.println(e);
20         }
21     }
22 }
如上是 java.nio 提供的讀取目錄內容的方法之一,第二個參數不填,則會列出目錄裡的所有檔案、子目錄、連結等所有內容。第二個參數有那些選項呢? 如下:
  1. *: 比對所有字元。
  2. **: 跨目錄比對所有字元。
  3. ?: 比對一個字元,如果用這個選項,例如: a?.txt,那麼就一定是 a 後面要接一個字元,a.txt 不算在這個條件裡。
  4. { }: 比對大括號內所有 pattern,例如: {a?.txt, *.jpg},那麼可能就會列出 ab.txt、face.jpg、leg.jpg …
  5. [ ]: 比對中括號內所有列出的字元,像是上面的程式,會找出 readme*.txt 及 Readme*.txt 的檔案,中括號內,可以放入如下選項:
(1) [0-9]: 0 到 9 的所有數字,當然,也可以是[3-7],只搜尋 3 到 7 的數字。
(2) [A-Z]: 大寫 A 到 Z。
(3) [a-z,A-Z]: 大小寫的所有英文字母。
(4) [2345RT]: 比對到括號中任一字元。
6. 大括號、中括號內也可以有 *、? 等萬用字元,並複合使用,例如: {*[0-9].jpg},輸出的結果可能就會是 face1.jpg、leg3.jpg …
  •  走訪目錄
上面的說明有提到讀取目錄的方法,如果我們要走訪目錄呢? java.nio 提到了一個介面 FileVisitor,實作這個介面後,會有四個 callback method,在走訪時,java.nio 會在各個時間點呼叫這四個 method,讓我們可以進行各種處理,四個介面說明如下:
  1. preVisitDirectory: 走訪一個目錄前。
  2. postVisitDirectory: 走訪一個目錄之後。
  3. visitFile: 走訪一個檔案時。
  4. visitFileFailed: 走訪檔案失敗,通常是因為無存取權限。
接下來看一下程式:
 1 package idv.steven.nio2.filedir;
 2 
 3 import java.io.IOException;
 4 import java.nio.file.FileVisitResult;
 5 import java.nio.file.FileVisitor;
 6 import java.nio.file.Files;
 7 import java.nio.file.Path;
 8 import java.nio.file.Paths;
 9 import java.nio.file.attribute.BasicFileAttributes;
10 import static java.nio.file.FileVisitResult.*;
11 
12 public class WalkFolder implements FileVisitor<Path> {
13 
14     @Override
15     public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
16             throws IOException {
17         
18         System.out.println("preVisitDirectory: " + dir.getFileName());
19 
20         return CONTINUE;
21     }
22 
23     @Override
24     public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
25             throws IOException {
26         
27         System.out.println("visitFile: " + file.getFileName());
28         
29         return CONTINUE;
30     }
31 
32     @Override
33     public FileVisitResult visitFileFailed(Path file, IOException exc)
34             throws IOException {
35         
36         System.out.println("visitFileFailed: " + file.getFileName());
37         
38         return CONTINUE;
39     }
40 
41     @Override
42     public FileVisitResult postVisitDirectory(Path dir, IOException exc)
43             throws IOException {
44 
45         System.out.println("postVisitDirectory: " + dir.getFileName());
46         
47         return CONTINUE;
48     }
49     
50     public static void main(String[] args) throws IOException {
51         WalkFolder walkFolder = new WalkFolder();
52         
53         Files.walkFileTree(Paths.get("D:/TEST"), walkFolder);
54     }
55 }
這個程式會走訪 D:/TEST  下的所有子目錄及檔案,在 method 中可進行需要的處理,特別要提出來說明的是返回值,上面四個 method 都返回 CONTINE,表示繼續走訪,java.nio 提供了四個返回值,如下:
  1. CONTINUE: 繼續走訪
  2. SKIP_SIBLINGS: 跳過同一階層的檔案或子目錄後繼續走訪
  3. SKIP_SUBTREE: 跳過正走訪的這個目錄後繼續走訪
  4. TERMINATE: 停止走訪
在許多的情況下,我們並不會需要同時實作四個 method,所以 java.nio 提供了一個 SimpleFileVisitor 類別,繼承這個類別,我們只需要 override 要特別處理的 method,這樣會方便些。

  • 拷貝、搬移、刪除
 1 package idv.steven.nio2.action;
 2 
 3 import java.io.IOException;
 4 import java.nio.file.Files;
 5 import java.nio.file.Path;
 6 import java.nio.file.Paths;
 7 
 8 public class Copy {
 9 
10     public static void main(String[] args) throws IOException {
11         Path from = Paths.get("D:/TEST/readme.txt");
12         Path to = Paths.get("D:/readme.txt");
13         Files.copy(from, to);
14     }
15 }
上面的程式,很簡單的都用預設值,將檔案由 from 拷貝一份到 to,Files.copy(...) 是可以帶參數,以指出要如何拷貝檔案,例如將它改寫如下:
import static java.nio.file.StandardCopyOption.*;
import static java.nio.file.LinkOption.*;
…
Files.copy(from, to, REPLACE_EXISTING, COPY_ATTRIBUTES, NOFOLLOW_LINKS);
第三個參數指出檔案的屬性也要拷貝到新檔,第四個參數則指出,如果這個檔案是 symbol link 就不要拷貝,這些參數定義在 StandardCopyOption 及 LinkOption 套件中。同樣的程式,可以用來拷貝目錄嗎? 可以,不過,目錄下的檔案不會被拷貝。Files 這個類別還提供有 move、delete 等 method,可以用來搬移、刪除檔案、目錄,詳細可以參考 Java Doc。

2015年7月19日 星期日

NIO.2: POSIX View

POSIX View 無庸置疑,是為 Unix like 作業系統準備的! 來看看程式吧 ~
 1 package idv.steven.nio2.metadata;
 2 
 3 import java.io.IOException;
 4 import java.nio.file.Files;
 5 import java.nio.file.Path;
 6 import java.nio.file.Paths;
 7 import java.nio.file.attribute.GroupPrincipal;
 8 import java.nio.file.attribute.PosixFileAttributeView;
 9 import java.nio.file.attribute.PosixFileAttributes;
10 import java.nio.file.attribute.PosixFilePermission;
11 import java.nio.file.attribute.PosixFilePermissions;
12 import static java.nio.file.LinkOption.NOFOLLOW_LINKS;
13 import java.util.Set;
14 
15 public class POSIXView {
16 
17     public static void main(String[] args) {
18         PosixFileAttributes attr = null;
19         Path path = Paths.get("/home/steven/seal/steven.pub");
20         
21         // read permissions
22         try {
23             attr = Files.readAttributes(path, PosixFileAttributes.class);
24         } catch (IOException e) {
25             System.err.println(e.getMessage());
26         }
27 
28         System.out.println("File owner: " + attr.owner().getName());
29         System.out.println("File group: " + attr.group().getName());
30         System.out.println("File permissions: " + attr.permissions().toString());
31         
32         // set permissions
33         Set<PosixFilePermission> permissions = PosixFilePermissions.fromString("rw-r--r--");
34         try {
35             Files.setPosixFilePermissions(path, permissions);
36             
37             GroupPrincipal group = path.getFileSystem().
38                     getUserPrincipalLookupService().lookupPrincipalByGroupName("steven");
39                     Files.getFileAttributeView(path, PosixFileAttributeView.class).setGroup(group);
40                     
41             GroupPrincipal groupSteven = (GroupPrincipal) Files.getAttribute(path, "posix:group", NOFOLLOW_LINKS);
42             System.out.println(group.getName());
43         } catch (IOException e) {
44             System.err.println(e);
45         }
46     }
47 }
 解釋程式前,先看一下輸出結果 ...
File owner: steven
File group: steven
File permissions: [OTHERS_READ, OWNER_WRITE, GROUP_WRITE, OWNER_READ, GROUP_READ]
執行後,steven.pub 的權限如下圖:

程式說明如下:
  • 22 ~ 30 行: 讀取檔案的權限,包括檔案的擁有者、群組及讀、寫、執行的權限,這裡的 file permissions 不是用 unix 使用者習慣的 664,而是以文字來表示。
  • 33 ~ 35 行: 設定檔案的權限,設定後權限改為 644。
  • 37 ~ 45 行: 變更檔案所屬的群組,當然啦~ 這個群組要是 OS 中已經存在的,且是目前執行的這支程式的帳號有權限可以變更的。

如果想要在 Unix/Linux 中建立一個目錄,並設定適當的權限,該怎麼做? 如下:
Path newFolder = FileSystems.getDefault().getPath("/home/steven/newFolder");
Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rwxr-x---");
FileAttribute<Set<PosixFilePermission>> attr = PosixFilePermissions.asFileAttribute(perms);
        
try {
    Files.createDirectory(newFolder, attr);
} catch (IOException e) {
    System.err.println(e);
}
先使用 PosixFilePermissions 設定好權限,再用 createDirectory 建立目錄。



2015年6月6日 星期六

fork Function


在「Advanced.Programming.in.the.UNIX.Environment, 3rd.Edition」一書中的8.3節 (p. 230),有個小程式,如下,是用來說明 UNIX 環境中,使用 fork 產生子行程 (child process),要注意的一些事,先看一下程式:

 1 #include <stdio.h>
 2 #include <unistd.h>
 3 
 4 int globvar = 6;
 5 char buf[] = "a write to stdout\n";
 6 
 7 int main(void) {
 8     int var;
 9     pid_t pid;
10 
11     var = 88;
12     if (write(STDOUT_FILENO, buf, sizeof(buf)-1) != sizeof(buf)-1)
13         printf("write error");
14     printf("before fork\n");
15 
16     if ((pid = fork()) < 0) {
17         printf("fork error");
18     }
19     else if (pid == 0) {
20         globvar++;
21         var++;
22     }
23     else {
24         sleep(2);
25     }
26 
27     printf("pid = %ld, getpid = %ld, glob = %d, var = %d\n", (long) pid, (long) getpid(), globvar, var);
28     _exit(0);
29 }

上面的程式很簡單的利用 fork 產生一個子行程,等待 2 秒後,印出一段訊息,顯示 fork 傳回值、行程的 pid (process id)、全域變數及區域變數,執行結果如下: (我的程式命名為 forEx01.c)
[steven@CentOS7 Debug]$ ./forkEx01
a write to stdout
before fork
pid = 0, getpid = 9214, glob = 7, var = 89
pid = 9214, getpid = 9213, glob = 6, var = 88
[steven@CentOS7 Debug]$ 
根據上述的執行結果,說明如下:
  1. fork 函數會產生一個子行程,子行程會執行父行程 (parent process) fork 之後的指令。
  2. fork 函數在父行程會傳回子行程的 pid,在子行程則傳回 0,子行程如果要得到父行程的 pid,可以透過 getppid 函數得到值。
  3. 上面程式讓父行程睡 2 秒後再印出結果,所以第一個結果是子程式印出的,第二個結果是父行程印出的,通常確實是會得到如上的結果 (除了 pid、getpid 的值會不同之外),但是,實際上有時會是父行程先印出,因為那個行程先執行,由 OS 決定,這裡父行程睡 2 秒只是增加子行程先執行的機率。
  4. 上述程式的全域變數 (global variable) 和區域變數 (local variable) 於子行程中都被加 1,但是都沒有影響到父行程的值,這表示不管是全域變數或區域變數,父行程、子行程都不共用!
  5. 子行程的輸出接在父行程 fork 前已輸出的內容之後,父行程後面的輸出又接在子行程的輸出之後,完全不會互相覆蓋,因為父行程和子行程會共享在 fork 之前已開啟的所有檔案描述符 (file descriptions),也就是說兩個行程會共享這些檔案的檔案指標! 標準輸出是在父行程一開始執行時就被開啟的,所以會被兩個行程共享。
除了上述程式所展現的父行程、子行程關係外,另外針對行程的一些基本觀念整理如下:
  1. UNIX 系統在系統啟動後,會啟動許多 process 來處理一些系統面的事,其中一個 pid 為 1 的 process 稱為 init process,是負責啟動和關閉系統。
  2. 程式以 fork 啟動一個或數個子行程,當子行程結束後,父行程要負責將其佔用的資源都回收後,才可真正將其結束,在子行程已經執行完,但是父行程尚未將其資源回收前,這個子行程就稱為 zombie (僵屍)。
  3. 如果父行程比子行程更早結束,子行程並不會被迫結束,而是會交由 init process 管理,也就是說,子行程的 ppid 會被改為 1,之後子行程結束,其資源會由 init process 進行回收。



2015年5月23日 星期六

Linux Network Programming - 初體驗

十幾年沒有寫 C 了,這可以說是重新學習,底下的程式是改寫自 W.Richard Stevens 的名著 - UNIX Network Programming Volume 1,改寫的原因有二:
(1) 在我的開發環境 scientific linux 7.0 上沒辦法正常 compile,可能是因為 Stevens 的程式是在 UNIX 上寫的,與 linux 上略有不同;
(2) Stevens 用 #define 將許多常用的函式重新定義,對於初學者來說,反而會被混淆,我將這些函式還原為原始函式。
這個程式有 client 和 server,server 先啟動,等待 client 連線,待 client 連線後回覆現在的系統時間,client 將收到的值印出。第一個程式是 server,第二個程式是 client。這裡會詳細說明每個函式,因為… 我是初學者...  XD
 1 #include <sys/socket.h>
 2 #include <sys/types.h>
 3 #include <stdio.h>
 4 #include <unistd.h>
 5 #include <netinet/in.h>
 6 #include <string.h>
 7 #include <arpa/inet.h>
 8 #include <time.h>
 9 
10 const int MAXLINE = 100;
11 const int LISTENQ = 1024;
12 
13 int main(int argc, char **argv) {
14     int listenfd, connfd;
15     struct sockaddr_in servaddr;
16     char buff[MAXLINE];
17 
18     time_t ticks;
19     listenfd = socket(AF_INET, SOCK_STREAM, 0);
20 
21     bzero(&servaddr, sizeof(servaddr));
22     servaddr.sin_family = AF_INET;
23     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
24     servaddr.sin_port = htons(1513);
25 
26     bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr));
27     listen(listenfd, LISTENQ);
28 
29     for( ; ; ) {
30         connfd = accept(listenfd, (struct sockaddr *) NULL, NULL);
31 
32         ticks = time(NULL);
33         snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
34         write(connfd, buff, strlen(buff));
35 
36         close(connfd);
37     }
38 
39 }
  • 第 19 行: 建立一個 socket 連線,不管 client 或 server 程式,一開始都會呼叫這個函式,函式所需要的三個參數說明如下:
    • 第一個參數: AF_INET 表示要建立網際網路的 IPv4 socket 連線,除此之外還可以填入 AF_INET6 (IPv6) 或 AF_UNSPEC (同時可以用 IPv4 及 IPv6),socket 函式及這幾個常數是定義在 socket.h 標頭檔裡,這也是為什麼第一行要引入這個標頭檔的原因,大部份的 socket 相關函式、常數都定義在這個標頭檔。
    • 第二個參數: 可以傳入的值有兩個 - SOCK_STREAM 及 SOCK_DGRAM,要建立 TCP 連線時傳入 SOCK_STREAM,要建立 UDP 連線時傳入 SOCK_DGRAM。
    • 第三個參數: 通常就是傳入 0,socket 將會選擇最適合的通訊協定,也就是第二個參數的說明,至於不傳入 0 時,用在什麼地方? 這個以後再說 (現在我也不知道)。
    • 傳回值: 錯誤時傳回 -1,正確傳回 0,C 語言的習慣就是正確傳回 0,錯誤則傳回錯誤代碼。
  • 第 21 行: bzero 是將指定的位指空間全部清為 0,這不是 ANSI C 的標準函式,ANSI C 的標準函式是 memset,不過 bzero 大多數的平台也都有支援,定義在 string.h 裡。傳入的兩個參數如下:
    • 第一個參數: 要清為 0 的位址。
    • 第二個參數: 位址空間的長度。
  • 第 22~24 行: 設定 server address 的值,這是在第 15 行宣告的變數,struct sockaddr_in 這個結構是為了在 bind 時傳入一些值,這三個值說明如下:
    • sin_family: 如 socket 的第二個參數說明的一樣,這是指要使用 TCP 通訊協定。
    • sin_addr.s_addr: 這裡指定為 INADDR_ANY 是使得任何位址連進來的 socket 連線都可以被接受,在設定前呼叫的 htonl 函式是為了讓值與平台無關,不同的 OS 有些字元排列順序高位元和低位元不一定相同,透過這個函式可以去除這種平台相依性,都轉為網路所規範的順序。
    • sin_port: 指定要 bind 到那個 port,htons 這個函式和 htonl 是一樣的,都是用來去除平台相依性,差別在於 htons 是用在 16 bits 的變數, htonl 是用在 32 bits 的變數。
  • 第 26 行: bind 是將 socket 和 sockaddr 繫結起來的函式,表示第 19 行建立的 socket 連線,將會是允許任何位址連線,而將傾聽的 port 是 1513。
  • 第 27 行: 開始傾聽,最大連線數是 LINTENQ 所定義的值,即 1024 條連線。
  • 第 29 行: 這裡建立一個無窮迴圈,所以這個 server 會一直執行,直到使用者按 ctrl-C 為止。
  • 第 30 行: server  會停在 accept 這裡等待 client 連線,參數說明如下:
    • 第一個參數: 透過傳入傾聽描述子 listenfd,將第 26、27 行傾聽的相關數據傳入。
    • 第二、三個參數: 待我弄懂了再補充  … XD
    • 傳回值: 建立連線後,傳回連線描述子 (connected descriptor),這個描述子會用來與新的 client 連線進行通訊。
  • 第 32 行: 取得目前的系統時間,這個 time 函式定義在 time.h 標頭檔裡。
  • 第 33 行: snprintf 函式定義在 stdio.h 裡,和 sprintf 的最大差別在於 snprintf 的第二個參數指出了第一個參數的空間大小,可防止實際產生出來的字串長度超過第一個參數所宣告的空間。
  • 第 34 行: 將結果寫出給 client,這裡可以看到第一個參數即是 accept 的傳回值,這樣就可以通知系統到底是要將資料傳給那個 client。
  • 第 36 行: 關閉與 client 間的 socket 連線。






 1 #include <stdio.h>
 2 #include <sys/socket.h>
 3 #include <sys/types.h>
 4 #include <netinet/in.h>
 5 #include <strings.h>
 6 #include <arpa/inet.h>
 7 
 8 const int MAXLINE = 100;
 9 
10 void err_sys(const char* x, ...)
11 {
12     perror(x);
13     //exit(1);
14 }
15 
16 void err_quit(const char* x, ...)
17 {
18     perror(x);
19     //exit(1);
20 }
21 
22 int main(int argc, char **argv)
23 {
24     int sockfd, n;
25     char recvline[MAXLINE + 1];
26     struct sockaddr_in servaddr;
27 
28     if (argc != 2) {
29         err_quit("usage: a.out <IPaddress>");
30         return 1;
31     }
32 
33     if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
34         err_sys("socket error");
35         return 1;
36     }
37 
38     bzero(&servaddr, sizeof(servaddr));
39     servaddr.sin_family = AF_INET;
40     servaddr.sin_port = htons(1513);
41     if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
42         err_quit("inet_pton error for %s", argv[1]);
43 
44     if (connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0)
45         err_sys("connect error");
46 
47     while ((n = read(sockfd, recvline, MAXLINE)) > 0) {
48         recvline[n] = 0;
49         if (fputs(recvline, stdout) == EOF)
50             err_sys("fputs error");
51     }
52 
53     if (n < 0) {
54         err_sys("read error");
55         return 1;
56     }
57 
58     return 0;
59 }
  • 第 41 行: client 程式執行時,要帶入 server 的 IP,inet_pton 函式即是將 IP 的文字格式轉成接下來要呼叫的 connect 所需要的 binary 格式。
  • 第 44 行: 呼叫 connect 連線到 server。
  • 第 47 行: 使用 read 讀取 server 傳回的值,三個參數說明如下:
    • 第一個參數: socket 描述子,第 33 行建立 socket 連線時記錄下來的數值。
    • 第二個參數: 讀取的值要放入的空間。
    • 第三個參數: 放入的空間的最大長度值。
  • 第 49 行: 將結果寫出到標準輸出裝置。
測試時,先執行 server 再執行 client,client 端顯示如下結果:
【日劇: Second Love】
在台灣播出時改名為「愛上女老師」,看這部日劇前,沒特別注意過深田恭子原來那麼偉大,而且都年過三十了,還那麼可愛,算是不簡單,許多人上了年紀還裝可愛,可是會被吐槽的,但是她不會。這部戲為什麼只有七集? 是因為收視率太低嗎? 這就不曉得了,但是除了結局太老套外,倒是還不錯看。