流程图
头文件和库
#include <WinSock2.h>
#pragma comment(lib,"ws2_32.lib")
初始化套接字库
初始化函数:
int WSAAPI WSAStartup
(
WORD wVersionRequested,
LPWSADATA lpWSAData
);
返回值:如果申请成功,返回值为 0,失败直接返回失败的代码,无法调用WSAGetLastError()。
示例:
WSADATA wsData;
int nRet=WSAStartup(MAKEWORD(2,2),&wsData);
if(nRet!=0)
{
return nRet;
}
申请监听套接字
WINSOCK_API_LINKAGE SOCKET WSAAPI socket(
int af,
int type,
int protocol
);
备注:af 主要指IP的类型,常用的有:AF_INET(ip4) 、AF_INET6(ip6)
type 是通信类型 ,常用是:SOCK_DGRAM(UDP)、SOCK_STREAM(TCP)
protocol:是指通信协议,常用IPPROTO_TCP和IPPROTO_UDP,一般写成 0
返回值:成功返回一个Socket ,失败返回值为INVALID_SOCKET,可以调用WSAGetLastError()函数获取返错误码。
示例:
SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock==INVALID_SOCKET)
{
return WSAGetLastError();
}
初始地址结构体
sockaddr_in sa,recSa;
int len = sizeof(sa);
sa.sin_addr.S_un.S_addr = INADDR_ANY;
sa.sin_family = AF_INET;
sa.sin_port = htons(9999);
绑定监听套接字
int WSAAPI bind(
SOCKET s,
const sockaddr *name,
int namelen
);
返回值:成功返回 0,失败返回SOCKET_ERROR,调用WSAGetLastError()函数获取返错误码。
示例:
int ret=bind(sock, (sockaddr*)&sa, len);
if(ret==SOCKET_ERROR)
{
return WSAGetLastError();
}
接收数据
int WSAAPI recvfrom(
SOCKET s,
__out_data_source(NETWORK) char *buf,
int len,
int flags,
sockaddr *from,
int *fromlen
);
备注: s 是监听套接字
buf 是接收数据的缓冲区
*from 是一个新的地址结构体,存储发送端的ip和端口信息
*fromlen 地址结构体的长度指针
返回值:成功返回是接收长度,返回 0表示对方连接已经关闭(UDP不涉及),否则返回一个SOCKET_ERROR
示例:
char buf[1024];
memset(buf, 0, 1024);
int nlen = recvfrom(sock, buf, 1024, 0, (sockaddr*)&recSa, &len);
if (nlen>0)
{
char sIP[20]={0};
inet_ntop(AF_INET, &recSa.sin_addr, sIP, 20);
cout <<sIP<< buf << endl;
//inet_ntoa(recSa.sin_addr) vs2019 这个函数不安全,不推荐使用
}
注意:使用inet_ntop() 这个函数需要添加头文件
#include <WS2tcpip.h>
发送数据
int WSAAPI sendto(
SOCKET s,
const char *buf,
int len,
int flags,
const sockaddr *to,
int tolen
);
备注:s 申请的通讯套接字
*buf 要发送的缓冲区
len 发送的长度
flags 调用的方式,一般写为 0
*to 目的地址指针
tolen 地址结构长度
返回值 :成功返回发送的长度,失败返回 SOCKET_ERROR
示例:
memset(sendBuf, 0, sizeof(sendBuf));
cout << "pelase input word:";
cin.getline(sendBuf, 64);
sendto(psock, sendBuf, sizeof(sendBuf), 0, (sockaddr*)&sClient, len);
关闭套接字和套接字库
closesocket(sock);
WSACleanup();
接收端和发送端的区别
两者主要是发送端不需要进行绑定操作,其他基本上无差别,两者的全部代码如下:
接收端:
#include <iostream>
#include <WinSock2.h>
#include <WS2tcpip.h>
#pragma comment (lib,"ws2_32.lib")
using namespace std;
int main()
{
WSADATA wsData;
int nret=WSAStartup(MAKEWORD(2, 2), &wsData);
if(nret!=0)
{
return nret;
}
sockaddr_in sa,recSa;
int len = sizeof(sa);
sa.sin_addr.S_un.S_addr = INADDR_ANY;
sa.sin_family = AF_INET;
sa.sin_port = htons(9999);
SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock==INVALID_SOCKET)
{
return WSAGetLastError();
}
bind(sock, (sockaddr*)&sa, len);
while (true)
{
char buf[1024];
memset(buf, 0, 1024);
int nlen = recvfrom(sock, buf, 1024, 0, (sockaddr*)&recSa, &len);
if (nlen>0)
{
char sIP[20];
inet_ntop(AF_INET, &recSa.sin_addr, sIP, 20);
//inet_ntoa(recSa.sin_addr);
cout << sIP<<buf << endl;
}
}
}
发送端:
// Client_UDP.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <WS2tcpip.h>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
int main()
{
WSADATA wdata;
WORD wVersion;
wVersion = MAKEWORD(2, 2);
WSAStartup(wVersion, &wdata);
if (HIBYTE(wdata.wVersion) != 2 || LOBYTE(wdata.wVersion) != 2)
{
return -1;
}
sockaddr_in sClient;
sClient.sin_family = AF_INET;
sClient.sin_port = htons(9999);
inet_pton(AF_INET, "127.0.0.1", &sClient.sin_addr);
SOCKET psock = socket(AF_INET, SOCK_DGRAM, 0);
int len = sizeof(sClient);
char sendBuf[128];
while (1)
{
memset(sendBuf, 0, sizeof(sendBuf));
cout << "pelase input word:";
cin.getline(sendBuf, 64);
sendto(psock, sendBuf, sizeof(sendBuf), 0, (sockaddr*)&sClient, len);
}
return 0;
}