日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

UNIX域套接字编程和socketpair 函数

發布時間:2025/3/17 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 UNIX域套接字编程和socketpair 函数 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、UNIX Domain Socket IPC

socket API原本是為網絡通訊設計的,但后來在socket的框架上發展出一種IPC機制,就是UNIX Domain Socket。雖然網絡socket也可用于同一臺主機的進程間通訊(通過loopback地址127.0.0.1),但是UNIX Domain Socket用于IPC更有效率:不需要經過網絡協議棧,不需要打包拆包、計算校驗和、維護序號和應答等,只是將應用層數據從一個進程拷貝到另一個進程。UNIX域套接字與TCP套接字相比較,在同一臺主機的傳輸速度前者是后者的兩倍。這是因為,IPC機制本質上是可靠的通訊,而網絡協議是為不可靠的通訊設計的。UNIX Domain Socket也提供面向流和面向數據包兩種API接口,類似于TCP和UDP,但是面向消息的UNIX Domain Socket也是可靠的,消息既不會丟失也不會順序錯亂。


使用UNIX Domain Socket的過程和網絡socket十分相似,也要先調用socket()創建一個socket文件描述符,address family指定為AF_UNIX,type可以選擇SOCK_DGRAM或SOCK_STREAM,protocol參數仍然指定為0即可。

UNIX Domain Socket與網絡socket編程最明顯的不同在于地址格式不同,用結構體sockaddr_un表示,網絡編程的socket地址是IP地址加端口號,而UNIX Domain Socket的地址是一個socket類型的文件在文件系統中的路徑,這個socket文件由bind()調用創建,如果調用bind()時該文件已存在,則bind()錯誤返回。

#define UNIX_PATH_MAX ? ?108

struct sockaddr_un {
sa_family_t sun_family; ? ? ? ? ? ? ? /* AF_UNIX */
char sun_path[UNIX_PATH_MAX]; ?/* pathname */
};


二、回射/客戶服務器程序

通信的流程跟前面說過的tcp/udp 是類似的,下面直接來看程序:

?

C++ Code?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
? /*************************************************************************
????>?File?Name:?echoser_tcp.c
????>?Author:?Simba
????>?Mail:?dameng34@163.com
????>?Created?Time:?Sun?03?Mar?2013?06:13:55?PM?CST
?************************************************************************/

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<string.h>
#include<sys/un.h>

#define?ERR_EXIT(m)?\
????do?{?\
????????perror(m);?\
????????exit(EXIT_FAILURE);?\
????}?while?(0)

void?echo_ser(int?conn)
{
????char?recvbuf[1024];
????int?n;
????while?(1)
????{

????????memset(recvbuf,?0,?sizeof(recvbuf));
????????n?=?read(conn,?recvbuf,?sizeof(recvbuf));
????????if?(n?==?-1)
????????{
????????????if?(n?==?EINTR)
????????????????continue;

????????????ERR_EXIT("read?error");
????????}

????????else?if?(n?==?0)
????????{
????????????printf("client?close\n");
????????????break;
????????}

????????fputs(recvbuf,?stdout);
????????write(conn,?recvbuf,?strlen(recvbuf));
????}

????close(conn);
}

/*?unix?domain?socket與TCP套接字相比較,在同一臺主機的傳輸速度前者是后者的兩倍。*/
int?main(void)
{
????int?listenfd;
????if?((listenfd?=?socket(PF_UNIX,?SOCK_STREAM,?0))?<?0)
????????ERR_EXIT("socket?error");

????unlink("/tmp/test?socket");?//地址復用
????struct?sockaddr_un?servaddr;
????memset(&servaddr,?0,?sizeof(servaddr));
????servaddr.sun_family?=?AF_UNIX;
????strcpy(servaddr.sun_path,?"/tmp/test?socket");

????if?(bind(listenfd,?(struct?sockaddr?*)&servaddr,?sizeof(servaddr))?<?0)
????????ERR_EXIT("bind?error");

????if?(listen(listenfd,?SOMAXCONN)?<?0)
????????ERR_EXIT("listen?error");

????int?conn;
????pid_t?pid;

????while?(1)
????{

????????conn?=?accept(listenfd,?NULL,?NULL);
????????if?(conn?==?-1)
????????{

????????????if?(conn?==?EINTR)
????????????????continue;
????????????ERR_EXIT("accept?error");
????????}

????????pid?=?fork();
????????if?(pid?==?-1)
????????????ERR_EXIT("fork?error");
????????if?(pid?==?0)
????????{
????????????close(listenfd);
????????????echo_ser(conn);
????????????exit(EXIT_SUCCESS);
????????}

????????close(conn);
????}

????return?0;
}


?

C++ Code?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
? /*************************************************************************
????>?File?Name:?echocli_tcp.c
????>?Author:?Simba
????>?Mail:?dameng34@163.com
????>?Created?Time:?Sun?03?Mar?2013?06:13:55?PM?CST
?************************************************************************/

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<string.h>
#include<sys/un.h>

#define?ERR_EXIT(m)?\
????do?{?\
????????perror(m);?\
????????exit(EXIT_FAILURE);?\
????}?while?(0)

void?echo_cli(int?conn)
{
????char?sendbuf[1024]?=?{0};
????char?recvbuf[1024]?=?{0};
????while?(fgets(sendbuf,?sizeof(sendbuf),?stdin)?!=?NULL)
????{


????????write(conn,?sendbuf,?strlen(sendbuf));
????????read(conn,?recvbuf,?sizeof(recvbuf));
????????fputs(recvbuf,?stdout);
????????memset(recvbuf,?0,?sizeof(recvbuf));
????????memset(sendbuf,?0,?sizeof(sendbuf));
????}

????close(conn);
}


int?main(void)
{
????int?sock;
????if?((sock?=?socket(PF_UNIX,?SOCK_STREAM,?0))?<?0)
????????ERR_EXIT("socket?error");

????struct?sockaddr_un?servaddr;
????memset(&servaddr,?0,?sizeof(servaddr));
????servaddr.sun_family?=?AF_UNIX;
????strcpy(servaddr.sun_path,?"/tmp/test?socket");

????if?(connect(sock,?(struct?sockaddr?*)&servaddr,?sizeof(servaddr))?<?0)
????????ERR_EXIT("connect?error");

????echo_cli(sock);

????return?0;
}


server 使用fork 的形式來接受多個連接,server調用bind 會創建一個文件,如下所示:

simba@ubuntu:~/Documents/code/linux_programming/UNP/socket$ ls -l /tmp/test\ socket?
srwxrwxr-x 1 simba simba 0 Jun 12 15:27 /tmp/test socket

即文件類型為s,表示SOCKET文件,與FIFO(命名管道)文件,類型為p,類似,都表示內核的一條通道,讀寫文件實際是在讀寫內核通道。程序中調用unlink(解除硬鏈接) 是為了在開始執行程序時刪除以前創建的文件,以便在重啟服務器時不會提示address in use。其他方面與以前說過的回射客戶服務器程序沒多大區別,不再贅述。


三、UNIX域套接字編程注意點

1、bind成功將會創建一個文件,權限為0777 & ~umask
2、sun_path最好用一個絕對路徑
3、UNIX域協議支持流式套接口與報式套接口
4、UNIX域流式套接字connect發現監聽隊列滿時,會立刻返回一個ECONNREFUSED,這和TCP不同,如果監聽隊列滿,會忽略到來的SYN,這導致對方重傳SYN。


四、socketpair 函數

功能:創建一個全雙工的流管道
原型 int socketpair(int domain, int type, int protocol, int sv[2]);
參數
domain: 協議家族
type: 套接字類型
protocol: 協議類型
sv: 返回套接字對
返回值:成功返回0;失敗返回-1

實際上socketpair 函數跟pipe 函數是類似的,也只能在同個主機上具有親緣關系的進程間通信,但pipe 創建的匿名管道是半雙工的,而socketpair 可以認為是創建一個全雙工的管道。

可以使用socketpair 創建返回的套接字對進行父子進程通信:

?

C++ Code?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
? /*************************************************************************
????>?File?Name:?echoser.c
????>?Author:?Simba
????>?Mail:?dameng34@163.com
????>?Created?Time:?Fri?01?Mar?2013?06:15:27?PM?CST
?************************************************************************/

#include<stdio.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<unistd.h>
#include<stdlib.h>
#include<errno.h>
#include<arpa/inet.h>
#include<netinet/in.h>
#include<string.h>

#define?ERR_EXIT(m)?\
????do?{?\
????????perror(m);?\
????????exit(EXIT_FAILURE);?\
????}?while?(0)


int?main(void)
{
????int?sockfds[2];

????if?(socketpair(PF_UNIX,?SOCK_STREAM,?0,?sockfds)?<?0)
????????ERR_EXIT("sockpair");

????pid_t?pid;
????pid?=?fork();
????if?(pid?==?-1)
????????ERR_EXIT("fork");

????if?(pid?>?0)
????{
????????int?val?=?0;
????????close(sockfds[1]);
????????while?(1)
????????{

????????????++val;
????????????printf("?sending?data:?%d\n",?val);
????????????write(sockfds[0],?&val,?sizeof(val));
????????????read(sockfds[0],?&val,?sizeof(val));
????????????printf("recv?data?:?%d\n",?val);
????????????sleep(1);
????????}

????}

????else?if?(pid?==?0)
????{

????????int?val;
????????close(sockfds[0]);
????????while?(1)
????????{

????????????read(sockfds[1],?&val,?sizeof(val));
????????????++val;
????????????write(sockfds[1],?&val,?sizeof(val));
????????}
????}

????return?0;
}


輸出如下:

simba@ubuntu:~/Documents/code/linux_programming/UNP/socket$ ./socketpair?
?sending data: 1
recv data : 2
?sending data: 3
recv data : 4
?sending data: 5
recv data : 6
?sending data: 7
recv data : 8
?sending data: 9
recv data : 10
?sending data: 11
recv data : 12
?sending data: 13
recv data : 14
?sending data: 15
recv data : 16
...................................


即父進程持有sockfds[0] 套接字進行讀寫,而子進程持有sockfds[1] 套接字進行讀寫。


?

參考:

《Linux C 編程一站式學習》

《TCP/IP詳解 卷一》

《UNP》

轉載于:https://www.cnblogs.com/alantu2018/p/8472989.html

總結

以上是生活随笔為你收集整理的UNIX域套接字编程和socketpair 函数的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。