一、代码实现
服务端
#include "stdafx.h"
#include <iostream>
#include <Winsock2.h>
#include <WS2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
using namespace std;
int main()
{
WSADATA wsaData;
SOCKET s;
SOCKADDR_IN server;
char sendbuf[1024], recvbuf[1024];
int err = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (-1 == err)
{
cerr << "WSAStartup调用失败." << endl;
goto Error;
}
//创建socket
s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (INVALID_SOCKET == s)
{
cerr << "SOCKET创建失败." << endl;
goto Error;
}
//绑定本地地址
server.sin_family = AF_INET;
inet_pton(AF_INET, "127.0.0.1", &server.sin_addr);
server.sin_port = htons(5000);
err = bind(s, (const sockaddr*)&server, sizeof server);
if (SOCKET_ERROR == err)
{
cerr << "绑定失败." << endl;
goto Error;
}
//接收客户端的数据
while (true)
{
SOCKADDR_IN fromaddr;
int len = sizeof(fromaddr);
memset(recvbuf, 0, 1024);
int iResult = recvfrom(s, recvbuf, 1024, 0, (sockaddr*)&fromaddr, &len);
if (iResult > 0)
{
char ip[16];
memset(ip, 0, sizeof ip);
inet_ntop(AF_INET, &fromaddr.sin_addr, ip, 16);
cout << "接收来自" << ip << "的数据:" << recvbuf << endl;
//发送数据
memset(sendbuf, 0, 1024);
strcpy_s(sendbuf, "Hello Client");
int err = sendto(s, sendbuf, 13, 0, (const sockaddr*)&fromaddr, sizeof fromaddr);
if (SOCKET_ERROR == err)
{
cerr << "发送失败." << endl;
goto Error;
}
}
}
Error:
if (INVALID_SOCKET == s)
closesocket(s);
WSACleanup();
getchar();
return 0;
}
客户端
#include "stdafx.h"
#include <iostream>
#include <WinSock2.h>
#include <WS2tcpip.h>
#pragma comment(lib, "ws2_32.lib")
using namespace std;
int main()
{
WSADATA wsaData;
SOCKET s;
SOCKADDR_IN server;
SOCKADDR_IN fromaddr;
int len = sizeof(fromaddr);
char sendbuf[1024], recvbuf[1024];
int err = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (-1 == err)
{
cerr << "WSAStartup调用失败." << endl;
goto Error;
}
//创建socket
s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (INVALID_SOCKET == s)
{
cerr << "SOCKET创建失败." << endl;
goto Error;
}
//给指定的服务端发送数据
server.sin_family = AF_INET;
inet_pton(AF_INET, "127.0.0.1", &server.sin_addr);
//server.sin_addr.S_un.S_addr = inet_addr("127.0.0.1"); //inet_pton InetPton
server.sin_port = htons(5000);
memset(sendbuf, 0, 1024);
strcpy_s(sendbuf, "Hello Server"); //strcpy_s
err = sendto(s, sendbuf, 13, 0, (const sockaddr*)&server, sizeof(server));
if (SOCKET_ERROR == err)
{
cerr << "发送失败.";
goto Error;
}
while (true)
{
memset(recvbuf, 0, 1024);
int iResult = recvfrom(s, recvbuf, 1024, 0, (sockaddr*)&fromaddr, &len);
if (SOCKET_ERROR == iResult)
{
cerr << "接收失败.";
goto Error;
}
else if (iResult > 0)
{
//inet_ntop InetNtop
//cout << "接收来自" << inet_ntoa(fromaddr.sin_addr) << "的数据:" << recvbuf << endl;
char ip[16];
memset(ip, 0, 16);
inet_ntop(AF_INET, &fromaddr.sin_addr, ip, 16);
cout << "接收来自" << ip << "的数据:" << recvbuf << endl;
}
}
Error:
if (INVALID_SOCKET == s)
closesocket(s);
WSACleanup();
getchar();
return 0;
}
二、总结
- inet_pton和inet_ntop
在VS2015中使用inet_addr和inet_ntoa网络地址转换函数会报错,因此采用最新的替代函数inet_pton和inet_ntop。 这两个函数是随IPv6出现的函数,对于IPv4地址和IPv6地址都适用,函数中p和n分别代表表达(presentation)和数值(numeric)。地址的表达格式通常是ASCII字符串,数值格式则是存放到套接字地址结构的二进制值。
/**
* @brief 将点分十进制的ip地址转化为用于网络传输的数值格式
* @return 若成功则为1,若输入不是有效的表达式则为0,若出错则为-1
*/
int inet_pton(int family, const char *strptr, void *addrptr);
/**
* @brief 将数值格式转化为点分十进制的ip地址格式
* @return 若成功则为指向结构的指针,若出错则为NULL
*/
const char * inet_ntop(int family, const void *addrptr, char *strptr, size_t len);
- 服务器端如何知道客户端的IP和端口信息
1.客户端向服务端发送消息,服务端通过recvfrom收到消息和客户端的地址信息,然后服务端就可以给这个客户端地址发送消息。