
操作系统里的进程通讯方式有6种:(有名/匿名)管道、信号、消息队列、信号量、内存(最快)、套接字(最常用),这里我们来介绍用socket来实现进程通讯。
1、简单实现一个单向发送与接收
这是套接字的工作流程
(对于有时间想慢慢看的推荐这篇博客:.html)
(不想自己画一遍,直接用别人的)
我们现在先来实现套接字对同一主机的通讯。(代码注释比较全)
服务器(虚拟机[Ubuntu]):
1 #include <unistd.h>
2 #include <string.h>
3 #include <iostream>
4 #include <arpa/inet.h>
5 #include <sys/socket.h>
6
7 #define MYPORT 1223///开应一个端口
8 #define IP "**.**.**.**"///你用的服务器的IPv4地址,这里我用了虚拟机(ubuntu)的地址
9 #define BACKLOG 10
10 #define getLen(zero) sizeof(zero) / sizeof(zero[0]) ///得到数组最大大小
11 using namespace std;
12
13 int main() {
14 int sockfd, new_fd;
15 struct sockaddr_in my_addr;
16 puts("SERVER:");
17 if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ) {
18 ///socket()函数发生错误则返回-1,否则会返回套接字文件描述符
19 ///对于int socket(int domain, int type, int protocol);中的参数想要详细了解可以看这篇博客:
20
21 perror("socket():");///显示错误
22 return 0;
23 }
24 my_addr.sin_family = AF_INET;///通讯在IPv4网络通信范围内
25 my_addr.sin_port = htons(MYPORT);///我的端口
26 my_addr.sin_addr.s_addr = inet_addr(IP);///用来得到一个32位的IPv4地址,inet_addr将"127.0.0.1"转换成s_addr的无符号整型。
27 bzero(&(my_addr.sin_zero), getLen(my_addr.sin_zero));///sin_zero是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。
28
29 /**
30 借用以下代码得到了my_addr.sin_addr.s_addr的类型是无符号整型
31 unsigned int a;
32 if(typeid(a) == typeid(my_addr.sin_addr.s_addr)){
33 puts("Yes");
34 }
35 **/
36
37
38 if(bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)) == -1) {///bind()函数将套接字与该IP:端口绑定起来。
39 perror("bind():");
40 return 0;
41 }
42 if(listen(sockfd, BACKLOG) == -1) {///启动监听,等待接入请求,BACKLOG是在进入队列中允许的连接数目
43 perror("listen():");
44 return 0;
45 }
46
47 socklen_t sin_size;
48 struct sockaddr_in their_addr;
49 if((new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size)) == -1) {
50 ///当你监听到一个来自客户端的connect请求时,要选择是将他放在请求队列里还是允许其连接,我这里写的其实是单进客户的,所以说无等待。
51 ///这个函数还返回了一个新的套接字,用于与该进程通讯。
52 ///还有一点是之前推荐的c++中的socket编程(入门),该博客里写的sin_size类型是int,可是实际上我在linux的C++环境下出现错误,类型要是socklen_t。
53 perror("accept():");
54 return 0;
55 }
56 printf("server: got connection from %sn", inet_ntoa(their_addr.sin_addr));///inet_ntoa可以将inet_addr函数得到的无符号整型转为字符串IP
57
58 char str[1007];
59
60 while(1) {///循环发送 以endS结束与这一进程的通讯,endS也作为客户端停止工作的标志送出
61 puts("send:");
62 scanf("%s", str);
63 if(send(new_fd, str, strlen(str), 0) == -1) {
64 ///send()函数,new_fd是accept返回的套接字文件描述符,str就你要发送的数据,数据长度,对于最后一位flag
65 /// flags取值有:
66 /// 0: 与write()无异(我自己也不知道什么意思,大概就是常规操作,以下提供几种flag值的定义,然后下面是这类宏定义的源码)
67
68 /// MSG_DONTROUTE 绕过路由表查找。
69 /// MSG_DONTWAIT 仅本操作非阻塞。
70 /// MSG_OOB 发送或接收带外数据。
71 /// MSG_PEEK 窥看外来消息。
72 /// MSG_WAITALL 等待所有数据。
73 ///
74 /// 源码里没找到0x00的定义,所以说我将其当作默认参数
75 /// enum
76 /// {
77 /// MSG_OOB = 0x01, /// Process out-of-band data.
78 /// #define MSG_OOB MSG_OOB
79 /// MSG_PEEK = 0x02, /// Peek at incoming messages.
80 /// #define MSG_PEEK MSG_PEEK
81 /// MSG_DONTROUTE = 0x04, /// Don't use local routing.
82 /// #define MSG_DONTROUTE MSG_DONTROUTE
83 /// #ifdef __USE_GNU
84 /// /// DECnet uses a different name.
85 /// MSG_TRYHARD = MSG_DONTROUTE,
86 /// # define MSG_TRYHARD MSG_DONTROUTE
87 /// #endif
88 /// MSG_CTRUNC = 0x08, /// Control data lost before delivery.
89 /// #define MSG_CTRUNC MSG_CTRUNC
90 /// MSG_PROXY = 0x10, /// Supply or ask second address.
91 /// #define MSG_PROXY MSG_PROXY
92 /// MSG_TRUNC = 0x20,
93 /// #define MSG_TRUNC MSG_TRUNC
94 /// MSG_DONTWAIT = 0x40, /// Nonblocking IO.
95 /// #define MSG_DONTWAIT MSG_DONTWAIT
96 /// MSG_EOR = 0x80, /// End of record.
97 /// #define MSG_EOR MSG_EOR
98 /// MSG_WAITALL = 0x100, /// Wait for a full request.
99 /// #define MSG_WAITALL MSG_WAITALL
100 /// MSG_FIN = 0x200,
101 /// #define MSG_FIN MSG_FIN
102 /// MSG_SYN = 0x400,
103 /// #define MSG_SYN MSG_SYN
104 /// MSG_CONFIRM = 0x800, /// Confirm path validity.
105 /// #define MSG_CONFIRM MSG_CONFIRM
106 /// MSG_RST = 0x1000,
107 /// #define MSG_RST MSG_RST
108 /// MSG_ERRQUEUE = 0x2000, /// Fetch message from error queue.
109 /// #define MSG_ERRQUEUE MSG_ERRQUEUE
110 /// MSG_NOSIGNAL = 0x4000, /// Do not generate SIGPIPE.
111 /// #define MSG_NOSIGNAL MSG_NOSIGNAL
112 /// MSG_MORE = 0x8000, /// Sender will send more.
113 /// #define MSG_MORE MSG_MORE
114 /// MSG_WAITFORONE = 0x10000, /// Wait for at least one packet to return.
115 /// #define MSG_WAITFORONE MSG_WAITFORONE
116 /// MSG_BATCH = 0x40000, /// sendmmsg: more messages coming.
117 /// #define MSG_BATCH MSG_BATCH
118 /// MSG_ZEROCOPY = 0x4000000, /// Use user data in kernel path.
119 /// #define MSG_ZEROCOPY MSG_ZEROCOPY
120 /// MSG_FASTOPEN = 0x20000000, /// Send data in TCP SYN.
121 /// #define MSG_FASTOPEN MSG_FASTOPEN
122 ///
123 /// MSG_CMSG_CLOEXEC = 0x40000000 /// Set close_on_exit for file
124 /// ///descriptor received through
125 /// ///SCM_RIGHTS.
126 /// #define MSG_CMSG_CLOEXEC MSG_CMSG_CLOEXEC
127 /// };
128
129 perror("send():");
130 close(new_fd);///发送失败就关闭该通讯
131 return 0;
132 }
133 if(!strcmp("endS", str))
134 break;
135 }
136 close(new_fd);///正常结束要关闭这些已建立的套接字
137 close(sockfd);
138
139 return 0;
140 } linux环境的服务端 客户端(虚拟机[Ubuntu]):(linux环境的客户端)
1 #include <unistd.h>
2 #include <string.h>
3 #include <iostream>
4 #include <arpa/inet.h>
5 #include <sys/socket.h>
6
7 #define PORT 1223/// 客户机连接远程主机的端口
8 #define MAXDATASIZE 100 /// 每次可以接收的最大字节
9 #define IP "**.**.**.**"
10 #define getLen(zero) sizeof(zero)/sizeof(zero[0])
11 using namespace std;
12
13 int main( ) {
14
15 int sockfd, numbytes;
16 char buf[MAXDATASIZE];///缓存接收内容
17 struct sockaddr_in their_addr;///和my_addr用法差不多
18
19 puts("USER:");
20 if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
21 perror("socket():");
22 return 0;
23 }
24
25 their_addr.sin_family = AF_INET;
26 their_addr.sin_port = htons(PORT);
27
28 their_addr.sin_addr.s_addr = inet_addr(IP);
29 bzero(&(their_addr.sin_zero),getLen(their_addr.sin_zero));
30 if(connect(sockfd,(struct sockaddr *)&their_addr,sizeof(struct sockaddr)) == -1) {
31 ///在客户端这里我们不需要绑定什么东西,因为我们只要向目的IP:端口发起连接请求
32
33 perror("connect():");
34 return 0;
35 }
36 while(1) {///循环接收
37 if((numbytes=recv(sockfd, buf, MAXDATASIZE, 0)) == -1) {///recv函数,套接字文件描述符,接收到这字符串里,最大长度,flag(之前有解释);
38 perror("recv():");
39 return 0;
40 }
41 buf[numbytes] = '