socket多人聊天

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;
}
发布了93 篇原创文章 · 获赞 83 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_39942341/article/details/103638625