UDP简单通讯例子
需要注意的是:
inet_addr函数如果使用的是VS2014以上的库编译时,会报错,所以我们需要选择兼容XP的编译方式,具体设置在项目属性中设置。如图所示:
服务端
// SocketUDPServer.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <WinSock2.h>
#include <Windows.h>
#include <clocale>
#pragma comment(lib, "ws2_32.lib")
void PrintErrorMessage(DWORD dwErrorCode)
{
setlocale(LC_ALL, "chs");
wchar_t strErrorMessage[MAXBYTE] = { 0 };
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, dwErrorCode, 0, strErrorMessage, MAXBYTE, nullptr);
wprintf(L"ErrorCode:%d ErrorMessage:%s", dwErrorCode, strErrorMessage);
}
int main()
{
WSAData wsaData;
int iErrorNumber = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iErrorNumber != 0)
{
printf("WSAStartup failed with error: %d\n", iErrorNumber);
return 1;
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
printf("Could not find a usable version of Winsock.dll\n");
WSACleanup();
return 1;
}
else
{
printf("The Winsock 2.2 dll was found okay\n");
}
SOCKET s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (INVALID_SOCKET == s)
{
PrintErrorMessage(GetLastError());
WSACleanup();
return 1;
}
sockaddr_in sockaddrServer, socketaddrClient;
sockaddrServer.sin_family = AF_INET;
sockaddrServer.sin_port = htons(10086);
sockaddrServer.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
bind(s, reinterpret_cast<sockaddr *>(&sockaddrServer), sizeof(sockaddrServer));
char buf[MAXBYTE] = { 0 };
int iLen = sizeof(sockaddr_in);
recvfrom(s, buf, MAXBYTE, 0, reinterpret_cast<sockaddr *>(&socketaddrClient), &iLen);
printf("received from Client:%s\r\n", buf);
scanf_s("%s", buf, MAXBYTE);
sendto(s, buf, MAXBYTE, 0, reinterpret_cast<sockaddr *>(&socketaddrClient), MAXBYTE);
closesocket(s);
WSACleanup();
system("pause");
return 0;
}
客户端
// SocketUDPClient.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <WinSock2.h>
#include <Windows.h>
#include <clocale>
#pragma comment(lib, "ws2_32.lib")
void PrintErrorMessage(DWORD dwErrorCode)
{
setlocale(LC_ALL, "chs");
wchar_t strErrorMessage[MAXBYTE] = { 0 };
FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, dwErrorCode, 0, strErrorMessage, MAXBYTE, nullptr);
wprintf(L"ErrorCode:%d ErrorMessage:%s", dwErrorCode, strErrorMessage);
}
int main()
{
WSAData wsaData;
int iErrorNumber = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iErrorNumber != 0)
{
printf("WSAStartup failed with error: %d\n", iErrorNumber);
return 1;
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
printf("Could not find a usable version of Winsock.dll\n");
WSACleanup();
return 1;
}
else
{
printf("The Winsock 2.2 dll was found okay\n");
}
SOCKET s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (INVALID_SOCKET == s)
{
PrintErrorMessage(GetLastError());
WSACleanup();
return 1;
}
sockaddr_in sockaddrClient;
sockaddrClient.sin_family = AF_INET;
sockaddrClient.sin_port = htons(10086);
sockaddrClient.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");
char buf[MAXBYTE] = "Hello";
int iResult = sendto(s, buf, MAXBYTE, 0, reinterpret_cast<sockaddr *>(&sockaddrClient), sizeof(sockaddr_in));
if (iResult == SOCKET_ERROR)
{
wprintf(L"sendto failed with error: %d\n", WSAGetLastError());
closesocket(s);
WSACleanup();
return 1;
}
recv(s, buf, MAXBYTE, 0);
printf("received from server:%s\r\n", buf);
closesocket(s);
WSACleanup();
system("pause");
return 0;
}
Socket的错误封装
这个错误封装的唯一一个好处就是能够深入的了解一下TCP和UDP的区别。
这个类没有写完整,还差一点儿通讯的功能,实在是写不下去了,以后如果有时间想写的时候再写吧。
里面用到了一些异常处理的方法,其实这些异常是没有必要的,我们可以做一些错误处理就可以了,而没有必要都要抛个异常。
有兴趣的朋友可以将这个类完善一下,从而达到简单通讯的目的。
头文件
#ifndef _SOCKET_H_
#define _SOCKET_H_
#include <exception>
#include <string>
#include <WinSock2.h>
#include <Windows.h>
#include <vector>
#pragma comment(lib, "ws2_32.lib")
namespace PoEdu
{
class SocketErrorException : public std::exception
{
public:
SocketErrorException(const char *msg, const int errorCode) : std::exception()
{
std::string temp = msg;
temp += std::to_string(errorCode);
len_ = temp.length() + sizeof(char);
CopyData(temp.c_str(), len_);
}
SocketErrorException(const SocketErrorException &other)
{
CopyData(other.msg_, other.len_);
}
SocketErrorException operator=(const SocketErrorException &other)
{
if (this == &other)
{
return *this;
}
CopyData(other.msg_, other.len_);
return *this;
}
~SocketErrorException()
{
delete[] msg_;
}
virtual char const* what() const
{
return msg_;
}
private:
void CopyData(const char *data, const size_t len)
{
msg_ = new char[len_];
strcpy_s(msg_, len_, data);
}
private:
char *msg_;
size_t len_;
};
class Socket
{
public:
enum SocketType
{
SOCKETTYPE_UDP,
SOCKETTYPE_TCP
};
Socket(SocketType socketTyp) throw(SocketErrorException);
~Socket() throw();
bool ServerStart(unsigned int port, unsigned maxCount);
bool ServerStart(sockaddr_in &addr, unsigned maxCount) throw(SocketErrorException);
bool ServerAccept();
bool Connect(const char *ip, unsigned int port);
bool Connect(sockaddr_in &addr);
bool Send(const char *data, const size_t len);
bool SendTo(const char *data, const size_t len, sockaddr_in &addr);
int Recv(char *data, const size_t maxSize);
int RecvFrom(char *data, const size_t maxSize, sockaddr_in &addr);
private:
SOCKET socket_;
SocketType socketType_;
bool server_;
std::vector<SOCKET> vecAcceptSockt_;
};
}
#endif//_SOCKET_H_
cpp文件
#include "stdafx.h"
#include "Socket.h"
#include <algorithm>
namespace PoEdu
{
Socket::Socket(SocketType socketTyp) : socketType_(socketTyp), server_(false)
{
WSAData wsaData;
int iErrorCode = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (iErrorCode != 0)
{
throw SocketErrorException("WSAStartup failed with errorCode:", iErrorCode);
}
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2)
{
WSACleanup();
throw SocketErrorException("Could not find a usable version of Winsock.dll. ErrorCode:", wsaData.wVersion);
}
int socket_af = AF_INET, sock_type = SOCK_DGRAM, sock_protocal = IPPROTO_UDP;
if (socketType_ == SOCKETTYPE_TCP)
{
sock_type = SOCK_STREAM;
sock_protocal = IPPROTO_TCP;
}
socket_ = socket(socket_af, sock_type, sock_protocal);
if (socket_ == INVALID_SOCKET)
{
WSACleanup();
throw SocketErrorException("cocket error:", WSAGetLastError());
}
}
Socket::~Socket()
{
closesocket(socket_);
WSACleanup();
}
bool Socket::ServerStart(unsigned int port, unsigned maxCount)
{
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.S_un.S_addr = INADDR_ANY;
// 127.0.0.1 环路地址
// 192.168.1.1 LAN网地址
// 110.110.11.1 上一级网络地址
return ServerStart(addr, maxCount);
}
bool Socket::ServerStart(sockaddr_in& addr, unsigned maxCount)
{
bool bRet = false;
do
{
if (::bind(socket_, (sockaddr *)&addr, sizeof(sockaddr_in)) == SOCKET_ERROR)
{
break;
}
if (socketType_ == SOCKETTYPE_TCP)
{
if (::listen(socket_, maxCount) == SOCKET_ERROR)
{
break;
}
}
server_ = true;
bRet = true;
} while (false);
return bRet;
}
bool Socket::ServerAccept()
{
bool bRet = false;
if (server_ && socketType_ == SOCKETTYPE_TCP)
{
SOCKET s = ::accept(socket_, nullptr, nullptr);
if (s != SOCKET_ERROR)
{
vecAcceptSockt_.push_back(s);
bRet = true;
}
}
return bRet;
}
bool Socket::Connect(const char* ip, unsigned int port)
{
sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.S_un.S_addr = inet_addr(ip);
return Connect(addr);
}
bool Socket::Connect(sockaddr_in& addr)
{
bool bRet = false;
if (socketType_ == SOCKETTYPE_TCP)
{
if (::connect(socket_, (sockaddr *)&addr, sizeof(sockaddr_in)) != SOCKET_ERROR)
{
bRet = true;
}
}
return bRet;
}
bool Socket::Send(const char* data, const size_t len)
{
bool bRet = false;
if (socketType_ == SOCKETTYPE_TCP)
{
if (server_)
{
// 有可能群聊,也有可能单聊
// 假设群发
std::vector<int> vecRet;
for (auto value : vecAcceptSockt_)
{
vecRet.push_back(::send(value, data, len, 0));
}
if (std::find_if(vecRet.begin(), vecRet.end(), [](int result)
{
return result == SOCKET_ERROR;
}) == vecRet.end())
{
bRet = true;
}
}
else
{
if (::send(socket_, data, len, 0) != SOCKET_ERROR)
{
bRet = true;
}
}
}
return bRet;
}
bool Socket::SendTo(const char* data, const size_t len, sockaddr_in &addr)
{
bool bRet = true;
::sendto(socket_, data, len, 0, (sockaddr *)&addr, sizeof(addr));
return bRet;
}
int Socket::Recv(char* data, const size_t maxSize)
{
if (socketType_ == SOCKETTYPE_TCP)
{
if (server_)
{
int iRet = 0;
for (auto value : vecAcceptSockt_)
{
// 用多线程,每个线程对应一个客户端
iRet += ::recv(value, data, maxSize, 0);
}
return iRet;
}
else
{
return ::recv(socket_, data, maxSize, 0);
}
}
}
int Socket::RecvFrom(char* data, const size_t maxSize, sockaddr_in& addr)
{
return 0;
}
}
// server -> client 一呼一应