服务端
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <stdio.h>
#include <iostream>
#include <conio.h>
#include <stdlib.h>
#include <algorithm>
#include <set>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
#define SERVERPORT 6666
set<SOCKADDR_IN*> clientSockAddrInSet;
SOCKET serverSocket;
void SendMsg()
{
SOCKET tempSocket = socket(AF_INET, SOCK_DGRAM, 0);
while (1)
{
// '+' 43
// '/' 47
// '*' 42
char key = _getch();
if (key == '+') //发送消息
{
int sendtoRet = 0;
printf("【*】群发【/】单发\n");
key = _getch();
printf("发送内容:");
char sendStr[100] = {};
gets_s(sendStr, sizeof(sendStr));
if (key == '*') //群发
{
for (auto clientSockAddr : clientSockAddrInSet)
{
sendtoRet = sendto(serverSocket, sendStr, strlen(sendStr), 0, (sockaddr*)clientSockAddr, sizeof(SOCKADDR_IN));
}
}
else if (key == '/') //单发
{
printf("客户端端口号:");
int hostPort = 0; //主机端口号(小端形式)
char temp[100] = {};
gets_s((char*)&temp, sizeof(temp));
sscanf(temp, "%d", &hostPort);
set<SOCKADDR_IN*>::iterator iter;
iter = find_if(clientSockAddrInSet.begin(), clientSockAddrInSet.end(), [hostPort](set<SOCKADDR_IN*>::value_type temp)
{
return ntohs(temp->sin_port) == hostPort;
});
if (iter != clientSockAddrInSet.end())
{
/*************注意*********/
//如果客户端是通过connect方式连接的服务端,那么服务端给客户端发送数据的时候,sendto的第一个参数套接字必须要和主机IP+Port绑定
//非连接的方式,任意一个Socket都OK(当然协议要正确)
//sendtoRet = sendto(tempSocket, sendStr, strlen(sendStr), 0, (SOCKADDR*)(*iter), sizeof(SOCKADDR_IN));
/**************************/
sendtoRet = sendto(serverSocket, sendStr, strlen(sendStr), 0, (SOCKADDR*)(*iter), sizeof(SOCKADDR_IN));
}
else
{
printf("输入端口号有误\n");
continue;
}
}
if (sendtoRet != 0)
printf("已发送%dbyte\n", sendtoRet);
else
printf("发送失败\n");
}
}
}
int main()
{
WSADATA wsaData = { 0 };
WSAStartup(MAKEWORD(2, 2), &wsaData);
serverSocket = socket(AF_INET, SOCK_DGRAM, 0);
SOCKADDR_IN sockaddr_in = { 0 };
sockaddr_in.sin_family = AF_INET;
sockaddr_in.sin_addr.S_un.S_addr = INADDR_ANY; //inet_addr("127.0.0.1"); //接收任意ip+指定端口连入
sockaddr_in.sin_port = htons(SERVERPORT);
int bindRet = bind(serverSocket, (sockaddr*)&sockaddr_in, sizeof(sockaddr_in));
if(bindRet == SOCKET_ERROR)
{
cout << "绑定失败\n";
closesocket(serverSocket);
WSACleanup();
system("pause");
return 0;
}
cout << "准备好了\n";
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)SendMsg, 0, 0, 0); //准备发送消息线程
while(true)
{
SOCKADDR_IN clientSockAddr_in = {0};
int clientSockAddrLen = sizeof(SOCKADDR_IN);
char recvStr[100] = { 0 };
int recvfromRet = recvfrom(serverSocket, recvStr, sizeof(recvStr), 0, (SOCKADDR*)&clientSockAddr_in,&clientSockAddrLen);
if (recvfromRet != SOCKET_ERROR)
printf("客户端(%s:%d):%s\n", inet_ntoa(clientSockAddr_in.sin_addr), ntohs(clientSockAddr_in.sin_port) , recvStr);
set<SOCKADDR_IN*>::iterator iter;
//新的客户端接入
iter =find_if(clientSockAddrInSet.begin(), clientSockAddrInSet.end(), [clientSockAddr_in](set<SOCKADDR_IN*>::value_type temp)
{
return temp->sin_port == clientSockAddr_in.sin_port;
});
if (iter == clientSockAddrInSet.end())
{
SOCKADDR_IN* temp = new SOCKADDR_IN{ 0 };
*temp = clientSockAddr_in;
clientSockAddrInSet.insert(temp);
}
}
}
客户端
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <WinSock2.h>
#include <WS2tcpip.h>
#include <stdio.h>
#include <iostream>
using namespace std;
#pragma comment(lib,"ws2_32.lib")
#define SERVERPORT 6666
#define SERVERIP "172.16.20.203"
//#define SERVERIP "192.168.74.255"123
#define PRINTIPPORT(clientSocket) \
{ SOCKADDR_IN sockaddrTemp = { 0 }; \
int sockaddrTempLen = sizeof(sockaddrTemp); \
getsockname(clientSocket, (SOCKADDR*)&sockaddrTemp, &sockaddrTempLen); \
cout << inet_ntoa(sockaddrTemp.sin_addr) << ":" << ntohs(sockaddrTemp.sin_port) << endl; \
}
void RecvMsg(SOCKET* socket)
{
while (true)
{
char recvStr[100] = { 0 };
SOCKADDR_IN sockaddr_in = {0};
int len = sizeof(sockaddr);
int recvRet = recvfrom(*socket, recvStr, sizeof(recvStr), 0, (sockaddr*)&sockaddr_in,&len);
if (recvRet != SOCKET_ERROR)
printf("服务端(%s:%d):%s\n", inet_ntoa(sockaddr_in.sin_addr), ntohs(sockaddr_in.sin_port), recvStr);
else
break;
}
}
int main()
{
WSADATA wsaData = { 0 };
WSAStartup(MAKEWORD(2, 2), &wsaData);
SOCKET clientSocket = socket(AF_INET, SOCK_DGRAM, 0);
//******bind自定义端口号测试*********/
//SOCKADDR_IN clientSockAddr_in = { 0 };
//clientSockAddr_in.sin_family = AF_INET;
//clientSockAddr_in.sin_addr.S_un.S_addr = INADDR_ANY;
//clientSockAddr_in.sin_port = htons(2222);
//if (bind(clientSocket, (SOCKADDR*)&clientSockAddr_in, sizeof(clientSockAddr_in)) == SOCKET_ERROR)
//{
// cout << "绑定套接字失败\n";
// closesocket(clientSocket);
// WSACleanup();
// system("pause");
// return 0;
//}
/***************************************/
SOCKADDR_IN sockaddr_in = { 0 };
sockaddr_in.sin_family = AF_INET;
sockaddr_in.sin_addr.S_un.S_addr = inet_addr(SERVERIP);
sockaddr_in.sin_port = htons(SERVERPORT);
//打印当前客户端使用的IP+Port
PRINTIPPORT(clientSocket)
//UDP的connect函数不用在服务端启动之后调用(客户端根本不关心服务端在不在,它只要让本地套接字知道了目的地址罢了)
int connectRet = connect(clientSocket, (sockaddr*)&sockaddr_in, sizeof(sockaddr_in));
if(connectRet == SOCKET_ERROR)
{
cout << "连接失败\n";
closesocket(clientSocket);
WSACleanup();
system("pause");
return 0;
}
//打印当前客户端使用的IP+Port
PRINTIPPORT(clientSocket)
while(true)
{
cout << "输入信息:";
char sendStr[100] = { 0 };
gets_s(sendStr, sizeof(sendStr));
//int sendtoRet = sendto(clientSocket, sendStr, strlen(sendStr), 0, (sockaddr*)&sockaddr_in, sizeof(sockaddr_in)); //sendto之后socket和ip+port系统进行绑定
int sendtoRet = sendto(clientSocket, sendStr, strlen(sendStr), 0, 0,0); //使用了connect方式,sockaddr_in 参数不需要了
if (sendtoRet != SOCKET_ERROR)
cout << "发送" << sendtoRet << "byte\n";
//接收一个服务端
static int flag = 0;
if (flag == 0)
{
flag = 1;
CreateThread(0, 0, (LPTHREAD_START_ROUTINE)RecvMsg, &clientSocket, 0, 0); //必须在和服务端通信过之后才能接收消息(通信一次之后)
}
//打印当前客户端使用的IP+Port
PRINTIPPORT(clientSocket)
}
}