ide:vs2019
client连接server
client发消息给server
server转发给其他的client
主要用select
wrap.h
#pragma once
#include<WinSock2.h>
#include<iostream>
using namespace std;
void init_wsa() {
//socket 版本
WORD socket_version = MAKEWORD(2, 2);
WSAData wsa_data;
//初始化wsa
if (WSAStartup(socket_version, &wsa_data) != 0) {
cout << "初始化wsa失败" << endl;
exit(-1);
}
//检查socket版本
if (LOBYTE(wsa_data.wVersion) != 2 ||
HIBYTE(wsa_data.wVersion) != 2) {
cout << "套接字库版本号不符" << endl;
WSACleanup();
exit(-1);
}
}
int CloseSocket(SOCKET& s) {
if (SOCKET_ERROR == closesocket(s)) {
cout << "close error" << WSAGetLastError() << endl;
WSACleanup();
exit(-1);
}
return 0;
}
SOCKET Accept(SOCKET& s, sockaddr* addr, int* addrlen) {
while (true) {
SOCKET cfd = accept(s, addr, addrlen);
if (INVALID_SOCKET == cfd) {
if (ECONNABORTED == errno || EINTR == errno) {
continue;
}
cout << "connect error" << WSAGetLastError() << endl;
CloseSocket(s);
WSACleanup();
exit(-1);
}
return cfd;
}
cout << "connect error" << WSAGetLastError() << endl;
closesocket(s);
WSACleanup();
exit(-1);
}
int Bind(SOCKET& s, const sockaddr* name, int namelen) {
int result = bind(s, name, namelen);
if (SOCKET_ERROR == result) {
cout << "bind error" << WSAGetLastError() << endl;
CloseSocket(s);
WSACleanup();
exit(-1);
}
return result;
}
SOCKET Socket(int af, int type, int protocol) {
SOCKET lfd = socket(af, type, protocol);
if (INVALID_SOCKET == lfd) {
cout << "socket error" << WSAGetLastError() << endl;
WSACleanup();
exit(-1);
}
return lfd;
}
int Listen(SOCKET& fd, int backlog) {
int result = listen(fd, backlog);
//设置同时连接的上限
if (SOCKET_ERROR == result) {
cout << "listen error" << WSAGetLastError() << endl;
CloseSocket(fd);
WSACleanup();
exit(-1);
}
return result;
}
int Connect(SOCKET& s, const sockaddr* name, int namelen) {
int result = connect(s, name, namelen);
if (SOCKET_ERROR == result) {
cout << "connect error" << WSAGetLastError() << endl;
closesocket(s);
WSACleanup();
exit(-1);
}
return result;
}
int Recv(SOCKET& s, char* buf, int len, int flags) {
while (true) {
int receive_len = recv(s, buf, len, flags);
if (SOCKET_ERROR == receive_len) {
if (EINTR == errno) {
continue;
}
return SOCKET_ERROR;
}
return receive_len;
}
return SOCKET_ERROR;
}
int Send(SOCKET& s, const char* buf, int len, int flags) {
while (true) {
if (SOCKET_ERROR == send(s, buf, len, flags)) {
if (errno == EINTR) {
continue;
}
return SOCKET_ERROR;
}
return 0;
}
return 0;
}
client
#include<iostream>
#include<string>
#include<thread>
#include<mutex>
#include<WinSock2.h>
#include<WS2tcpip.h>
#pragma comment(lib,"ws2_32.lib")
#include "wrap.h"
using namespace std;
mutex _mutex;
/*
* 接收数据
* @param cfd 文件描述符
*/
void receive(SOCKET& cfd) {
while (true) {
//接收数据
char receive_data[BUFSIZ];
//接收数据长度
int receive_len;
receive_len = Recv(cfd, receive_data, BUFSIZ, 0);
//发生错误
if (SOCKET_ERROR == receive_len) {
//cout << "receive error" << WSAGetLastError() << endl;
closesocket(cfd);
WSACleanup();
exit(-1);
}//断开连接
else if (0 == receive_len) {
closesocket(cfd);
WSACleanup();
exit(-2);
}
else {
lock_guard<mutex> guard(_mutex);
receive_data[receive_len] = '\0';
cout << receive_data << endl;
}
}
closesocket(cfd);
WSACleanup();
exit(0);
}
int main() {
const char* SERVER_IP = "127.0.0.1";
const u_short SERVER_PORT = 8888;
init_wsa();
//客户端文件描述符
SOCKET cfd = Socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in server_addr = {};
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(SERVER_PORT);
inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr.S_un.S_addr);
//连接
Connect(cfd, (sockaddr*)&server_addr, sizeof(server_addr));
cout << "建立连接" << endl;
//开线程接收数据
thread th(receive, ref(cfd));
th.detach();
string send_data;
while (true) {
getline(cin, send_data);
//读到EOF,断开连接
if (cin.eof()) {
break;
}
else if ("" == send_data) {
lock_guard<mutex> guard(_mutex);
cout << "输入为空" << endl;
continue;
}
//发送信息
if (SOCKET_ERROR == Send(cfd, send_data.c_str(), static_cast<int>(send_data.size()), 0)) {
cout << "send error" << WSAGetLastError() << endl;
closesocket(cfd);
WSACleanup();
return -1;
}
}
closesocket(cfd);
WSACleanup();
return 0;
}
server
#include<iostream>
#include<string>
#include<cstring>
#include<WinSock2.h>
#include<WS2tcpip.h>
#include<cctype>
#pragma comment(lib,"ws2_32.lib")
#include "wrap.h"
using namespace std;
int main() {
const u_short PORT = 8888;
char receive_data[BUFSIZ];
int receive_len;
init_wsa();
//server文件描述符
SOCKET lfd = Socket(AF_INET, SOCK_STREAM, 0);
sockaddr_in server_addr = {};
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(PORT);
server_addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY);
//端口复用
BOOL bOptVal = FALSE;
setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, (char*)&bOptVal, sizeof(bOptVal));
//绑定端口
Bind(lfd, (sockaddr*)&server_addr, sizeof(server_addr));
//设置同时连接的上限
Listen(lfd, SOMAXCONN);
//连接过来的客户端的文件描述符(-1表示没有连接)
SOCKET client[FD_SETSIZE];
string client_ip[FD_SETSIZE];
memset(client, -1, sizeof(int) * FD_SETSIZE);
//最大的文件描述符
int max_fd = static_cast<int>(lfd);
//文件描述符在client数组中最大的位置
int max_i = -1;
fd_set rset, all_set;
//初始化全0
FD_ZERO(&all_set);
FD_SET(lfd, &all_set);
//客户端
sockaddr_in client_addr;
//长度
socklen_t client_addr_len = sizeof(client_addr);
SOCKET cfd;
cout << "等待连接" << endl;
while (true) {
rset = all_set;
//由于只关心读的,所以其他的设成nullptr,返回满足条件的总数
int nready = select(max_fd + 1, &rset, nullptr, nullptr, nullptr);
if (SOCKET_ERROR == nready) {
cout << "select error" << WSAGetLastError() << endl;
closesocket(lfd);
WSACleanup();
return -1;
}
//lfd对应位置为1,说明有新连接,处理一个
if (FD_ISSET(lfd, &rset) != 0) {
client_addr_len = sizeof(client_addr);
//建立连接
cfd = Accept(lfd, (sockaddr*)&client_addr, &client_addr_len);
int i = 0;
while (i < FD_SETSIZE && client[i] != INVALID_SOCKET) {
++i;
}
//连接已经到达上线
if (i >= FD_SETSIZE) {
cout << "too many clients" << endl;
return -1;
}
//记录
client[i] = cfd;
client_ip[i].assign(inet_ntop(AF_INET, &client_addr.sin_addr.S_un.S_addr, receive_data, BUFSIZ));
client_ip[i] += ":";
client_ip[i] += to_string(ntohs(client_addr.sin_port));
cout << "client ip: " << client_ip[i] << endl;
//向all_set添加文件描述符cfd
FD_SET(cfd, &all_set);
//更新
if (cfd > max_fd) {
max_fd = static_cast<int>(cfd);
}
if (i > max_i) {
max_i = i;
}
--nready;
//没有要处理的
if (0 == nready) {
continue;
}
}
for (int i = 0; i <= max_i; ++i) {
cfd = client[i];
if (INVALID_SOCKET == cfd) {
continue;
}
if (FD_ISSET(cfd, &rset) != 0) {
receive_len = Recv(cfd, receive_data, BUFSIZ, 0);
//读取错误
if (SOCKET_ERROR == receive_len) {
//cout << "receive error" << WSAGetLastError() << endl;
closesocket(cfd);
FD_CLR(cfd, &all_set);
client[i] = -1;
}
//客户端关闭连接
else if (0 == receive_len) {
closesocket(cfd);
FD_CLR(cfd, &all_set);
client[i] = -1;
}
else {
receive_data[receive_len] = '\0';
//ip:port
string msg = client_ip[i];
msg += " : ";
msg.append(receive_data);
SOCKET send_fd;
//广播消息
for (int j = 0; j <= max_i; ++j) {
send_fd = client[j];
if (INVALID_SOCKET == send_fd || cfd == send_fd) {
continue;
}
//发送错误
if (SOCKET_ERROR == Send(send_fd, msg.c_str(), static_cast<int>(msg.size()), 0)) {
cout << "send error" << WSAGetLastError() << endl;
closesocket(send_fd);
FD_CLR(send_fd, &all_set);
client[i] = -1;
}
}
}
--nready;
if (0 == nready) {
break;
}
}
}
}
closesocket(lfd);
WSACleanup();
return 0;
}