1、简单讲讲什么是UDP?
UDP(User Datagram Protocol)是一种无连接性的网络通讯协议。这种协议相比于TCP协议来说,不需要在通讯前建立连接,但也不会维护连接状态。它的发送速度更快,但不能保证数据的可靠性。因此,UDP更适应于小型的数据传输。
2、C/S(客户/服务)模型。
3、服务端:引用套接字头文件<WinSock2.h>、库文件(lib,"Ws2_32.lib")
3.1 打开网络库,校验版本
/*启动网络库,启动了这个库,库里的函数才能被使用*/
int WSAStartup(
WORD wVersionRequired, /* 库的版本 */
LPWSADATA lpWSAData /* 这是一个指针,用来存储版本信息 */
);
网络库版本有 MAKEWORD(1, 2); //主版本号为1,副版本号为2,返回 0x0201
MAKEWORD(2, 2); //主版本号为2,副版本号为2,返回 0x0202 。
在使用前要定义一个数据类型为WSADATA的结构体变量来存储版本信息地址。
4、创建套接字Scoket
SOCKET s = socket(int af,int type,int protocol);//创建套接字;
/*变量 af:套接字所用网络的格式,常用有AF_INET,PF_INET(IPv4 Internet协议),PF_INET6(IPv6 Internet协议)*/
//变量 type:套接字所用的网络通讯协议,SOCK_STREAM(TCP);SOCK_DGRAM(UDP);
//变量protocol:套接口所用的协议,如调用者不想指定,也可以将 protocol 的值设为 0,系统会自动根据嵌套字类型推演出应该使用什么协议
5、定义IP地址,并且为套接字绑定IP地址
5.1、结构体sockaddr_in,需要头文件<netinet/in.h>
struct sockaddr_in 变量 ;//定义sockaddr_in的结构体变量addr
变量.sin_family //该参数一般表示为协议 类型,分为IPV4 IPV6 //一般填AF_INET,表示为IPV4协议
变量.sin_addr.s_addr //一般表示为IP地址, 注意 :一般在使用时需要进行大小端转换。
变量.sin_port //一般表示为端口号设置 注意 : 也需要进行大小端的转换。、
5.2、htons函数
作用:a. 将端口从"主机端序" 转为 “网络端序”
b. 如果给定的端口不是short,则转为short。
5.3、bind函数
bind函数用于将套接字与指定端口相连。也就是把IP和端口连接到一个未命名的套接字上。使用此函数时需要头文件 <sys/types.h> 和 <sys/socket.h>
int bind(int sockfd, SOCKADDR *my_addr, addrlen)
//变量sockfd: 要绑定的套接字
//变量my_addr:要绑定的IP,注意要取地址
//变量addrlen:my_addr的大小,sizeof(my_addr)
6、接收,发送客户端消息
hile (1)
{ //接收消息
//char szRecvBuffer[1024] = { 0 }; //接收缓冲区
int nReturnValue = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr*)&cliaddr, &len);
//接收到客户端消息
printf("Client Data : %s \n", buf);
char szSendBuffer[1024] = { 0 }; //发送缓冲区
printf("Input Something : ");
scanf_s("%s", szSendBuffer, 1024);
if (SOCKET_ERROR == sendto(s, szSendBuffer, 1024, 0, (struct sockaddr*)&cliaddr, sizeof(cliaddr)))
{
printf("send fail!");
exit(1);
}
7、关闭接口closesocket( );
8、对于客户端而言与服务端相似,只不过是recvfrom函数和sendto函数的使用顺序不同。
服务器端总体代码如下:
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <iostream>
#pragma comment(lib, "Ws2_32.lib")//引用头文件和库文件
using namespace std;
int main(void)
{
WSADATA wsaData;
//初始化WinSock2.2
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
printf("初始化失败");
return 0;
}
SOCKET s = socket(AF_INET, SOCK_DGRAM, 0);//创建套接字,
//可以将 protocol 的值设为 0,系统会自动根据嵌套字类型推演出应该使用什么协议
if (s == INVALID_SOCKET)
{
printf("socket error!");
}
//指定绑定地址
struct sockaddr_in addr;
//定义服务器地址
addr.sin_family = PF_INET; //使用IPv4地址
addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //设置本地IP
addr.sin_port = htons(51314); //端口
//绑定到Socket
if (SOCKET_ERROR == ::bind(s, (SOCKADDR*)&addr, sizeof(SOCKADDR)))
{
printf("bind error!");
exit(1);
}
char buf[1024];
char ipbuf[64];
struct sockaddr_in cliaddr;
int len = sizeof(cliaddr);
while (1)
{ //接收消息
//char szRecvBuffer[1024] = { 0 }; //接收缓冲区
int nReturnValue = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr*)&cliaddr, &len);
//接收到客户端消息
printf("Client Data : %s \n", buf);
char szSendBuffer[1024] = { 0 }; //发送缓冲区
printf("请输入消息 : ");
scanf_s("%s", szSendBuffer, 1024);
if (SOCKET_ERROR == sendto(s, szSendBuffer, 1024, 0, (struct sockaddr*)&cliaddr, sizeof(cliaddr)))
{
printf("send fail!");
exit(1);
}
}
closesocket(s);
exit(1);
}
客户端总体代码:
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <iostream>
#pragma comment(lib, "Ws2_32.lib")
using namespace std;
int main(void)
{
WSADATA wsaData;
//初始化WinSock2.2
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
{
printf("初始化失败");
return 0;
}
SOCKET g = socket(AF_INET, SOCK_DGRAM, 0);
//可以将 protocol 的值设为 0,系统会自动根据嵌套字类型推演出应该使用什么协议
if (g == INVALID_SOCKET)
{
printf("socket error!");
}
struct sockaddr_in addr;
addr.sin_family = PF_INET; //使用IPv4地址
addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //使用本机IP
addr.sin_port = htons(51314
); //端口
while (1)
{
//发送到服务器的消息
char szSendData[1024] = { 0 };
printf("请输入消息 : ");
scanf_s("%s", szSendData, 1024);
if (SOCKET_ERROR == sendto(g, szSendData, strlen(szSendData) + 1, 0, (struct sockaddr*)&addr, sizeof(addr)))
{
printf("send fail!");
exit(1);
}
//接收服务器返回的信息
char szBuffer[1024] = { 0 };
int nReturnValue = recv(g, szBuffer, 1024, 0);
if (0 == nReturnValue)
{
exit(1);
}
printf("server data : %s\n", szBuffer);
}
closesocket(g);
}
运行结果如下:
值得注意的是:服务端和客户端套接字要绑定相同的IP地址和端口。通俗来说,两个接口(scoket)要用一根两头粗细(端口)相同的线(ip)连起来。