當前位置: 妍妍網 > 碼農

Linux網路套用開發---行程通訊IPC匯總

2024-03-05碼農

點選上方Linux開源社群」,選擇「設為星標

優質文章,及時送達

轉自:網路

一. 行程簡述:

行程是作業系統的概念,每當我們執行一個程式時,對於作業系統來講就建立了一個行程,在這個過程中,伴隨著資源的分配和釋放。可以認為行程是一個程式的一次執行過程。

1)Linux下行程結構

Linux下一個行程在記憶體裏有三部份的數據,就是"程式碼段"、"堆疊段"和"數據段"。其實學過組合語言的人一定知道,一般的CPU都有上述三種段寄存器,以方便作業系統的執行。

這三個部份也是構成一個完整的執行序列的必要的部份。所以不同的行程間,由於linux系統虛擬記憶體地址的管理,這三個段也是獨立存在的,所以行程間是不能相互存取數據的,需要透過系統提供的特殊方法來進行相互通訊。

2)Linux下行程通訊的方法:

行程使用者空間是相互獨立的,一般而言是不能相互存取的。但很多情況下行程間需要互相通訊,來完成系統的某項功能。行程透過與內核及其它行程之間的互相通訊來協調它們的行為。

比較常用的IPC通訊方法有:

管道(有名和無名)、訊號、號誌、共享記憶體、訊息佇列和套接字socket通訊。

3)行程通訊使用場景:

(1)數據傳輸:行程間數據傳輸;

(2)通知事件:一個行程向另一個或一組行程發送訊息,通知某個事件的發生(如子行程終止時需通知父行程);

(3)資源共享:多個行程共享資源,需要內核提供同步互斥機制;

(4)行程控制:某行程需要控制另一個行程的執行(如Debug行程),此時控制行程需要攔截另一個行程的所有陷入、異常、狀態等。


二. 行程通訊的方法:

1. 有名管道和無名管道

a. 無名管道(父子行程、兄弟行程間通訊):

---特點:

(1) 半雙工。數據同一時刻只能單向傳輸;

(2) 數據從管道一端寫入,另一端讀出;

(3) 寫入管道的數據遵循先進先出;

(4) 管道非普通檔,不屬於某個檔案系統,只存在於記憶體;

(5) 無名管道只能在具有公共祖先的行程(父子行程、兄弟行程等)之間使用。

---操作步驟:

(1)建立: pipe函式用來建立無名管道

(2)操作: read讀;write寫

(3)關閉操作埠: close

例子程式:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<sys/stat.h>
intmain(void)
{
char buf[32] = {0};
pid_t pid;
// 數量為 2 個:一個讀端, 一個寫端,
int fd[2] = {-1};
// 建立無名管道
pipe(fd);
printf("fd[0] is %d\n", fd[0]);
printf("fd[2] is %d\n", fd[1]);
// 建立行程
pid = fork();
if (pid < 0)
{
printf("error\n");
}
if (pid > 0)
{
int status;
close(fd[0]);
write(fd[1], "hello", 5);
close(fd[1]);
wait(&status);
exit(0);
}
if (pid == 0)
{
close(fd[1]);
read(fd[0], buf, 32);
printf("buf is %s\n", buf);
close(fd[0]);
exit(0);
}
return0;
}

b. 有名管道(允許無親緣關系行程間的通訊)。

----特點:

(1) 它可以使互不相關的兩個行程實作彼此通訊

(2) 該管道可以透過路徑名來指出,並且在檔案系統中是可見的。在建立管道之後,兩個行程就可以把它當作普通檔進行讀寫,使用非常方便。

(3) FIFO嚴格遵循先進先出原則,對管道及FIFO的讀總是從開始處返回數據,對它們的寫則把數據添加到末尾。有名管道不支持如Iseek()等檔的定位操作。

---操作步驟:

(1) 建立有名管道檔: mkfifo即是命令也是函式;mknod也可以建立管道檔;

(2) 開啟有名管道: open;

(3) 讀/寫: read/write

(4) 關閉: close

例子程式:

(1)named_pipe_write.c:
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
intmain(int argc, char *argv[])
{
int ret;
char buf[32] = {0};
int fd;
if (argc < 2)
{
printf("Usage:%s <fifo name> \n", argv[0]);
return-1;
}
if (access(argv[1], F_OK) == -1)
{
///建立有名管道檔
ret = mkfifo(argv[1], 0666);
if (ret == -1)
{
printf("mkfifo is error \n");
return-2;
}
printf("mkfifo is ok \n");
}
///開啟有名管道檔
fd = open(argv[1], O_WRONLY);
while (1)
{
sleep(1);
write(fd, "hello", 5);
}
close(fd);
return0;
}

(2) named_pipe_read.c
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>
intmain(int argc, char *argv[])
{
char buf[32] = {0};
int fd;
if (argc < 2)
{
printf("Usage:%s <fifo name> \n", argv[0]);
return-1;
}
///開啟有名管道,write已經建立的檔;
fd = open(argv[1], O_RDONLY);
while (1)
{
sleep(1);
read(fd, buf, 32);
printf("buf is %s\n", buf);
memset(buf, 0, sizeof(buf));
}
close(fd);
return0;
}

2.號誌

1) 概念和原理:

號誌是一種計數器,用於控制對共享資源的存取。每次行程存取共享資源時,都要先獲取一個號誌,如果號誌的值大於0,

則行程可以繼續存取,否則行程需要等待。存取完成後,行程會釋放號誌,使其值加1,以便其他行程存取;

2) 相關函式:

linux系統提供如下函式來對號誌值進行操作的,包括的表頭檔為sys/sem.h。

--semget函式:建立一個新號誌或者獲取一個已有的號誌的鍵

--semop函式: 對號誌進行改變,做p或者v操作

--semctl函式:用來直接控制號誌資訊

--刪除號誌:ipcrm -s id

3)例子程式:

---增加號誌的值例子(sempore_add.c )://=============================sempore_add.c==========================
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<stdio.h>
#define KEY 1234
union semun {
int val;
structsemid_ds *buf;
ushort *array;
};
intmain()
{
int semid = semget(KEY, 1, IPC_CREAT | 0666);
if (semid < 0) {
perror("semget error");
return1;
}
union semun arg;
arg.val = 1;
if (semctl(semid, 0, SETVAL, arg) < 0) {
perror("semctl error");
return1;
}
structsembufbuf;
buf.sem_num = 0;
buf.sem_op = 1;
buf.sem_flg = SEM_UNDO;
if (semop(semid, &buf, 1) < 0) {
perror("semop error");
return1;
}
printf("Semaphore value: %d\n", semctl(semid, 0, GETVAL, arg));
return0;
}





---減少號誌的值例子(sempore_sub.c):

//=============================sempore_sub.c==========================
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<stdio.h>
#define KEY 1234
union semun {
int val;
structsemid_ds *buf;
ushort *array;
};
intmain()
{
int semid = semget(KEY, 1, IPC_CREAT | 0666);
if (semid < 0) {
perror("semget error");
return1;
}
union semun arg;
arg.val = 1;
if (semctl(semid, 0, SETVAL, arg) < 0) {
perror("semctl error");
return1;
}
structsembufbuf;
buf.sem_num = 0;
buf.sem_op = -1;
buf.sem_flg = SEM_UNDO;
if (semop(semid, &buf, 1) < 0) {
perror("semop error");
return1;
}
printf("Semaphore value: %d\n", semctl(semid, 0, GETVAL, arg));

////銷毀號誌
if (semctl(semid, 0, IPC_RMID, 0) < 0) {
perror("semctl error");
return1;
}
printf("Semaphore destroyed\n");
return0;
}










3.訊號

(1)訊號概念和原理:

訊號是一種異步通訊方式,當行程接收到一個訊號時,會打斷當前的執行,轉而執行與該訊號相關聯的訊號處理常式。 比較類似軟中斷。但訊號和中斷有所不同,中斷的響應和處理都發生在內核空間,而訊號的響應發生在內核空間, 訊號處理常式的執行卻發生在使用者空間。

Linux提供了許多訊號,如SIGINT、SIGTERM、SIGKILL等,可以在linux系統 Shell 中檢視所有訊號和對應的編號:kill -l

(2)主要函式:

a. void (*signal(int sig,void (*func)(int)))(int);

說明:繫結收到某個訊號後 的回呼函式.第一個參數為訊號,第二個參數為對此訊號掛接使用者自己的處理常式指標。 返回值為以前訊號處理常式的指標。例子:int ret = signal(SIGSTOP, sig_handle);

b. int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);

說明:由於 signal 不夠健壯,推薦使用 sigaction 函式,sigaction 函式重新實作了 signal 函式

c. int kill(pid_t pid,int sig);

說明:kill函式向行程號為pid的行程發送訊號,訊號值為sig。當pid為0時,向當前系統的所有行程發送訊號sig。

kill的pid參數有四種情況:

---pid>0, 則發送訊號sig給行程號為pid的行程

---pid=0,則發送訊號sig給當前行程所屬組中的所有行程

---pid=-1,則發送訊號sig給除1號行程與當前行程外的所有行程

---pid<-1,則發送訊號sig給屬於行程組pid的所有行程

例子:結束父行程 kill(getppid(), SIGKILL);

d. int raise(int sig);

說明: 向當前行程中自舉一個訊號sig, 即向當前行程發送訊號。相當於 kill(getpid(),sig);

e. unsigned int alarm(unsigned int seconds);

說明: 用來設定訊號SIGALRM在經過參數seconds指定的秒數後傳送給目前的行程. 如果參數seconds為0,則之前設定的鬧鐘會被取消,並將剩下的時間返回

f. int sigqueue(pid_t pid, int sig, const union sigval value);

說明:用於向指定的行程發送特定的訊號,並且可以傳遞一個額外的數據值。它提供了比 kill 函式更豐富的功能,可以用於行程間的高級通訊。


(3)例子程式:

-----------------signal_receiver.c ------------------------------
#include<stdio.h>
#include<string.h>
#include<signal.h>
#include<unistd.h>
#include<sys/types.h>
#include<unistd.h>
voidsignal_Handle(int sig, siginfo_t* info, void* ucontext)
{
printf("handler : sig = %d\n", sig);
printf("handler : info->si_signo = %d\n", info->si_signo);
printf("handler : info->si_code = %d\n", info->si_code);
printf("handler : info->si_pid = %d\n", info->si_pid);
printf("handler : info->si_value = %d\n", info->si_value.sival_int);
}
intmain(int argc, char** argv)
{
printf("pid :%d\n", getpid());
structsigactionact = {0};
act.sa_sigaction = signal_Handle;
act.sa_flags = SA_RESTART | SA_SIGINFO;
/* 添加訊號遮蔽字 */
/* 下面訊號在訊號處理常式執行時會被暫時阻塞 */
sigaddset(&act.sa_mask, 40);
sigaddset(&act.sa_mask, SIGINT);
/* 設定訊號的處理行為,設定後40和SIGINT訊號將由act裏面的訊號處理常式處理 */
sigaction(40, &act, NULL);
sigaction(SIGINT, &act, NULL);
while(1)
{
sleep(1);
}
return0;
}







-----------------signal_sender.c ------------------------------
#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<unistd.h>
#include<signal.h>

intmain(int argc, char** argv)
{
pid_t pid = atoi(argv[1]);
union sigval sv = {123456};
//向指定pid發送訊號;
sigqueue(pid, 40, sv);
raise(SIGINT);
return0;
}




4.訊息佇列

(1)概念和原理:

訊息佇列是一種行程間通訊的方式,允許一個行程向另一個行程發送訊息。它是一種異步通訊方式,發送方發送訊息後即可繼續執行,不必等待接收方的響應。

原理如下圖所示:

(2)特點:

---訊息佇列是訊息的連結串列,存放於記憶體中,內核維護訊息佇列;

---訊息佇列中的訊息是有型別和格式的;

---訊息佇列可實作訊息隨機查詢,不一定要遵循先進先出的順序,而是每個行程可以按照自訂的型別進行讀取;微信搜尋公眾號:架構師指南,回復:架構師 領取資料 。

---與管道相同,讀出數據後,訊息佇列對應數據會被刪除;

---每個管道都有訊息佇列識別元,在整個系統中是唯一的;

---訊息佇列允許一個或者多個行程向它寫入或者讀取數據;

---內核重新開機或者人為刪除才會刪除訊息佇列,否則會一直存在與系統中

(3) 相關函式:

a. key_t ftok(const char *pathname, int proj_id);

說明:獲取系統唯一Key值(IPC鍵值),系統中可能會存在許多的訊息佇列,透過Key這個系統唯一值,可以選擇想要進入的訊息佇列;

b. int msgget(key_t key, int msgflg);

說明:建立或者開啟一個新的訊息佇列。即使行程不同,但是如果key值是相同的,那麽也可以進入相同的訊息佇列,返回相同的訊息佇列識別元,

c. 檢視訊息佇列的一些Linux命令:

ipcs -q : 檢視當前行程間通訊之訊息佇列

ipcrm -q 佇列號: 刪除指定的訊息佇列;

d. int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);

說明: 將新訊息添加到訊息佇列;

e. ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);

說明: 從指定的訊息佇列識別元中接受資訊,同時一旦接受成功,從訊息佇列中刪除該資訊。

f. int msgctl(int msqid, int cmd, struct msqid_ds *buf);

說明:對訊息佇列進行修改,修改內容或者刪除訊息佇列等

(3)例子程式:

-----------------message_sender.c ------------------------------
#include<stdio.h>
#include<sys/msg.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
//定義訊息
structmess
{

long type;
char data[128];
};
intmain()
{
key_t key;
///建立生成唯一的key;
if ((key = ftok("/home/tmp", 'a')) == -1) {
perror("ftok");
exit(1);
}

int msgid=msgget((key_t)key,IPC_CREAT|0600);
if(msgid==-1)
{
exit(0);
}
structmessdt;
dt.type=1;
strcpy(dt.data,"hello1");
//1號訊息內容hello1
msgsnd(msgid,(void*)&dt,128,0);//標誌位0
}

-----------------message_reader.c ------------------------------
#include<stdio.h>
#include<sys/msg.h>
#include<stdlib.h>
#include<unistd.h>
structmess
{

long type;
char data[128];
};
intmain()
{
int msgid=msgget((key_t)1235,IPC_CREAT|0600);
if(msgid==-1)
{
exit(0);
}
structmessdt;
msgrcv(msgid,(void*)&dt,128,1,0);
printf("%s",dt.data);
///刪除佇列
if (msgctl(msqid, IPC_RMID, NULL) == -1) {
perror("msgctl");
exit(1);
}
}





5.共享記憶體

(1)概念:

共享記憶體是一種高效的IPC機制,是最快的行程間通訊方式,很多追求效率的程式之間進行通訊的時候都會選擇它;它允許多個行程共享同一個實體記憶體區域,從而避免了數據拷貝和行程切換的開銷。

原理如下圖所示:

(2)共享記憶體的建立與釋放:

---共享記憶體的建立大致包括以下兩個過程:

a.在實體記憶體當中申請共享記憶體空間;

b.將申請到的共享記憶體掛接到地址空間,即建立對映關系;

---共享記憶體的釋放大致包括以下兩個過程:

a. 將共享記憶體與地址空間去關聯,即取消對映關系。

b. 釋放共享記憶體空間,即將實體記憶體歸還給系統。

(3)相關函式:

a.key_t ftok(const char *pathname, int proj_id);

說明:這個返回的key值可以傳給共享記憶體參數,作為struct ipc_perm中唯一標識共享記憶體的key;

b. int shmget(key_t key, size_t size, int shmflg);

說明:共享記憶體的建立;

c. void *shmat(int shmid, const void *shmaddr, int shmflg)

說明:將共享記憶體連線到行程地址空間,shmat函式的第三個參數shmflg有以下三個選項:

SHM_RDONLY: 關聯共享記憶體後只進行讀取操作

SHM_RND:若shmaddr不為NULL,則關聯地址自動向下調整為SHMLBA的整數倍。

0: 預設為讀寫許可權

d. int shmctl(int shmid, int cmd, struct shmid_ds *buf);

說明:控制共享記憶體,cmd如下:

IPC_STAT: 獲取共享記憶體的當前關聯值,此時參數buf作為輸出型參數

IPC_SET: 在行程有足夠許可權的前提下,將共享記憶體的當前關聯值設定為buf所指的數據結構中的值

IPC_RMID: 刪除共享記憶體段;

e. int shmdt(const void *shmaddr)

說明:取消共享記憶體與行程地址空間之間的關聯

(3)記憶體共存相關的系統命令:

---檢視共享記憶體命令:ipcs -m

---刪除共享記憶體命令:ipcrm -m

(4)例子程式:共享記憶體數據讀;

//shm_server.c
-----------------shm_server.c ------------------------------
#include<stdio.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<unistd.h>
#define PATHNAME "/home/IPC/shm/server.c"//路徑名
#define PROJ_ID 0x6666 //整數識別元
intmain()
{
key_t key = ftok(PATHNAME, PROJ_ID); //獲取key值
if (key < 0){
perror("ftok");
return1;
}
int shm = shmget(key, SIZE, IPC_CREAT | IPC_EXCL | 0666); //建立新的共享記憶體
if (shm < 0){
perror("shmget");
return2;
}
printf("key: %x\n", key); //打印key值
printf("shm: %d\n", shm); //打印共享記憶體使用者層id
char* mem = shmat(shm, NULL, 0); //關聯共享記憶體
while (1)
{
//伺服端不斷讀取共享記憶體當中的數據並輸出
while (1)
{
printf("client# %s\n", mem);
sleep(1);
}
}
shmdt(mem); //共享記憶體去關聯
shmctl(shm, IPC_RMID, NULL); //釋放共享記憶體
return0;
}









客戶端: 共享記憶體數據寫;

-----------------shm_client.c ------------------------------
#include<stdio.h>
#include<stdio.h>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<unistd.h>
#define PATHNAME "/home/IPC/shm/server.c"//路徑名
#define PROJ_ID 0x6666 //整數識別元
intmain()
{
key_t key = ftok(PATHNAME, PROJ_ID); //獲取與server行程相同的key值
if (key < 0){
perror("ftok");
return1;
}
int shm = shmget(key, SIZE, IPC_CREAT); //獲取server行程建立的共享記憶體的使用者層id
if (shm < 0){
perror("shmget");
return2;
}
printf("key: %x\n", key); //打印key值
printf("shm: %d\n", shm); //打印共享記憶體使用者層id
char* mem = shmat(shm, NULL, 0); //關聯共享記憶體
int i = 0;
while (1)
{
//客戶端不斷向共享記憶體寫入數據
int i = 0;
while (1)
{
mem[i] = 'A' + i;
i++;
mem[i] = '\0';
sleep(1);
}
}
shmdt(mem); //共享記憶體去關聯
return0;
}






6.套接字

(1)概念和原理:

套接字是一種用於網路通訊的編程介面, 也是一種特殊的IPC通訊機制,一般分為兩種角色:客戶端和伺服器 ,既可以在本機不同行程間通訊,也可以在跨網路不同的多台主機間通訊,可以一對多。

流程如下圖:

(2)相關函式:

a. int tcp_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

說明:建立Socket函式建立一個Socket物件,並指定通訊協定和型別(流式或數據報式)。 IPPROTO_TCP表示TCP協定

b. int bind(int sock, struct sockaddr *addr, socklen_t addrlen); //Linux

說明:繫結地址,使用 bind 函式將 Socket 繫結到一個特定的IP地址和埠號上。

c. listen;

說明:設定監聽,等待接收client端連線請求;

d. int accept(int sock, struct sockaddr *addr, socklen_t *addrlen);

說明:接受連線,對於流式 Socket,使用 accept 函式接受客戶端的連線請求,並返回一個新的Socket物件用於與客戶端進行通訊。 對於數據報式Socket,可以省略此步驟。

e. int connect(int sock, struct sockaddr *serv_addr, socklen_t addrlen);

說明:連線指定IP和埠的伺服器

f. ssize_t send(int sockfd, const void *buf, size_t len, int flags);

或ssize_t write(int fd, const void *buf, size_t nbytes);

說明:數據發送;

g. ssize_t recv(int sockfd, void *buf, size_t len, int flags);

或 ssize_t read(int fd, void *buf, size_t nbytes);

說明:數據接收;

h. int close(int fd) ;

說明:關閉連線,使用 close 函式關閉Socket連線。

(3)例子程式:

本地socket 通訊 伺服端程式:

----------------local_socket_server.c ------------------------------
#include<stdlib.h>
#include<stdio.h>
#include<stddef.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<errno.h>
#define QLEN 10
#define IPC_SOCKET_PATH "ipctest.socket"
intserv_listen(constchar *name)
{
int fd, len, err, rval;
structsockaddr_unun;
/* create a UNIX domain stream socket */
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
return(-1);
/* in case it already exists */
unlink(name);
/* fill in socket address structure */
memset(&un, 0, sizeof(un));
un.sun_family = AF_UNIX;
strcpy(un.sun_path, name);
len = offsetof(struct sockaddr_un, sun_path) + strlen(name);
/* bind the name to the descriptor */
if (bind(fd, (struct sockaddr *)&un, len) < 0) {
rval = -2;
goto errout;
}
if (listen(fd, QLEN) < 0) { /* tell kernel we're a server */
rval = -3;
goto errout;
}
return(fd);
errout:
err = errno;
close(fd);
errno = err;
return(rval);
}
intserv_accept(int listenfd, uid_t *uidptr)
{
int clifd, len, err, rval;
time_t staletime;
structsockaddr_unun;
structstatstatbuf;
len = sizeof(un);
if ((clifd = accept(listenfd, (struct sockaddr *)&un, &len)) < 0)
return(-1); /* often errno=EINTR, if signal caught */
/* obtain the client's uid from its calling address */
len -= offsetof(struct sockaddr_un, sun_path); /* len of pathname */
un.sun_path[len] = 0; /* null terminate */
if (stat(un.sun_path, &statbuf) < 0) {
rval = -2;
goto errout;
}
if (S_ISSOCK(statbuf.st_mode) == 0) {
rval = -3; /* not a socket */
goto errout;
}
if (uidptr != NULL)
*uidptr = statbuf.st_uid; /* return uid of caller */
/* we're done with pathname now */
unlink(un.sun_path);
return(clifd);
errout:
err = errno;
close(clifd);
errno = err;
return(rval);
}
///////////////////////////main ////////////////////////////////
intmain(void)
{
int lfd, cfd, n, i;
uid_t cuid;
char buf[1024];
lfd = serv_listen(IPC_SOCKET_PATH);
if (lfd < 0) {
switch (lfd) {
case-3:perror("listen"); break;
case-2:perror("bind"); break;
case-1:perror("socket"); break;
}
exit(-1);
}
cfd = serv_accept(lfd, &cuid);
if (cfd < 0) {
switch (cfd) {
case-3:perror("not a socket"); break;
case-2:perror("a bad filename"); break;
case-1:perror("accept"); break;
}
exit(-1);
}
while (1)
{
n = read(cfd, buf, 1024);
if (n == -1) {
if (errno == EINTR)
break;
}
elseif (n == 0) {
printf("the other side has been closed.\n");
break;
}
////send back data to client
for (i = 0; i < n; i++)
{buf[i] = toupper(buf[i]);
write(cfd, buf, n);
}
}
close(cfd);
close(lfd);
return0;
}















本地socket 通訊 客戶端程式:

----------------local_socket_client.c ------------------------------

#include<stdio.h>
#include<stdlib.h>
#include<stddef.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<errno.h>
#define CLI_PATH "/var/tmp/"/* +5 for pid = 14 chars */
#define IPC_SOCKET_PATH "ipctest.socket"
/*
* Create a client endpoint and connect to a server.
* Returns fd if all OK, <0 on error.
*/

intcli_conn(constchar *name)
{
int fd, len, err, rval;
structsockaddr_unun;
/* create a UNIX domain stream socket */
if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
return(-1);
/* fill socket address structure with our address */
memset(&un, 0, sizeof(un));
un.sun_family = AF_UNIX;
sprintf(un.sun_path, "%sd", CLI_PATH, getpid());
len = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path);
/* in case it already exists */
unlink(un.sun_path);
if (bind(fd, (struct sockaddr *)&un, len) < 0) {
rval = -2;
goto errout;
}
/* fill socket address structure with server's address */
memset(&un, 0, sizeof(un));
un.sun_family = AF_UNIX;
strcpy(un.sun_path, name);
len = offsetof(struct sockaddr_un, sun_path) + strlen(name);
if (connect(fd, (struct sockaddr *)&un, len) < 0) {
rval = -4;
goto errout;
}
return(fd);
errout:
err = errno;
close(fd);
errno = err;
return(rval);
}
///////////////////////////main ////////////////////////////////
intmain(void)
{
int fd, n;
char buf[1024];
fd = cli_conn(IPC_SOCKET_PATH);
if (fd < 0) {
switch (fd) {
case-4:perror("connect"); break;
case-3:perror("listen"); break;
case-2:perror("bind"); break;
case-1:perror("socket"); break;
}
exit(-1);
}
while (fgets(buf, sizeof(buf), stdin) != NULL) {
write(fd, buf, strlen(buf));
n = read(fd, buf, sizeof(buf));
write(STDOUT_FILENO, buf, n);
}
close(fd);
return0;
}







不同主機端的套接字IPC通訊屬於網路通訊話題,這裏就不再詳細論述了。

-End-

讀到這裏說明你喜歡本公眾號的文章,歡迎 置頂(標星)本公眾號 Linux技術迷,這樣就可以第一時間獲取推播了~

本公眾號,後台回復:Linux,領取2T學習資料 !

1. 

2. 

3.

4.