操作系统里的进程通讯方式有6种:(有名/匿名)管道、信号、消息队列、信号量、内存(最快)、套接字(最常用),这里我们来介绍用socket来实现进程通讯。
1、简单实现一个单向发送与接收
这是套接字的工作流程
(对于有时间想慢慢看的推荐这篇博客:https://www.cnblogs.com/kefeiGame/p/7246942.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);中的参数想要详细了解可以看这篇博客:https://blog.csdn.net/liuxingen/article/details/44995467 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 %s ", 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 }
客户端(虚拟机[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] = '