C++ socket编程 虚拟机与主机 简单通讯

操作系统里的进程通讯方式有6种:(有名/匿名)管道、信号、消息队列、信号量、内存、套接字。

这是套接字的工作流程

(对于有时间想慢慢看的推荐这篇博客: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 "192.168.50.128"///你用的服务器的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\n", 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()无异
67             /// MSG_DONTROUTE:告诉内核,目标主机在本地网络,不用查路由表
68             /// MSG_DONTWAIT:将单个I/O操作设置为非阻塞模式
69             /// MSG_OOB:指明发送的是带外信息
70 
71             perror("send():");
72             close(new_fd);///发送失败就关闭该通讯
73             return 0;
74         }
75         if(!strcmp("endS", str))
76             break;
77     }
78     close(new_fd);///正常结束要关闭这些已建立的套接字
79     close(sockfd);
80 
81     return 0;
82 }
83 
84 linux环境的服务端
linux环境的服务端

 客户端(虚拟机[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 PORT 1223/// 客户机连接远程主机的端口
 8 #define MAXDATASIZE 100 /// 每次可以接收的最大字节
 9 #define IP "192.168.50.128"
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] = '\0';
42         if(!strcmp(buf, "endS")) {///接收到endS两边一起结束
43             break;
44         }
45         cout<<"Received: "<<buf<<endl;///输出接收的字符
46     }
47     close(sockfd);
48     return 0;
49 
50 }
linux环境的客户端

接下来我们把这个客户端移植到windows操作系统下,代码肯定是要有小改动的。但是这个是最后的操作,我们一步步来:

让虚拟机和本机能够ping通(这个我一开始在网络上找 博客,然后都没用,后面把虚拟机的虚拟网络编辑器恢复默认就可以了,所以说建议自己尝试解决);

因为主机和虚拟机可以用IP地址(IPv4)ping通,也就是可以访问该ip,那么我们的服务器就要在那个客户端(主机)可访问的IP上拿一个端口出来用来通讯。

所以说我们服务器的IP地址要选ifconfig指令里看到的虚拟机里的IPv4地址。

接下来开始移植,其实基本思想和代码结构完全没变。

客户端(windows)

 1 #include <iostream>
 2 #include <stdlib.h>
 3 #include <winsock2.h>
 4 #pragma comment(lib,"ws2_32.lib")
 5 ///我在codeblocks下不可以运行这些是因为这个libws2_32.a找不到
 6 ///解决方法:Settings->compiler->Global compiler settings->(找到)Linker settings(横着排开的目录)->Add->去MinGW/lib找到libws2_32.a就可以了
 7 
 8 
 9 #define PORT 1223/// 客户机连接远程主机的端口
10 #define MAXDATASIZE 100 /// 每次可以接收的最大字节
11 using namespace std;
12 
13 int main( ) {
14     WORD sockVersion = MAKEWORD(2,2);
15     WSADATA wsaData;
16     if(WSAStartup(sockVersion, &wsaData)!=0){
17         return 0;
18     }
19     ///windows环境下的Winsock是要初始化的;即:固定代码。
20 
21     int sockfd, numbytes;
22     char buf[MAXDATASIZE];
23     struct hostent *he;
24     struct sockaddr_in their_addr;
25 
26     puts("USER:");
27     if((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1){
28         cout<<WSAGetLastError()<<endl;///这个可以输出WSAError号
29         perror("socket");
30         return 0;
31     }
32 
33     their_addr.sin_family = AF_INET;
34     their_addr.sin_port = htons(PORT);
35 
36     their_addr.sin_addr.s_addr = inet_addr("192.168.50.128");
37     memset(their_addr.sin_zero, 0, sizeof(their_addr.sin_zero));
38     if(connect(sockfd,(struct sockaddr *)&their_addr,sizeof(struct sockaddr)) == -1) {
39         perror("connect");
40         return 0;
41     }
42     while(1) {
43         if((numbytes=recv(sockfd, buf, MAXDATASIZE, 0)) == -1) {
44             perror("recv");
45             return 0;
46         }
47         buf[numbytes] = '\0';
48         if(!strcmp(buf, "endS")) {
49             break;
50         }
51         cout<<"Received: "<<buf<<endl;
52     }
53     closesocket(sockfd);///函数不同
54     return 0;
55 }
windows环境的客户端

试着通讯,应该是没问题的!!(至少本地没问题)

猜你喜欢

转载自www.cnblogs.com/DCD112358/p/10522172.html