Fork me on GitHub

windows上简单的UDP通信实现及Linux移植

    UDP是即用户数据协议,是面向无连接;本篇在windows上通过套接字实现简单的UDP通信,编译器使用的是VS,需要在工程->设置->连接->对象/库模块 中加入ws2_32.lib ,或者在include语句后面加上:#pragma comment(lib, “ws2_32.lib”)ws2_32.dll提供了对以下网络相关API的支持:accept、bind、closesocket、connect、htonl、htons、ioctlsocket、inet_addr、inet_ntoa、listen、ntohl、ntohs、recv、recvfrom、select、send、sendto等

    源码如下:

1
2
3
4
5
6
7
8
9
udp.h
#pragma once

//声明为C函数
extern "C"
{
int socket_send(const char *IP);
int socket_recv();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
udp.c
#include<winsock2.h>
#include<string.h>
#include<stdio.h>

int socket_send(const char *IP)
{
DWORD ver;
WSADATA wsaData;
//制定socket版本
ver = MAKEWORD(1, 1);
//socket初始化
WSAStartup(ver, &wsaData);
//AF_INET代表ipv4 SOCK_DGRAM即UDP
SOCKET st = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in addr;
//初始化
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
//host to net short 设置端口发送到指定IP的端口
addr.sin_port = htons(8080);
//指定IP地址
addr.sin_addr.s_addr = inet_addr(IP);
//unsigned long laddr = inet_addr("192.168.6.200");
//unsigned char *p = &laddr;
//printf("%u.%u.%u.%u\n", *p, *(p+1), *(p+2), *(p+3));
char buf[1024] = {0};
size_t rc = 0;
while (1)
{
memset(buf, 0, sizeof(buf));
gets(buf);
//退出条件,字符串中第一个字符为0即退出循环
if (buf[0] == '0')
break;
//发送数据
rc = sendto(st, buf, strlen(buf), 0, (struct sockaddr *)&addr, sizeof(addr));
}
//释放资源,windwows要求,不写的话程序结束自动释放,但建议写上,符合逻辑的完整性
closesocket(st);
WSACleanup();
//返回发送的数据长度
return rc;
}

int socket_recv()
{
DWORD ver;
WSADATA wsaData;
ver = MAKEWORD(1, 1);
WSAStartup(ver, &wsaData);
SOCKET st = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
//设置接收端口
addr.sin_port = htons(8080);
//接收方,不需要指定IP地址,INADDR_ANY任意
addr.sin_addr.s_addr = htonl(INADDR_ANY);
int rc = 0;
//将端口号和程序绑定,失败则退出
if (bind(st, (struct sockaddr *)&addr, sizeof(addr)) > -1)
{
char buf[1024] = { 0 };
//接受传过来的sockaddr_结构
struct sockaddr_in sendaddr;
//初始化
memset(&sendaddr, 0, sizeof(sendaddr));
int len = sizeof(sendaddr);
while (1)
{
memset(buf, 0, sizeof(buf));
//recvfrom是一个阻塞函数,没收到数据会一直阻塞
rc = recvfrom(st, buf, sizeof(buf), 0, (struct sockaddr *)&sendaddr, &len);
printf("%s: %s\n", inet_ntoa(sendaddr.sin_addr), buf);
}
}
//关闭socket
closesocket(st);
//释放资源
WSACleanup();
//返回接收到的数据长度
return rc;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
main.cpp
#include<iostream>
#include"udp.h"

int main(int argc, char *argv[])
{
if (argc > 1)
//传入IP地址
socket_send(argv[1]);
else
//无参数则作为接收方
socket_recv();
system("pause");
return 0;
}

    整个代码段不长,逻辑也简单,一个发送数据函数,一个接受数据的函数;启用发送函数时,IP要指定正确,否则接受方会一直等待接收。

    通过CMD运行实例如下:

    但有时候需要将代码移植到Linux,就需要对上面的代码进行更改,因此需要考虑一种通用的代码,在windows和Linux不需要修改都可以编译运行;下面简单的通过宏定义来实现这个需求。

    源代码如下 :添加了一个MYLINUX的宏

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
udp.h
#ifndef UDP_H_XX
#define UDP_H_XX

#ifdef __cplusplus
extern "C"
{
#endif
int socket_send(const char *IP);
int socket_recv();
#ifdef __cplusplus
}
#endif

#endif
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
udp.h
#include<string.h>
#include<stdio.h>

#ifdef MYLINUX
#include<unistd.h>
#include<arpa/inet.h>
#include<sys/socket.h>
#include<sys/types.h>
#define SOCKET int
#else
#include<winsock2.h>
#endif


int socket_send(const char *IP)
{
#ifndef MYLINUX
DWORD ver;
WSADATA wsaData;
//socket版本
ver = MAKEWORD(1, 1);
//socket初始化
WSAStartup(ver, &wsaData);
#endif
//AF_INET ipv4 SOCK_DGRAM即UDP
SOCKET st = socket(AF_INET, SOCK_DGRAM, 0);
//定义一个socket
struct sockaddr_in addr;
//初始化
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
//host to net short
addr.sin_port = htons(8080);
addr.sin_addr.s_addr = inet_addr(IP);
//unsigned long laddr = inet_addr("192.168.6.200");
//unsigned char *p = &laddr;
//printf("%u.%u.%u.%u\n", *p, *(p+1), *(p+2), *(p+3));
char buf[1024] = {0};
size_t rc = 0;
while (1)
{
memset(buf, 0, sizeof(buf));
gets(buf);
//退出条件
if (buf[0] == '0')
break;
//发送数据
rc = sendto(st, buf, strlen(buf), 0, (struct sockaddr *)&addr, sizeof(addr));
}
//释放资源
#ifdef MYLINUX
close(st);
#else
closesocket(st);
WSACleanup();
#endif
return rc;
}

int socket_recv()
{
#ifndef MYLINUX
DWORD ver;
WSADATA wsaData;
ver = MAKEWORD(1, 1);
WSAStartup(ver, &wsaData);
#endif // !MYLINUX
SOCKET st = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
// printf("%d\n", addr.sin_port);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
int rc = 0;
//将端口号和程序绑定
if (bind(st, (struct sockaddr *)&addr, sizeof(addr)) > -1)
{
char buf[1024] = { 0 };
struct sockaddr_in sendaddr;
memset(&sendaddr, 0, sizeof(sendaddr));
#ifdef MYLINUX
socklen_t len;
#else
int len;
#endif
len = sizeof(sendaddr);
while (1)
{
memset(buf, 0, sizeof(buf));
rc = recvfrom(st, buf, sizeof(buf), 0, (struct sockaddr *)&sendaddr, &len);
printf("%s: %s\n", inet_ntoa(sendaddr.sin_addr), buf);
}
}
#ifdef MYLINUX
close(st);
#else
//释放资源
closesocket(st);
WSACleanup();
#endif
return rc;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
main.cpp
include"udp.h"
int main(int argc, char *argv[])
{
if (argc > 1)
socket_send(argv[1]);
else
socket_recv();
#ifndef MYLINUX
system("pause");
#endif
return 0;
}
1
2
gcc main.cpp udp.c -DMYLINUX -o myudp
#指定宏MYLINUX,暂且忽略gets的危险警告

------ 本文结束 ------