对比TCP与UDP的通信区别
UDP Server没有listen()和accept()
TCP Server
#include <iostream>
#include <WinSock2.h>
// 包含网络库
#pragma comment(lib,"ws2_32.lib")
using namespace std;
int main()
{
// 1. 初始化套接字 初始化套接字库
cout << "UDP Server" << endl;
WORD wVersion;
WSADATA wsaData;
int err;
wVersion = MAKEWORD(1, 1);
err = WSAStartup(wVersion, &wsaData);
if (err != 0)
{
return err;
}
if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
{
WSACleanup();
return -1;
}
// 2. 创建套接字
SOCKET sockSrv = socket(AF_INET,SOCK_DGRAM,0);
if (INVALID_SOCKET == sockSrv) //加入容错机制
{
printf("socket errorNo = %d\n",GetLastError());
return -1;
}
// 3.分配地址和端口
SOCKADDR_IN addrSrv;
// h:host to n:net l:long 主机字节序转换为网络字节序
addrSrv.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(6001);
if(SOCKET_ERROR == bind(sockSrv,(SOCKADDR*)&addrSrv,sizeof(SOCKADDR_IN)))
{
printf("bind errorNo = %d\n",GetLastError());
return -1;
}
// 4. 等待接收数据
SOCKADDR_IN addrCli; // 目的套接字地址族
int len = sizeof(SOCKADDR_IN);
char recvBuf[100] = {0};
char sendBuf[100] = {0};
while(true)
{
recvfrom(sockSrv, recvBuf, 100, 0, (SOCKADDR*)&addrCli, &len);
cout << recvBuf << endl;
sprintf_s(sendBuf, 100, "Ack:%s", recvBuf);
sendto(sockSrv, sendBuf,strlen(sendBuf) + 1,0, (SOCKADDR*)&addrCli, len);
}
// 5. 关闭套接字
closesocket(sockSrv);
WSACleanup();
system("pause");
return 0;
}
代码逻辑:
- 初始化套接字 初始化套接字库
- 创建套接字
- 分配地址和端口
- bind() 分配地址和端口
- 等待接收数据 recvfrom()接收数据 sendto()发送数据 while循环
TCP Client
#include <iostream>
#include <WinSock2.h>
// 包含网络库
#pragma comment(lib,"ws2_32.lib")
using namespace std;
int main()
{
// 1. 初始化套接字 初始化套接字库
cout << "UDP Client" << endl;
WORD wVersion;
WSADATA wsaData;
int err;
wVersion = MAKEWORD(1, 1);
err = WSAStartup(wVersion, &wsaData);
if (err != 0)
{
return err;
}
if (LOBYTE(wsaData.wVersion) != 1 || HIBYTE(wsaData.wVersion) != 1)
{
WSACleanup();
return -1;
}
// 2. 创建UDP套接字
SOCKET sockCli = socket(AF_INET, SOCK_DGRAM, 0);
if (INVALID_SOCKET == sockCli) //加入容错机制
{
printf("socket errorNo = %d\n", GetLastError());
return -1;
}
// 3. 填充地址和端口
SOCKADDR_IN addrSrv;
addrSrv.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
addrSrv.sin_family = AF_INET;
addrSrv.sin_port = htons(6001);
int len = sizeof(SOCKADDR_IN);
char sendBuf[100] = "hello";
char recvBuf[100] = { 0 };
// 4.发送UDP数据
sendto(sockCli, sendBuf, strlen(sendBuf) + 1,0,(SOCKADDR*)&addrSrv, len);
// 5.接收UDP数据
recvfrom(sockCli,recvBuf,100,0,(SOCKADDR*)&addrSrv,&len);
cout << recvBuf << endl;
closesocket(sockCli);
system("pause");
return 0;
}
代码逻辑:
- 初始化套接字 初始化套接字库
- 创建UDP套接字
- 填充地址和端口
- 直接发送/接收数据
运行结果:
关于bind()函数
官方文档:绑定函数将本地地址与套接字相关联。
int bind(
[in] SOCKET s,
const sockaddr *addr,
[in] int namelen
);
bind()是一个用于将套接字(socket)与特定的网络地址(IP 地址和端口号)绑定的函数。它在套接字创建后,但在进行网络通信之前被调用。
三个参数的含义:
sockfd
:需要绑定的套接字描述符(socket descriptor)。addr
:指向要绑定的网络地址的结构体指针,通常是struct sockaddr
或其派生结构体的指针。addrlen
:网络地址结构体的长度。
关于sendto()函数
官方文档:
sendto 函数将数据发送到特定目标。
int WSAAPI sendto(
[in] SOCKET s,
[in] const char *buf,
[in] int len,
[in] int flags,
[in] const sockaddr *to,
[in] int tolen
);
sendto()
是用于发送数据到指定目标地址的函数,通常用于无连接的数据报套接字(SOCK_DGRAM
)。它可以向指定的目标地址发送数据,并指定数据的长度和其他参数。
参数的含义:
s
:要发送数据的套接字描述符(socket descriptor)。buf
:指向包含要发送的数据的缓冲区的指针。len
:要发送的数据的长度。flags
:发送标志,用于控制发送操作的行为,例如是否启用特定的选项。to
:指向目标地址的结构体指针,通常是struct sockaddr
或其派生结构体的指针。tolen
:目标地址结构体的长度。
关于recvfrom()函数
官方文档:
recvfrom 函数接收数据报并存储源地址。
int recvfrom(
[in] SOCKET s,
[out] char *buf,
[in] int len,
[in] int flags,
[out] sockaddr *from,
[in, out, optional] int *fromlen
);
recvfrom()
是用于接收来自指定源地址的数据的函数,通常用于无连接的数据报套接字(SOCK_DGRAM
)。它可以从指定的源地址接收数据,并存储到指定的缓冲区中。
参数含义:
s
:要接收数据的套接字描述符(socket descriptor)。buf
:指向接收数据的缓冲区的指针。len
:缓冲区的长度,即最大接收的字节数。flags
:接收标志,用于控制接收操作的行为,例如是否启用特定的选项。from
:指向存储源地址的结构体的指针,通常是struct sockaddr
或其派生结构体的指针。fromlen
:指向存储源地址结构体长度的整型指针。