基于UDP 的通信
UDP_Server
加载库
先写框架
//加载库
//创建套接字
while(true){
//发送消息
//接收消息
}
//关闭套接字
//卸载库
善于使用帮助文档
https://learn.microsoft.com/en-us/windows/win32/api/winsock2/
可以在代码中选中,按F1,直接跳转

语法

返回值:int
[in]:输入参数-需要自己赋值
Version:版本

[out]:输出参数-函数赋值-需要为指针类型
LP:指针
LPWSADATA:WSADATA类型的指针
[in/out]:输入输出参数
返回值
如果成功, WSAStartup 函数返回零。 否则,它将返回下面列出的错误代码之一。

要求

#include<iostream>
#include<winsock2.h>
//同一个解决方案下"",不同解决方案下<>
//导入依赖库
#pragma comment(lib,"Ws2_32.lib")
using namespace std;
int main(){
//加载库
WORD version=MAKEWORD(2,2);
WSADATA data;
int err=WSAStartup(version,&data);
//判断返回值
if(0!=err){
cout<<"WSASartup error!"<<endl;
WSACleanup();
return 1;
}
//判断库的版本号对不对
if(2!=HIBYTE(data.wVersion)||2!=LOBYTE(data.wVersion)){
cout<<"WSASartup version error"<<endl;
WSACleanup();
return 1;
}else{
cout<<"wVersion success"<<endl;
}
}
创建套接字
语法

af:地址族 ip地址类型-ipv4
type:socket类型-用户数据报
protocol:传输使用的协议-UDP
工具-错误查找里面可以根据返回的错误码查找相关错误
//创建套接字
SOCKET sock=socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
if(INVALID_SOCKET==sock){
cout<<"socket error"<<WSAGetLastError()<<endl;
WSACleanup();
return 1;
}else{
cout<<"socket success"<<endl;
}
绑定网卡和端口号
告诉操作系统,当前进程使用的是什么类型的ip地址,哪个端口号,哪个ip
语法
[in] s
标识未绑定套接字的描述符。
绑定socket:该进程使用该socket的ip地址端口号用于接收,发送数据。
一个进程可以绑定多个socket。
[in] name
指向要分配给绑定套接字 的本地地址 的 sockaddr 结构的指针。
下面的 sockaddr 结构和sockaddr_in结构用于 IPv4。 其他协议使用类似的结构。
struct sockaddr {
ushort sa_family;
char sa_data[14];//数据顺序需要自己排列,比较麻烦
};
//这两个结构体的大小相同,内容相同,可以进行强转,不会丢失数据
struct sockaddr_in {
short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
使用sockaddr_in更便于赋值。
struct in_addr sin_addr;//结构体套联合体
struct in_addr {
union {
struct {
u_char s_b1;
u_char s_b2;
u_char s_b3;
u_char s_b4;
} S_un_b;
struct {
u_short s_w1;
u_short s_w2;
} S_un_w;
u_long S_addr;
} S_un;
};
[in] namelen
name 参数指向的值的长度(以字节为单位)。通过指针,不能指定指针指向的空间大小。通过明确指针空间大小,可以避免指针越界。
返回值
如果未发生错误, 绑定 将返回零。 否则,它将返回SOCKET_ERROR,并且可以通过调用 WSAGetLastError 来检索特定的错误代码。
//给结构体赋值
sockaddr_in addr;
addr.sin_family=AF_INET;
//数字大的端口号无人使用 涉及大小端问题 规定网络字节序 htons 转换成大端
addr.sin_port=htons(6666);
addr.sin_addr.S_un.S_addr = INADDR_ANY;
err=bind(sock,(sockaddr*)&addr,sizeof(addr));//指针和指针才可以强转
if(SOCKET_ERROR==err){
cout<<"bind error"<<WSAGetLastError()<<endl;
//关闭套接字
closesocket();
//卸载库
WSACleanup();
return 1;
}else{
cout<<"bind success"<<endl;
}
发送接收数据
接收数据
int WSAAPI recvfrom(
[in] SOCKET s,//本进程使用的套接字
[out] char *buf,//数据接收从哪个地址开始
[in] int len,//可接收的数据大小
[in] int flags,//初始化标志位-0-默认方式接收
[out] sockaddr *from,//接收到的数据的来源地址
[in, out, optional] int *fromlen//地址的长度存储的位置
);
参数
[in] s
标识绑定套接字的描述符。
[out] buf
传入数据的缓冲区。
[in] len
buf 参数指向的缓冲区的长度(以字节为单位)。
[in] flags
一组选项,用于修改函数调用的行为,超出为关联套接字指定的选项。
[out] from
指向 sockaddr 结构中的缓冲区的可选指针,该缓冲区将在返回时保存源地址。
[in, out, optional] fromlen
指向 from 参数指向的缓冲区大小(以字节为单位)的可选指针。
返回值
如果未发生错误, recvfrom 将返回收到的字节数。 如果连接已正常关闭,则返回值为零。 否则,将返回值 SOCKET_ERROR,并且可以通过调用 WSAGetLastError 来检索特定的错误代码。
int recvNum=0;
int sendNum=0;
char recvBuf[9999]="";
char sendBuf[9999]="";
sockaddr_in addrClient;
int addrClientSize=sizeof(addrClient);
while(true){
recvNum=recvfrom(sock,recvBuf,sizeof(recvBuf),0,(sockaddr*)&addrClient,&addrClientSize);
if(recvNum>0){
cout<<"ip"<<inet_ntoa(addrcClient.sin_addr)<<"say:"<<recvBuf<<endl;
}else{
cout<<"recv error"<<WSAGetLastError()<<endl;
break;
}
//发送数据
gets(sendBuf);
sendNum=sendto(sock,sendBuf,sizeof(sendBuf),0,(sockaddr*)&addrClient,addrClientSize);
if(SOCKET_ERROR==sendNum){
cout<<"send error"<<WSAGetLastError()<<endl;
break;
}else{
cout<<"send success"<<endl;
}
}
closesocket(sock);
WSACleanup();
return 0;
发送数据
sendto 语法
int WSAAPI sendto(
[in] SOCKET s,//发送使用的socket
[in] const char *buf,//从哪个地址开始写入数据
[in] int len,//可使用的空间大小
[in] int flags,//使用的接收方式
[in] const sockaddr *to,//发送给哪个ip地址所在的位置
[in] int tolen//该IP地址所占的长度
);
参数
[in] s
标识可能连接的 () 套接字的描述符。使用的套接字。
[in] buf
指向包含要传输的数据的缓冲区的指针。
[in] len
buf 参数指向的数据的长度(以字节为单位)。
[in] flags
一组指定调用方式的标志。0-最简单的接收方式。
[in] to
指向包含目标套接字地址 的 sockaddr 结构的可选指针。
[in] tolen
由 to 参数指向的地址的大小(以字节为单位)。
返回值
如果未发生错误, sendto 将返回发送的总字节数,这可能小于 len 指示的数量。 否则,将返回值 SOCKET_ERROR,并且可以通过调用 WSAGetLastError 检索特定的错误代码。
地址形式转换
IP地址分类:
- 字符串类型 十进制四等分类型“192.168.3.222”,方便用户查看。
- ulong类型可存储更长的数据。
两种地址转换
字符串->ulong
- inet_addr();
ulong->字符串
- inet_ntoa();//实际上是in_addr转字符串
完整代码
#include<iostream>
//头文件不在当前解决方案<> 在当前解决方案" "
#include<winsock2.h >
//导入依赖库
#pragma comment(lib,"Ws2_32.lib")//立即导入 lib-library
using namespace std;
int main()
{
//1.加载库(使用库函数)/静态库 添加头文件,导入依赖库(lib+库名)
// 加载库相当于把exe拷入到
//动态库 添加头文件,导入依赖库 动态库dll和exe放在一起然后才能用
//W-windows S-socket A-api Windows里面关于Socket的一个接口
WORD version = MAKEWORD(2, 2);//输入参数需要赋值
WSADATA data;//输出参数不需要赋值
int err = WSAStartup(version,&data);//未定义标识符 没有导入库
//实现在库里面实现 除了需要引用头文件,还需要导入依赖库
//函数参数填完了需要判断返回值
//判断函数调用是否成功 成功就继续,失败就停止 return
if(0!=err){
cout << "WSAStartup error" << endl;
return 1;
}
//判断库的版本号对不对
//函数调用成功了但是库的版本号可能不对,因此需要判断版本号是否正确
if (2 != HIBYTE(data.wVersion) || 2 != LOBYTE(data.wVersion)) {
cout << "WSAStartup version error " << endl;
//卸载库
WSACleanup();
return 1;
}
else {
cout << "WSAStartup success" << endl;
}
//2.创建套接字(通信的基石,得先有套接字后面才能用)
SOCKET sock=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (INVALID_SOCKET == sock) {
cout << "socket error" << " " << WSAGetLastError() << endl;
//卸载库
WSACleanup();
return 1;
}
else {
cout << "socket succuss" << endl;
}
//3.绑定网卡和端口号,(告诉系统可以接收发给哪个网卡和端口号的数据)
//告诉操作系统,当前进程使用的是什么类型的ip地址,哪个端口号,哪个ip
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(67890);//数字大的端口号无人使用//端口 涉及大小端问题 规定网络字节序 转换成大端
addr.sin_addr.S_un.S_addr = INADDR_ANY;//地址 绑定任意(所有)网卡
//ip地址分两种类型:字符串类型 十进制四等分类型"192.168.3.222"(方便用户看) ulong类型(更长)
//两种类型相互转换 字符串类型转 ulong: inet_addr()
//ulong转字符串: inet_ntoa() 实际上是in_addr转字符串
err=bind(sock, (sockaddr*)&addr, sizeof(addr));//指针和指针才能强转
if (SOCKET_ERROR == err) {
cout << "bind error" << WSAGetLastError() << endl;//只有网络操作的时候WSAGetLastError可用
closesocket(sock);//一个进程可以有多个套接字,需要指定关闭哪个套接字
WSACleanup();
return 1;
}
else {
cout << "bind success" << endl;
}
//传输数据:发送和接收数据,是一个循环
//先接收数据的是服务端
int nRecvNum = 0;//局部变量定义在循环的外面 提高效率 避免反复的申请空间
//减少申请空间的时间
int nSendNum = 0;
char recvBuf[9999] = "";
char sendBuf[9999] = "";
sockaddr_in addrClient;
int addrClientSize = sizeof(addrClient);
while (true) {
//4.接收数据(阻塞函数 有一步再进行下一步)
//接收使用的套,接收到的数据,数据的大小,接收的方式,接收方的地址,地址的长度
nRecvNum=recvfrom(sock,recvBuf,sizeof(recvBuf),0,(sockaddr*)&addrClient,&addrClientSize);
if (nRecvNum > 0) {
//打印接收到的数据 接收的ip地址ulong转char
cout << "ip:"<< inet_ntoa(addrClient.sin_addr)<<" say:" << recvBuf << endl;
}
else {
cout << "recv error" << WSAGetLastError() << endl;
break;
}
//5.发送数据
gets_s(sendBuf);
nSendNum=sendto(sock,sendBuf,sizeof(sendBuf),0,(sockaddr*)&addrClient,addrClientSize);
if (SOCKET_ERROR==nSendNum) {
cout << "send error" << WSAGetLastError() << endl;
break;
}
else{
cout << "send success" << endl;
}
}
//6.关闭套接字
closesocket(sock);
//8.卸载库
WSACleanup();
return 0;
//inet_nota 函数太老系统不适配,关闭SDL 安全检查
}
完整代码2
#include<iostream>
#include<winsock2.h>
using namespace std;
#pragma comment(lib,"Ws2_32.lib")
int main() {
//1 加载库
WORD version = MAKEWORD(2, 2);
WSADATA data;
int err=WSAStartup(version,&data);
if (0 != err) {
cout << "WSAStartup err" << endl;
return 1;
}
else {
cout << "WSASTartup success" << endl;
}
if (2 != HIBYTE(data.wVersion) || 2 != LOBYTE(data.wVersion)) {
cout << "Version err" << endl;
WSACleanup();
return 1;
}
else {
cout << "Version right" << endl;
}
//2创建套接字
SOCKET sock = socket(AF_INET,SOCK_DGRAM,IPPROTO_UDP);
if (INVALID_SOCKET == sock) {
cout << "socket err " << WSAGetLastError()<<endl;
WSACleanup();
return 1;
}
else {
cout << "socket success" << endl;
}
sockaddr_in addr;
addr.sin_addr.S_un.S_addr = INADDR_ANY;
addr.sin_family = AF_INET;
addr.sin_port = htons(56789);
err = bind(sock,(sockaddr*)&addr,sizeof(addr));
if (SOCKET_ERROR == err) {
cout << "bind error" << GetLastError() << endl;
closesocket(sock);
WSACleanup();
return 1;
}else{
cout << "bind success" << endl;
}
char sendBuf[9999] = "";
char recvBuf[9999] = "";
int sendBuflen = sizeof(sendBuf);
int recvBufsize = sizeof(recvBuf);
sockaddr_in addrTo;
int addrToLen = sizeof(addrTo);
while (true) {
//3循环接受数据
int recvNum = recvfrom(sock,recvBuf,recvBufsize,0,(sockaddr*)&addrTo,&addrToLen);
if (recvNum<0) {
cout << "recvv erro" << WSAGetLastError() << endl;
break;
}
else {
cout << "ip:" << inet_ntoa(addrTo.sin_addr) << " say:" <<recvBuf<< endl;
}
//4发送数据
gets_s(sendBuf);
int sendNum = sendto(sock, sendBuf, sendBuflen, 0, (sockaddr*)&addrTo, sizeof(addrTo));
if (SOCKET_ERROR == sendNum) {
cout << "send error" << WSAGetLastError() << endl;
break;
}
else {
cout << "send success" << endl;
}
}
//5关闭套接字
WSACleanup();
//6卸载库
closesocket(sock);
}
655

被折叠的 条评论
为什么被折叠?



