最近要做一个东西,关于一个软件上得到的信号,如何通过连接的局域网,将数据传输出去。我没做过相关的东西,但是我想应该和软件连接数据库的过程大致是差不多的,就一个ip和一个端口号啥的。
一.问题思路
多个设备同时连接同一个局域网,在一个电脑上发送一个数据,在别的电脑上可以实时接收这个数据。在找工作面试的时候,提前背了相关tcp、udp的特性,由于tcp是面向连接的,虽然传输数据是可靠的,但是速率是比udp慢的。因此,我想了想使用udp传输是比较合适的,这个地方要用到socket编程。当时面试只是背了背什么c++版本的socket编程,其大致和java之中的socket类似,但是还没有用过,趁着这个机会学一下。
二.ip设置与端口的设置
首先,查看本机ip。
按下windows键 + r ,打开运行,输入cmd。
在运行框里输入ipconfig(都是基本操作),查看ip地址。
可以看到我的接收端的ip地址是192.168.1.144.
我现在自己的电脑上进行试一下。
接收端代码:
#define _winsock_deprecated_no_warnings
#include <iostream>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
int main() {
// 初始化winsock
wsadata wsadata;
if (wsastartup(makeword(2, 2), &wsadata) != 0) {
std::cout << "初始化套接字" << std::endl;
return -1;
}
// 创建套接字
socket receiversocket = socket(af_inet, sock_dgram, 0);
if (receiversocket == invalid_socket) {
std::cout << "创建套接字失败" << std::endl;
wsacleanup();
return -1;
}
// 绑定监听地址
sockaddr_in receiveraddress;
receiveraddress.sin_family = af_inet;
receiveraddress.sin_port = htons(1234); // 设置监听端口号
receiveraddress.sin_addr.s_addr = inaddr_any;
if (bind(receiversocket, (sockaddr*)&receiveraddress, sizeof(receiveraddress)) == socket_error) {
std::cout << "绑定套接字失败" << std::endl;
closesocket(receiversocket);
wsacleanup();
return -1;
}
// 接收数据
char buffer[1024];
sockaddr_in senderaddress;
int senderaddresssize = sizeof(senderaddress);
int receivedbytes = recvfrom(receiversocket, buffer, sizeof(buffer), 0, (sockaddr*)&senderaddress, &senderaddresssize);
if (receivedbytes == socket_error) {
std::cout << "接收数据失败" << std::endl;
}
else {
buffer[receivedbytes] = '\0';
std::cout << "接收到的数据: " << buffer << std::endl;
}
// 清理资源
closesocket(receiversocket);
wsacleanup();
getchar();
return 0;
}
发送端代码:
#define _winsock_deprecated_no_warnings
#include <iostream>
#include <winsock2.h>
#pragma comment(lib, "ws2_32.lib")
int main() {
// 初始化winsock
wsadata wsadata;
if (wsastartup(makeword(2, 2), &wsadata) != 0) {
std::cout << "初始化socket失败" << std::endl;
return -1;
}
// 创建套接字
socket sendersocket = socket(af_inet, sock_dgram, 0);
if (sendersocket == invalid_socket) {
std::cout << "创建socket失败" << std::endl;
wsacleanup();
return -1;
}
// 设置接收方地址
sockaddr_in receiveraddress;
receiveraddress.sin_family = af_inet;
receiveraddress.sin_port = htons(1234); // 设置接收方的监听端口号
receiveraddress.sin_addr.s_addr = inet_addr("192.168.1.144");
// 发送数据
const char* message = "你好呀!小杨同学";
if (sendto(sendersocket, message, strlen(message), 0, (sockaddr*)&receiveraddress, sizeof(receiveraddress)) == socket_error) {
std::cout << "发送数据失败" << std::endl;
}
else {
std::cout << "成功发送数据" << std::endl;
}
// 清理资源
closesocket(sendersocket);
wsacleanup();
return 0;
}
最终的结果,可以看到两台电脑正常通信:
三.问题的出现
一台机器上进行通信是完全没有问题的,但是当两台机器进行通信的时候,其中一台给另外一台发送消息,是正常的,但是当反过来的时候发现通信不了。
我的防火墙已经关闭了,应该没啥问题哈,直接ping一下看看
但是ping了一下发现可以ping通呀。
四.问题的解决
解决方案一:防火墙关闭---端口
接下来去看一下相应的端口状态:使用windows + r,打开任务管理器 -> 性能
性能 -> 打开资源监视器
查看端口的权限情况
可以发现我的监听端口一栏之中,udp完全是不允许的。
那就继续深究一下端口的问题,发现前面的那个防火墙状态只是用来防病毒的,并不是用来监视端口的。首先打开控制面板
启用或者关闭防火墙
关闭防火墙
发现还是不可以。
查看端口信息,可以看到1235端口并没有指定ipv4。
那么我们需要给1235端口号指定一个ipv4的地址。
这个地方我就直接将端口号改为了本机端口:
发现偶尔可以接收到消息,有的时候还是接收不到消息。
解决方案二:多次传输
想一下udp的传输协议,这是不可靠的传输协议,因此,重新修改代码,发现与上面那个地址是否绑定无关。
接收端:我将信号变为一直接收的状态
while (1)
{
// 接收数据
char buffer[1024];
sockaddr_in senderaddress;
int senderaddresssize = sizeof(senderaddress);
int receivedbytes = recvfrom(receiversocket, buffer, sizeof(buffer), 0, (sockaddr*)&senderaddress, &senderaddresssize);
if (receivedbytes == socket_error) {
std::cout << "接收数据失败" << std::endl;
}
else {
buffer[receivedbytes] = '\0';
std::cout << "接收到的数据: " << buffer << std::endl;
}
}
发送端:将信号发送10次。
// 发送数据
const char* message = "你好呀!小杨同学";
for (int i = 0; i < 10; i++)
{
if (sendto(sendersocket, message, strlen(message), 0, (sockaddr*)&receiveraddress, sizeof(receiveraddress)) == socket_error) {
std::cout << "发送数据失败" << std::endl;
}
else {
std::cout << "成功发送数据" << std::endl;
}
}
结果:发送了10次数据,只接收到了八个数据。
通过上述数据说明,在进行使用的过程之中,既然寻求速度选择了udp发送数据,第一个注意点是需要将防火墙进行关闭。第二个注意点是注意数据的丢失情况,只进行发送一次可能出现发送不成功的问题。
补充解决方案三:方案一的替代版
一般而言,电脑上的防火墙是不允许关闭的,因此,有没有什么更好的方案去替代方案一。
将防火墙进行打开, 防火墙 -> 高级设置
入站规则 -> 新建规则
端口
只允许连接
随便起个名字
可以看到也是正常使用的。
五.界面设计
(1)发送端
其中,三个输入框之中的变量绑定分别为
cipaddressctrl m_ip; // ip
cedit m_port; // 端口号
cedit m_edit; // 发送数据
将中间的最大的发送数据的编辑框设置属性
需要注意在相应的属性设置栏之中,不使用预编译头文件,否则会与c6990错误产生冲突。
发送端代码进行测试:
可以发现是能够进行正常的使用的。
(2)接收端
接收端的代码就不详细解释了,详细的资源可以在博客之中进行下载。
最终的测试界面是正确的。
代码下载地址:
发表评论