1、Window下UDP
首先,涉及到的API函数及相关的数据类型:(1)MAKEWORD()宏 用来将创建含有一个请求版本号的WORD值
(2)windows下的套接字库
WSAStartup() 加载套接字库
WSACleanup() 清除套接字库上述两个函数通常成对使用
函数原型:
SOCKET WSAAPI socket(_In_ int af,_In_ int type,_In_ int protocol);
_In_ int af: 具体的协议地址类型。
_In_ int type: 具体的socket类型。比如UDP,TCP等。
_In_ int protocol: 针对协议族地址的可选项。
(4)htons()
函数原型:
u_short WSAAPI htons(
_In_ u_short hostshort
);
_In_ u_short hostshort表示一个16bit的数值
host to net short 将一个usigned short类型(准确的说应该是2Bytes)的值从主机字节序转换成TCP/IP网络字节序。
和他相似的还有个函数
(5)htonl()
u_long WSAAPI htonl(
_In_ u_long hostlong
);
host to net long 将一个usigned long类型(4Bytes)的值从主机字节序转换成TCP/IP网络字节序。
(6)inet_addr();
unsigned long inet_addr(
_In_ const char *cp
);
将一个点分十进制表示的IP地址(字符串表示char*)转换成一个unsiged long类型的数值。
(7)inet_ntoa();
char* FAR inet_ntoa(
_In_ struct in_addr in
);
这个函数和inet_addr()作用正好相反,它的作用是将一个unsigned long类型的数值转换成一个点分十进制表示的IP地址(字符串char *)
(8)struct sockaddr_in
typedef struct sockaddr_in {
ADDRESS_FAMILY sin_family;
USHORT sin_port;
IN_ADDR sin_addr;
CHAR sin_zero[8];
} SOCKADDR_IN, *PSOCKADDR_IN;
typedef struct in_addr {
union {
struct {
UCHAR s_b1;
UCHAR s_b2;
UCHAR s_b3;
UCHAR s_b4;
} S_un_b;
struct {
USHORT s_w1;
USHORT s_w2;
} S_un_w;
ULONG S_addr;
} S_un;
} IN_ADDR, *PIN_ADDR, *LPIN_ADDR;
完整代码:
#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib,"ws2_32.lib")
/*udp发送*/
void udp_send()
{
WORD version = MAKEWORD(2, 2);
WSADATA wsaData;
WSAStartup(version, &wsaData); //打开windows socket资源
SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
addr.sin_addr.S_un.S_addr = inet_addr("192.168.0.130");
sendto(sock, "Hello world!", strlen("Hello world!"), 0,
(struct sockaddr*)&addr, sizeof(addr));
closesocket(sock);
WSACleanup();
}
/*udp接收*/
void udp_recv()
{
WORD version = MAKEWORD(2, 2);
WSADATA wsaData;
if (0 != WSAStartup(version, &wsaData)) //打开windows socket资源
{
return;
}
SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
addr.sin_addr.S_un.S_addr = INADDR_ANY;
int rt = bind(sock, (struct sockaddr*)&addr, sizeof(addr));
struct sockaddr_in client;
memset(&client, 0, sizeof(client));
char buf[512] = {0};
int len = sizeof(client);
rt = recvfrom(sock, buf, 512, 0, (struct sockaddr*)&client, &len);
printf("%s\n", buf);
closesocket(sock);
WSACleanup();
}
用图来表示上述过程:
2、linux下UDP
将上述代码移植到linux下:
//#include <winsock2.h>
#include <stdio.h>
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
//#pragma comment(lib,"ws2_32.lib")
#define SOCKET int
/*udp发送*/
void udp_send()
{
/*
WORD version = MAKEWORD(2, 2);
WSADATA wsaData;
WSAStartup(version, &wsaData); //打开windows socket资源
*/
SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
struct sockaddr_in addr;
memset(&addr,0,sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
addr.sin_addr.s_addr = inet_addr("127.0.0.1");
sendto(sock, "Hello world!", strlen("Hello world!"), 0,
(struct sockaddr*)&addr, sizeof(addr));
close(sock);
// closesocket(sock);
//WSACleanup();
}
/*udp接收*/
void udp_recv()
{
/*
WORD version = MAKEWORD(2, 2);
WSADATA wsaData;
if (0 != WSAStartup(version, &wsaData)) //打开windows socket资源
{
return;
}
*/ SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
addr.sin_addr.s_addr = INADDR_ANY;
int rt = bind(sock, (struct sockaddr*)&addr, sizeof(addr));
struct sockaddr_in client;
memset(&client, 0, sizeof(client));
char buf[512] = {0};
socklen_t len = sizeof(client);
rt = recvfrom(sock, buf, 512, 0, (struct sockaddr*)&client, &len);
printf("%s\n", buf);
close(sock);
// closesocket(sock);
// WSACleanup();
}
分析:Linux下并不需要再每次使用socket的时候加载WSAstartup()来加载socket资源。此外,由于API函数所在的头文件不同,所有移植的时候会包含不同的头文件。(Tips:Linux下查看某个头文件所在的文件可使用man 命令来查找)。从上述的代码结构可以看出Windows和Linux下UDP通信的流程其实是一样的。