Windows编程技术:Socket通信(下)

本文转载自【微信公众号:MicroPest ,ID:gh_696c36c5382b】,经微信公众号授权转载,如需转载原文作者联系

UDP面向报文传输数据,在数据传输过程中不能保证可靠性,可能会出现丢包的情况。由于UDP是一种无连接传输方式,所以支持一对一、一对多、多对一和多对多的交互通信。

一、函数介绍

1.1 sendto()

int sendto(int s, const void *buf, int len, unsigned int flags,const struct sockaddr *to, int tolen);

返回值说明:

成功则返回实际传送出去的字符数,失败返回-1,错误原因会存于errno 中。

参数说明:

s: socket描述符;  buf: UDP数据报缓存区(包含待发送数据);  len: UDP数据报的长度;  flags:调用方式标志位(一般设置为0);  to:  指向接收数据的主机地址信息的结构体(sockaddr_in需类型转换);  tolen:to所指结构体的长度;

1.2 recvfrom()

int recvfrom(int s, void *buf, int len, unsigned int flags,struct sockaddr *from, int *fromlen);

返回值说明:

成功则返回实际接收到的字符数,失败返回-1,错误原因会存于errno 中。

参数说明:

s: socket描述符;  buf: UDP数据报缓存区(包含所接收的数据);   len: 缓冲区长度。   flags: 调用操作方式(一般设置为0)。   from: 指向发送数据的客户端地址信息的结构体(sockaddr_in需类型转换);  fromlen:指针,指向from结构体长度值。

二、实现原理

UDP基于无连接通信,所以不区分服务器端与客户端,因此在程序实现的时候,只需要同一个程序就可以完成数据通信了。

Windows编程技术:Socket通信(下)休闲区蓝鸢梦想 - Www.slyday.coM

初始化Winsock环境后,便调用Socket函数创建数据报套接字;然后对sockaddr_in结构体进行设置,设置绑定的Ip地址和端口等信息并调用bind函数进行绑定;绑定成功后,可以使用recvfrom函数与sendto函数与另一UDP程序进行数据的收发。通信结束后,关闭套接字,释放资源。

三、源码

由于UDP基于无连接通信,不区分客户端与服务端,所以使用同一程序来实现数据通信。首先要做的便是绑定IP和端口。具体操作是初始化Winsock库环境,创建数据报套接字,绑定IP和端口,同时创建多线程接收通信数据。

// 绑定IP地址和端口

BOOL Bind(char *lpszIp, int iPort)

{

// 初始化 Winsock 库

WSADATA wsaData = { 0 };

::WSAStartup(MAKEWORD(2, 2), &wsaData);

// 创建数据报套接字

g_sock = socket(AF_INET, SOCK_DGRAM, 0);

if (INVALID_SOCKET == g_sock)

{

return FALSE;

}

// 设置绑定IP地址和端口信息

sockaddr_in addr = { 0 };

addr.sin_family = AF_INET;

addr.sin_port = ::htons(iPort);

addr.sin_addr.S_un.S_addr = ::inet_addr(lpszIp);

// 绑定IP地址和端口

if (0 != bind(g_sock, (sockaddr *)(&addr), sizeof(addr)))

{

return FALSE;

}

// 创建接收信息多线程

::CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)RecvThreadProc, NULL, NULL, NULL);

return TRUE;

}

UDP不需要建立那边就可以直接发送和接收数据了,所以只要指明IP地址和端口即可。

// 数据发送

void SendMsg(char *lpszText, char *lpszIp, int iPort)

{

// 设置目的主机的IP地址和端口等地址信息

sockaddr_in addr = { 0 };

addr.sin_family = AF_INET;

addr.sin_port = ::htons(iPort);

addr.sin_addr.S_un.S_addr = ::inet_addr(lpszIp);

// 发送数据到目的主机

::sendto(g_sock, lpszText, (1 + ::lstrlen(lpszText)), 0, (sockaddr *)(&addr), sizeof(addr));

printf("[sendto]%s\n", lpszText);

}

同样,由于UDP基于无连接通信,所以可以直接接收数据,而且还可以获取接收数据的源地址信息。

// 数据接收

void RecvMsg()

{

char szBuf[MAX_PATH] = { 0 };

while (TRUE)

{

sockaddr_in addr = { 0 };

// 注意此处, 既是输入参数也是输出参数

int iLen = sizeof(addr);

// 接收数据

::recvfrom(g_sock, szBuf, MAX_PATH, 0, (sockaddr *)(&addr), &iLen);

printf("[recvfrom]%s\n", szBuf);

}

}

// 接收数据多线程

UINT RecvThreadProc(LPVOID lpVoid)

{

// 接收数据

RecvMsg();

return 0;

}

四、测试

因为使用的是同一个程序,意味着要运行两次;第一次首先输入源Ip:127.0.0.1和源端口号13579,再输入目的Ip:127.0.0.1和目的端口3579;提示绑定;

第二次首先输入源Ip:127.0.0.1和源端口号3579,再输入目的Ip:127.0.0.1和目的端口13579;提示绑定;正好和第一次的相反;

在第一个窗口中回车发送数据,则在第二个窗口中收到数据;反之,也是如此。

验证1:本机下,

Windows编程技术:Socket通信(下)休闲区蓝鸢梦想 - Www.slyday.coM

查看两个程序状态,

Windows编程技术:Socket通信(下)休闲区蓝鸢梦想 - Www.slyday.coM

均只能看到本地IP和端口号;远程IP和端口号不可见。

有一点没搞明白,为什么WireShark抓这个UDP程序的包没有抓到?是哪里有问题吗?

验证2:本机+虚拟机环境下,

Windows编程技术:Socket通信(下)休闲区蓝鸢梦想 - Www.slyday.coM

再来抓包下,

Windows编程技术:Socket通信(下)休闲区蓝鸢梦想 - Www.slyday.coM

发现了数据包,这是成功的场景。

相关推荐

  • 友情链接:
  • PHPCMSX
  • 智慧景区
  • 微信扫一扫

    微信扫一扫
    返回顶部

    显示

    忘记密码?

    显示

    显示

    获取验证码

    Close