区别:
recv + accpet == recvfrom
Send + connect == sendto
1. windows的UDP编程模型
客户端:
#include <WinSock2.h>
#pragma comment(lib,"Ws2_32.lib ")
#include <windows.h>
#include <stdio.h>
int main(){
//0 请求协议版本
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
if (LOBYTE(wsaData.wVersion) != 2 ||
HIBYTE(wsaData.wVersion) != 2){
printf("请求协议版本失败:%d\n", GetLastError());
return -1;
}
printf("请求协议版本成功!\n");
//1 创建socket
SOCKET serverSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (SOCKET_ERROR == serverSocket){
printf("创建socket失败:%d\n", GetLastError());
//8 清理协议版本信息
WSACleanup();
return -1;
}
printf("创建socket成功!\n");
//2 服务器协议地址簇
SOCKADDR_IN sAddr = { 0 };
sAddr.sin_family = AF_INET; // 和socket第一个参数一致
sAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); // 把数点格式字符串转换成网络字节序
sAddr.sin_port = htons(10086); // 65536 5000以上 大小端转换
//3 绑定
int r = bind(serverSocket, (sockaddr*)&sAddr, sizeof sAddr);
if (-1 == r){
printf("绑定失败:%d\n", GetLastError());
//7 关闭socket
closesocket(serverSocket);
//8 清理协议版本信息
WSACleanup();
return -1;
}
printf("绑定成功!\n");
//4 接收数据
char buff[1024];
//客户端协议地址簇
SOCKADDR_IN cAddr = { 0 };
int len = sizeof cAddr;
while (1){
r = recvfrom(serverSocket, buff, 1023, NULL,(sockaddr*)&cAddr,&len);
if (r > 0){
buff[r] = 0;
printf(">>%s\n", buff);
sendto(serverSocket, "砍死你个耙耳朵", strlen("砍死你个耙耳朵"), NULL,
(sockaddr*)&cAddr, sizeof cAddr);
}
}
//5 关闭socket
closesocket(serverSocket);
//6 清理协议版本信息
WSACleanup();
return 0;
}
服务器端:
#include <WinSock2.h>
#pragma comment(lib,"Ws2_32.lib ")
#include <windows.h>
#include <stdio.h>
int main(){
//0 请求协议版本
WSADATA wsaData;
WSAStartup(MAKEWORD(2, 2), &wsaData);
if (LOBYTE(wsaData.wVersion) != 2 ||
HIBYTE(wsaData.wVersion) != 2){
printf("请求协议版本失败:%d\n", GetLastError());
return -1;
}
printf("请求协议版本成功!\n");
//1 创建socket
SOCKET serverSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (SOCKET_ERROR == serverSocket){
printf("创建socket失败:%d\n", GetLastError());
//8 清理协议版本信息
WSACleanup();
return -1;
}
printf("创建socket成功!\n");
//2 服务器协议地址簇
SOCKADDR_IN sAddr = { 0 };
sAddr.sin_family = AF_INET; // 和socket第一个参数一致
sAddr.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); // 把数点格式字符串转换成网络字节序
sAddr.sin_port = htons(10086); // 65536 5000以上 大小端转换
//3 发送数据到服务器 sendto
char buff[1024];
int r;
char temp[1024];
while (1){
printf("请输入:");
scanf("%s", buff);
sendto(serverSocket, buff, strlen(buff), NULL, //炮弹 一瓶水
(sockaddr*)&sAddr, sizeof sAddr); //向谁发炮 往哪扔
r = recv(serverSocket, temp, 1023, NULL);
if (r > 0){
temp[r] = 0;
printf("来自服务器的问候:%s\n", temp);
}
}
//4 关闭socket
closesocket(serverSocket);
//5 清理协议版本信息
WSACleanup();
return 0;
}
2. Qt的UDP编程模型
客户端:
#include "clientwidget.h"
#include "ui_clientwidget.h"
clientwidget::clientwidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::clientwidget)
{
ui->setupUi(this);
setWindowTitle("udp客户端 ");
//1. 创建socket
pUdpSocket = new QUdpSocket(this);
//2. 收数据
connect(pUdpSocket,&QUdpSocket::readyRead,
[=](){
//2.1. 接收
char buff[1024] = {0};
QHostAddress ip;
quint16 port;
//recvForm
pUdpSocket->readDatagram(buff,1024,&ip,&port);
//2.2. 显示
QString str = QString("ip:%1,port:%2,msg:%3").
arg(ip.toString()).arg(port).arg(buff);
ui->textEdit_Recv->append(str);
});
}
clientwidget::~clientwidget()
{
delete ui;
}
void clientwidget::on_btn_Send_clicked()
{
//3.1. 获取port和ip
QString ip = ui->lineEdit_IP->text();
QString port = ui->lineEdit_Port->text();
QString msg = ui->textEdit_Send->toPlainText();
pUdpSocket->writeDatagram(msg.toUtf8(),QHostAddress(ip),port.toUInt());
}
服务器端:
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
setWindowTitle("udp服务器 ");
//1. 创建socket
pUdpSocket = new QUdpSocket(this);
//2. 绑定
//pUdpSocket->bind(8888);
//4.0. 设置组播
//4.1. 绑定,任意地址
pUdpSocket->bind(QHostAddress::AnyIPv4,9999);
//4.2. 进组
pUdpSocket->joinMulticastGroup(QHostAddress("224.0.0.3"));
pUdpSocket->joinMulticastGroup(QHostAddress("224.0.0.4"));
//3. 收数据
connect(pUdpSocket,&QUdpSocket::readyRead,
[=](){
//3.1. 接收
char buff[1024] = {0};
QHostAddress ip;
quint16 port;
//recvForm
pUdpSocket->readDatagram(buff,1024,&ip,&port);
//3.2. 显示
QString str = QString("ip:%1,port:%2,msg:%3").arg(ip.toString()).
arg(port).arg(buff);
ui->textEdit_Recv->append(str);
});
}
Widget::~Widget()
{
delete ui;
}
//4. 发送数据
void Widget::on_btn_Send_clicked()
{
//4.1. 获取port和ip
QString ip = ui->lineEdit_IP->text();
QString port = ui->lineEdit_Port->text();
QString msg = ui->textEdit_Send->toPlainText();
pUdpSocket->writeDatagram(msg.toUtf8(),QHostAddress(ip),port.toUInt());
}
D类地址:
组播组可以是永久的也可以是临时的。
组播组地址中,有一部分由官方分配的,称为永久组播组。
永久组播组保持不变的是它的ip地址,组中的成员构成可以发生变化。
永久组播组中成员的数量都可以是任意的,甚至可以为零。
那些没有保留下来供永久组播组使用的ip组播地址,可以被临时组播组利用。
-
224.0.0.0~224.0.0.255
为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配,其它地址供路由协议使用; -
224.0.1.0~224.0.1.255
是公用组播地址,可以用于Internet; -
224.0.2.0~238.255.255.255
为用户可用的组播地址(临时组地址),全网范围内有效; -
239.0.0.0~239.255.255.255
为本地管理组播地址,仅在特定的本地范围内有效。
3. 点播,广播,组播
4. 程序打包
总结
- C/C++ Qt 跨平台
- 几乎所有的操作系统都是用C/C++来写的
- 源文件->编译->链接->可执行程序文件
- 到处编译到处运行
- windows上,用windows的编译器来编译,然后运行
- Linux上,用Linux的编译器来编译,然后运行
- Java的跨平台缘由
Java虚拟机:C++写的: