Winsock套接字开发网络聊天室实例(C/S)模式

news2024/12/26 2:11:05

聊天室的基本要求

聊天器采用客户端/服务器(C/S)模式;
1,客户端利用UDP与服务器连接,客户端与客户端之间通过UDP互相通讯;
2,服务器端具有服务器端口设置,维护客户端个人信息,记录客户端状态,分配账号等功能
     客户端具有服务器地址及端口设置,用户注册,用户登陆,添加好友和删除好友,查看好友信       息,给好友发送消息等功能;
3,服务器与客户端间、客户端之间的交互采用控制台方式或GUI窗口方式均可;


聊天器实例的实现情况

1,程序完成了基于客户端/服务器(C/S)模式的设计目标,模拟出类似于QQ聊天室的应用方式,并实现其相关的基本功能。
2,程序完成了基于UDP的设计目标,实现了客户端与服务器,客户端与客户端之间通过UDP互相通讯的设计模式。
3,服务器端实现了具有服务器端口设置,维护客户端个人信息,记录客户端状态等应用功能;客户端实现了具有服务器地址及端口设置,用户注册,用户登陆,给好友发送消息,实现多个用户之间群聊等应用功能。
4,不足之处在于服务器与客户端间、客户端之间采用控制台方式实现了相互交互,未实现基于GUI窗口开发的方式。


运行注意事项(看完再去运行蛤)

    这个基于winsock的UDP网络聊天器是本人的期末大作业的内容,因为自己知道做一个课程设计对于很多同学来说难度都太大了,为了让大家能够在更短的时间内有一份可以完整提交的项目以及答辩顺利,所以我无偿的发布出我的大作业供大家学习使用。

项目基于的环境

  • windows操作系统
  • Visual studio 2022 编译器
  • X86(debug)运行架构

网上的项目繁多,所以需要找到适合自己的去学习使用,比如这门课对于苹果电脑的同学就不是很友好。

项目运行须知

同绝大多数的通信实例一样,网络编程对于网络环境的环境&介质要求很高,所以在运行项目之前,请确保运行项目的主机网络的畅通,关闭windows系统的防火墙,连上自己的手机热点做局域网测试效果为佳。

还有要清楚项目的运行逻辑,基于C/S模型的程序实例,一定要记得要先运行服务器,然后在运行客户端,客户端才可以正常访问服务器,并且要注意服务器和客户端的IP与端口信息,这个对于网络通信是非常重要的,很多bug往往不是代码本身的问题,而是出现在了网络配置的问题上,所以如果你是第一在电脑上运行项目,那么第一次就要做好程序在主机上的网络配置,找到代码里有关于网络配置的相关定义进行修改哦。

最后,在用编辑器调试程序运到报错运行不起来时,要学会复制所报出的问题,在百度上搜索问题的答案,这是基本功。


部分实例演示

服务器的运行界面

客户端的运行界面

 

用户上线提醒

用户注册提醒

 私聊功能的实现

 

群聊功能的实现

 

 聊天室的整体设计 

该程序采用经典的c/s架构,即采用客户端/服务器架构。模型的设计包括三个主要部分,即文件存储部分,服务器部分,以及客户端部分。

如图3.1.1所示,其中,服务器的功能为接收发送器的消息请求,并根据消息类型进行不同的响应处理;服务器中通过文件存储用户的用户名和密码。

客户端的功能实现包括两个部分:发送器和接收器。其中发送器所实现的功能包括注册新账号,登录已有账号,发送群聊消息和私聊消息等;接收器主要实现的功能包括接收服务器转发的群聊消息和私聊消息,并将其显示在显示屏上。

    服务器要处理的消息类型一共有五种,分别是登录请求、注册请求、群聊消息、私聊消息、退出命令。这五种消息类型,设计使用字符串的第一个字符来进行区分,比如’L‘是Login的首字母,用来作为登录请求的标志,’R‘是Rigister的首字母,用来作为注册请求的标志,’G‘是Group的首字母,用来作为群聊消息的标志,’P'是Personal的首字母,用来作为私聊消息的标志,最后字符串"exit"可以作为用户退出的命令。

 服务器要处理的五种消息类型对应着聊天器所要实现的五种功能,即登录功能,注册功能,群聊功能,私聊功能,关闭程序。下文将针对聊天器所要实现的功能给出设计的方案与实现。

 聊天室服务器完整代码

#include<WinSock2.h>
#include<iostream>
#include<fstream>
#include<vector>
#include<string>
#include<cstdlib>
#pragma comment(lib,"ws2_32.lib")
using namespace std;

#define DEFAULT_PORT 5055
#define BUFFER_LENGTH 1024
class user
{
public:
	user(string username, string ip, int sender_port, int receiver_port)
	{
		this->username = username;
		this->ip = ip;
		this->sender_port = sender_port;
		this->receiver_port = receiver_port;

		//设置接收器的地址
		receiver.sin_family = AF_INET;
		receiver.sin_port = htons(receiver_port);
		char *addr = new char[ip.length() + 1];
		strcpy(addr, ip.c_str());
		receiver.sin_addr.s_addr = inet_addr(addr);
	}
	string username;              //用户名
	string ip;                    //客户端ip地址
	int sender_port;              //发送器端口
	int receiver_port;	          //接收器端口
	struct sockaddr_in receiver;  //存储接收器的地址
};
class server
{
public:
	bool Startup();                                                   //检测是否满足服务器运行的环境
	bool SetServerSocket();                                           //设置服务器用来监听信息的socket套接字
	bool Checktxt();                                                  //检测存储文件是否存在,若不存在,创建一个
	void work();                                                      //服务器运行的主函数
	void SendMessage(string message, struct sockaddr_in x);           //发送信息的函数
	void Sendonlinelist();                                            //向客户端发送好友在线列表
	bool TestUsernameAndPassword(string username, string password, int &flag);   //测试用户名和密码是否正确
	bool TestDuplicateLogin(string username);                         //测试是否重复登录
	bool TestDuplicateRigister(string username);                      //测试是否重复注册
	string Getusername(string ip, int port);                           //根据ip和端口号获得用户名
	int  Getuserindex(string username);                               //根据用户名获得用户在在线用户表的索引号	
	void extractLoginuserinfor(string userinfor, string &username, string &password, string &receiverport); //提取登录请求中的用户名密码和显示器端口号
	void extractRegisteruserinfor(string userinfor, string&username, string&password);                       //提取注册请求中的用户名和密码
	void extactPersonalMessageReceivername(string &message, string &receivername);                           //提取私聊消息中的接收者的姓名
	
private:
	WSADATA wsaData;
	SOCKET sSocket;                            //用来接收消息的套接字
	struct sockaddr_in ser;                    //服务器地址
	struct sockaddr_in cli;                    //客户地址
	int cli_length = sizeof(cli);                //客户地址长度
	char recv_buf[BUFFER_LENGTH];              //接收数据的缓冲区
	vector<user> usertable;                    //在线用户表
	string sendmessage, printmessage;           //存储服务器转发、打印用的字符串
	int iSend, iRecv;                          //存储服务器发送和接收的字符串的长度
};

bool server::Startup()
{
	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
	{
		cout << "Failed to load Winsock." << endl;
		return false;
	}
	return true;
}
bool server::SetServerSocket()
{
	//产生服务器端套接口
	sSocket = socket(AF_INET, SOCK_DGRAM, 0);
	if (sSocket == INVALID_SOCKET)
	{
		cout << "socket()Failed:" << WSAGetLastError() << endl;
		return false;
	}
	//建立服务器端地址
	ser.sin_family = AF_INET;
	ser.sin_port = htons(DEFAULT_PORT);               //htons()函数把一个双字节主机字节顺序的数转换为网络字节顺序的数
	ser.sin_addr.s_addr = htonl(INADDR_ANY);          //htonl()函数把一个主机字节顺序的数转换为网络字节顺序的数   
	if (bind(sSocket, (LPSOCKADDR)&ser, sizeof(ser)) == SOCKET_ERROR)
	{
		cout << "bind()Failed:" << WSAGetLastError() << endl;
		return false;
	}
	return true;
}
void server::SendMessage(string message, struct sockaddr_in x)
{
	char *send_buf = new char[message.length() + 1];
	strcpy(send_buf, message.c_str());
	SOCKET rSocket = socket(AF_INET, SOCK_DGRAM, 0);
	if (rSocket == INVALID_SOCKET)
	{
		cout << "socket()Failed:" << WSAGetLastError() << endl;
		return;
	}
	iSend = sendto(rSocket, send_buf, message.length() + 1, 0, (SOCKADDR*)&(x), sizeof(x));
	if (iSend == SOCKET_ERROR)
	{
		cout << "sendto failed:" << WSAGetLastError() << endl;
		closesocket(rSocket);
		return;
	}
	closesocket(rSocket);
}
void server::Sendonlinelist()
{
	string onlinelist;
	for (int i = 0; i < usertable.size(); i++)
		onlinelist = onlinelist + usertable[i].username + "#";
	onlinelist = onlinelist + "$";                //结束标志
	SendMessage(onlinelist, cli);
}
bool server::TestUsernameAndPassword(string username, string password, int &flag)
{
	if (!Checktxt())
	{
		cout << "无法找到存储文件." << endl << endl;
		flag = 0;                                 //未找到用户名的标志
		return false;
	}
	fstream in("C:\\userform\\userform.txt");
	string line;
	string username_txt, password_txt;
	while (getline(in, line))
	{
		for (int i = 0; i < line.size(); i++)
		{
			if (line[i] == '#')
			{
				username_txt = line.substr(0, i);
				password_txt = line.substr(i + 1);
				break;
			}
		}
		if (username_txt == username)         //该用户名存在
		{
			if (password == password_txt)     //且密码正确
			{
				in.close();
				return true;                  //返回验证成功
			}
			cout << "用户" << username << "登录密码错误" << endl << endl;       //返回密码错误的信息
			flag = 1;                        //密码错误的标志
			return false;
		}
	}
	in.close();
	cout << "未注册过的用户:" << username << endl << endl;
	flag = 0;                                 //未找到用户名的标志
	return false;
}
bool server::TestDuplicateLogin(string username)
{
	int i;
	for (i = 0; i < usertable.size(); i++)
		if (usertable[i].username == username) break;
	if (i == usertable.size())     //该用户还没有登录过
		return false;
	else
	{
		cout << "用户" << username << "重复登录" << endl;
		return true;
	}
}
bool server::TestDuplicateRigister(string username)
{
	if (!Checktxt())
	{
		cout << "无法找到存储文件." << endl << endl;
		return true;
	}
	fstream in("C:\\userform\\userform.txt");
	string line;
	while (getline(in, line))
	{
		string username_txt;
		for (int i = 0; i < line.size(); i++)
		{
			if (line[i] == '#')
			{
				username_txt = line.substr(0, i);              //提取用户名
				if (username_txt == username)                  //对比,相等则表明已经注册过
				{
					in.close();
					cout << "用户名" << username << "重复注册" << endl << endl;
					return true;
				}
				break;                                         //否则继续对比下一个用户名
			}
		}
	}
	in.close();
	return false;                                               //代码执行到这说明该用户名还没有注册过
}
string server::Getusername(string ip, int port)
{
	for (int i = 0; i < usertable.size(); i++)
		if (usertable[i].ip == ip&&usertable[i].sender_port == port)
			return usertable[i].username;
	cout << "非法的用户连接上服务器" << endl;
	cout << "ip地址为:" << ip << endl << "端口号为:" << port << endl;
	return "";
}
int server::Getuserindex(string username)
{
	int i = 0;
	for (i = 0; i < usertable.size(); i++)
		if (usertable[i].username == username) break;
	return i;
}
void server::extractLoginuserinfor(string userinfor, string &username, string &password, string &receiverport)
{
	int i;
	for (i = 0; i < userinfor.length(); i++)           //提取用户名
	{
		if (userinfor[i] == '#')
		{
			username = userinfor.substr(0, i);
			break;
		}
	}
	for (int j = i + 1; j < userinfor.length(); j++)  //提取密码和显示器端口号
	{
		if (userinfor[j] == '#')
		{
			password = userinfor.substr(i + 1, j - i - 1);
			receiverport = userinfor.substr(j + 1);
			break;
		}
	}
}
void server::extractRegisteruserinfor(string userinfor, string&username, string&password)
{
	for (int i = 0; i < userinfor.size(); i++)
	{
		if (userinfor[i] == '#')
		{
			username = userinfor.substr(0, i);
			password = userinfor.substr(i + 1);
			break;
		}
	}
}
void server::extactPersonalMessageReceivername(string &message, string &receivername)
{
	for (int i = 0; i < message.size(); i++)
	{
		if (message[i] == '#')
		{
			receivername = message.substr(0, i);
			message = message.substr(i + 1);
			break;
		}
	}
}
bool server::Checktxt()
{
	FILE *fp = fopen("C:\\userform\\userform.txt", "r");
	if (fp == NULL)
	{
		system("md C:\\userform");
		ofstream out("C:\\userform\\userform.txt");
		if (!out)
			return false;
		out.close();
		return true;
	}
	return true;
}
void server::work()
{
	cout << "-----------------" << endl;
	cout << "Server running" << endl;
	cout << "-----------------" << endl;
	while (true)                                       //进入一个无限循环,进行数据接收和发送
	{
		memset(recv_buf, 0, sizeof(recv_buf));         //初始化接收缓冲区
		iRecv = recvfrom(sSocket, recv_buf, BUFFER_LENGTH, 0, (struct sockaddr*)&cli, &cli_length);
		if (iRecv == SOCKET_ERROR)
		{
			cout << "recvfrom()Failed:" << WSAGetLastError() << endl;
			continue;
		}

		//获取发送方的地址(ip和端口)
		char *x = inet_ntoa(cli.sin_addr); string address(x);         //获取客户端ip
		int userport = ntohs(cli.sin_port);                           //获取客户端端口

		string infortype = string(recv_buf);                          //根据infortype[0]来判断消息的类型       
		if (infortype[0] == 'L')                                      //登录请求
		{
			string userinfor = infortype.substr(1);                   //除去消息类型
			string username, password, receiver_port;
			extractLoginuserinfor(userinfor, username, password, receiver_port);  //提取用户名和密码
																				  //向不合法用户发送登录失败的回应
			int flag = 0;
			if (!TestUsernameAndPassword(username, password, flag))
			{
				if (flag == 0)
					SendMessage("0", cli);
				if (flag == 1)
					SendMessage("1", cli);
				continue;
			}
			//查询该用户是否重复登录
			if (TestDuplicateLogin(username))
			{
				SendMessage("2", cli);
				continue;
			}
			//将合法的未登录的用户加入列表
			int receiver_port_int = atoi(receiver_port.c_str());
			user newuser(username, address, userport, receiver_port_int);
			usertable.push_back(newuser);

			printmessage = "(上线消息)" + newuser.username + "已上线";               //设置要打印的消息
			sendmessage = printmessage;                                           //设置要转发的消息	
			SendMessage("Y", cli);                                                //向客户端发送登录成功的回应
		}
		else if (infortype[0] == 'R')                 //注册信息
		{
			string userinfor = infortype.substr(1);                  //除去消息类型		
			string username, password;
			extractRegisteruserinfor(userinfor, username, password); //提取用户名和密码

																	 //检测用户名是否已经注册过
			if (TestDuplicateRigister(username))
			{
				SendMessage("N", cli);
				continue;
			}
			//向文件写入新注册的用户名和密码
			if (!Checktxt())
			{
				SendMessage("N", cli);
				continue;
			}
			fstream out("C:\\userform\\userform.txt", ios::app);
			out << userinfor << endl;
			out.close();
			//发送注册成功的回应
			SendMessage("Y", cli);
			cout << "注册成功" << endl << "新用户名为:" << username << endl << endl;
			continue;
		}
		else if (infortype[0] == 'G')                                          //群聊消息
		{
			string message = infortype.substr(1);
			string sendername = Getusername(address, userport);                  //获取发送者姓名
			if (sendername == "")   continue;
			printmessage = "(群消息)" + sendername + ":" + message;             //设置要打印的消息
			sendmessage = printmessage;
			//sendmessage = "G#"+sendername + ":" + message;                       //设置要转发的消息
		}
		else if (infortype[0] == 'P')                     //私聊消息
		{
			if (infortype[1] == 'L')                      //获取在线好友列表的请求
			{
				Sendonlinelist();
				continue;
			}
			if (infortype[1] == 'M')                      //私聊消息
			{
				string message = infortype.substr(2);
				string sendername = Getusername(address, userport);  //提取发送者姓名
				if (sendername == "")  continue;
				//提取接收者姓名
				string receivername;
				extactPersonalMessageReceivername(message, receivername);
				//检查接收者是否离线
				int i = Getuserindex(receivername);
				if (i == usertable.size())                              //接收者已经离线
				{
					SendMessage("N", cli);
					continue;
				}
				SendMessage("Y", cli);                                  //向发送方发送成功的响应
				printmessage = "(私消息)" + sendername + "->" + receivername + ":" + message;               //设置要打印的消息
				cout << printmessage << endl;
				cout << "用户ip:" << address << endl;
				cout << "用户端口:" << userport << endl;
				cout << "当前在线人数:" << usertable.size() << endl << endl;
				sendmessage = printmessage;                                                                 //设置要发送的消息
				SendMessage(sendmessage, usertable[i].receiver);
				if (sendername != receivername)
				{
					int j = Getuserindex(sendername);
					SendMessage(sendmessage, usertable[j].receiver);
				}
				continue;
			}
		}
		else if (infortype == "exit")
		{
			string sendername = Getusername(address, userport);
			if (sendername == "") continue;
			int i = Getuserindex(sendername);
			if (i >= usertable.size() || i < 0) continue;
			SendMessage("exit", usertable[i].receiver);                                 //向该用户显示器发送退出命令
			usertable.erase(usertable.begin() + i);
			printmessage = "(下线消息)" + sendername + "已下线";                         //设置要打印的消息
			sendmessage = printmessage;                                                 //设置要转发的消息

		}
		//在服务器上打印消息	
		cout << printmessage << endl;
		cout << "用户ip:" << address << endl;
		cout << "用户端口:" << userport << endl;
		cout << "当前在线人数:" << usertable.size() << endl << endl;
		//向客户端发送消息
		for (int i = 0; i < usertable.size(); i++)
			SendMessage(sendmessage, usertable[i].receiver);
	}
	

}

int main()
{
	server x;
	if (x.Startup() == false)
		return 0;
	if (x.SetServerSocket() == false)
		return 0;
	x.work();
	return 0;
}

客户端发送器的完整代码

#include<Winsock2.h>
#include<iostream>
#include<string>
#include<ctime>
#include<tchar.h>
#include<Windows.h>
#include<fstream>
#include<vector>
#include <sstream>
using namespace std;
#pragma comment(lib,"ws2_32.lib")

#define DEFAULT_PORT 5055
#define DATA_BUFFER 1024

class client
{
public:
	bool Startup();
	void SetServerAddress();
	int GeneratePort();               //随机生成显示器端口号
	bool Getonlinelist();             //获得在线的用户名
	void work();                      //发送器的主函数
private:
	WSADATA wsaData;
	SOCKET sClient;                       //发送信息和接收信息时使用的套接字
	struct sockaddr_in ser;               //保存服务器的地址
	int ser_length = sizeof(ser);
	struct sockaddr_in communication;
	int communication_length = sizeof(communication);
	char recv_buf[DATA_BUFFER];           //接收信息的缓冲区
	int receiver_port;                    //显示器的端口号
	vector<string> onlinelist;            //保存在线用户的用户名
	int iSend, iRecv;
};

bool client::Startup()
{
	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
	{
		cout << "Failed to load Winsock." << endl;
		return false;
	}
	sClient = socket(AF_INET, SOCK_DGRAM, 0);
	if (sClient == INVALID_SOCKET)
	{
		cout << "socket()Failed:" << WSAGetLastError() << endl;
		return false;
	}
	return true;
}
void client::SetServerAddress()
{
	cout << "请输入ip地址:";
	string iptemp;
	cin >> iptemp;
	char *ip = new char[iptemp.length() + 1];
	strcpy(ip, iptemp.c_str());
	//建立服务器端地址
	ser.sin_family = AF_INET;
	ser.sin_port = htons(DEFAULT_PORT);
	ser.sin_addr.s_addr = inet_addr(ip);
}
int client::GeneratePort()
{
	srand((unsigned)time(NULL));
	int x = 1024 + rand() % (5000 - 1024);
	return x;
}
bool client::Getonlinelist()            //向服务器请求获取好友在线列表
{
	if (onlinelist.size() > 0)
		onlinelist.clear();
	char getonlinelist[3] = "PL";
	iSend = sendto(sClient, getonlinelist, 3, 0, (struct sockaddr*)&ser, ser_length);
	if (iSend == SOCKET_ERROR)
	{
		cout << "sendto()Failed:" << WSAGetLastError() << endl;
		return false;
	}
	memset(recv_buf, 0, sizeof(recv_buf));
	iRecv = recvfrom(sClient, recv_buf, sizeof(recv_buf), 0, (struct sockaddr*)&communication, &communication_length);   ///
	if (iRecv == SOCKET_ERROR)
	{
		cout << "recvfrom() Failed" << WSAGetLastError() << endl;
		return false;
	}
	string list(recv_buf);
	string friendname;
	for (int i = 0; i < list.length(); i++)
	{
		if (list[i] == '$')  break;
		else if (list[i] == '#')
		{
			onlinelist.push_back(friendname);
			friendname = "";
		}
		else
			friendname = friendname + list[i];
	}
	cout << "----------------------------" << endl;
	cout << "在线好友列表" << endl;
	for (int i = 0; i < onlinelist.size(); i++)
		cout << i << ":  " << onlinelist[i] << endl;
	cout << "----------------------------" << endl;
	return true;
}

void client::work()
{
	while (true)
	{
		memset(recv_buf, 0, sizeof(recv_buf));
		system("cls");
		cout << "****************************************" << endl;
		cout << "*                                      *" << endl;
		cout << "*       1.登录  2.注册  3.退出         *" << endl;
		cout << "*                                      *" << endl;
		cout << "****************************************" << endl;

		string choice;
		getline(cin, choice);
		if (choice == "1")
		{
			system("cls");
			cout << "请输入用户名:";
			string username;
			getline(cin, username);
			cout << "请输入密码:";
			string password;
			getline(cin, password);
			//产生显示器端口
			receiver_port = GeneratePort();
			//将端口号写入文件供显示器程序读取
			ofstream out("port.txt");
			out << receiver_port << "\n" << username;
			out.close();

			string init_infortemp = "L" + username + "#" + password + "#" + to_string(receiver_port);
			char *init_infor = new char[init_infortemp.length() + 1];
			strcpy(init_infor, init_infortemp.c_str());
			//向服务器验证用户信息
			iSend = sendto(sClient, init_infor, init_infortemp.length() + 1, 0, (struct sockaddr*)&ser, ser_length);
			//接收服务器回应的消息
			iRecv = recvfrom(sClient, recv_buf, sizeof(recv_buf), 0, (SOCKADDR*)&communication, &communication_length);
			if (iRecv == SOCKET_ERROR)
			{
				cout << "recvfrom() Failed:" << GetLastError() << endl;
				cout << "未收到服务器的响应,登录失败,请输入Y返回首页:";
				string ret;
				while (getline(cin, ret))
				{
					if (ret == "Y")break;
					cout << "未收到服务器的响应,登录失败,请输入Y返回首页:";
				}
				continue;
			}
			if (recv_buf[0] == 'Y')   //登录成功
			{
				system("cls");
				ShellExecute(NULL, _T("open"), _T("receiver.exe"), NULL, NULL, SW_SHOW);     //运行显示器程序
			}
			else if (recv_buf[0] == '0')
			{
				cout << "未注册用户名,登录失败,请输入Y返回首页:";
				string ret;
				while (getline(cin, ret))
				{
					if (ret == "Y")break;
					cout << "未注册用户名,登录失败,请输入Y返回首页:";
				}
				continue;
			}
			else if (recv_buf[0] == '1')
			{
				cout << "密码错误,登录失败,请输入Y返回首页:" << endl;
				string ret;
				while (getline(cin, ret))
				{
					if (ret == "Y")break;
					cout << "密码错误,登录失败,请输入Y返回首页:";
				}
				continue;
			}
			else if (recv_buf[0] == '2')
			{
				cout << "重复登录,登录失败,请输入Y返回首页:" << endl;
				string ret;
				while (getline(cin, ret))
				{
					if (ret == "Y")break;
					cout << "重复登录,登录失败,请输入Y返回首页:";
				}
				continue;
			}

			//选择聊天方式
			while (true)
			{
				system("cls");
				cout << "---------------------------------------------------" << endl;
				cout << "                 用户名:" << username << endl << endl;;
				cout << "            1.私聊  2.群聊  3.退出登录             " << endl << endl;
				cout << "---------------------------------------------------" << endl;
				string mode;
				getline(cin, mode);
				if (mode == "1")    //私聊
				{
					system("cls");
					cout << "私聊模式中,输入return返回上一级" << endl << endl;
					if (!Getonlinelist())    continue;                           //获取好友在线列表失败
					cout << "请选择私聊对象的序号" << endl;
					string choose;
					getline(cin, choose);
					if (choose == "return") continue;
					int i = 0;
					for (i = 0; i < choose.size(); i++)
						if (choose[i] > '9' || choose[i] < '0')break;
					if (i < choose.size()) continue;	
					stringstream stream(choose);
					int index = 0;
					stream >> index;
					if (index<0 || index>=onlinelist.size()) continue;
					while (true)                     //向该用户循环发送消息,直到输入return退出
					{
						system("cls");
						cout << "正在和" << onlinelist[index] << "私聊中" << ",输入return返回上一级" << endl << endl;
						string message;
						getline(cin, message);
						if (message == "return")
						{
							system("cls");
							break;
						}
						message = "PM" + onlinelist[index] + "#" + message;
						char *buf = new char[message.length() + 1];
						strcpy(buf, message.c_str());
						iSend = sendto(sClient, buf, message.length() + 1, 0, (struct sockaddr*)&ser, ser_length);
						if (iSend == SOCKET_ERROR)
						{
							cout << "sendto()Failed:" << WSAGetLastError() << endl;
							break;
						}
						delete[]buf;
						iRecv = recvfrom(sClient, recv_buf, sizeof(recv_buf), 0, (SOCKADDR*)&communication, &communication_length);
						if (recv_buf[0] == 'Y') continue;
						else
						{
							cout << onlinelist[index] << "已下线" << "输入Y返回主菜单";
							string ret;
							while (getline(cin, ret))
							{
								if (ret == "Y") break;
								cout << onlinelist[index] << "已下线" << "输入Y返回主菜单";
							}
							break;
						}
					}
				}
				else if (mode == "2")    //群聊
				{
					system("cls");
					while (true)
					{
						system("cls");
						cout << "群聊模式,输入return返回上一级" << endl << endl;
						string message;
						getline(cin, message);
						if (message == "return")
						{
							system("cls");
							break;
						}

						message = "G" + message;
						char *buf = new char[message.length() + 1];
						strcpy(buf, message.c_str());
						iSend = sendto(sClient, buf, message.length() + 1, 0, (struct sockaddr*)&ser, ser_length);
						delete[]buf;
						if (iSend == SOCKET_ERROR)
						{
							cout << "sendto()Failed:" << WSAGetLastError() << endl;
							break;
						}
					}
					continue;
				}
				else if (mode == "3")                             //退出登录
				{
					char buf[] = "exit";
					iSend = sendto(sClient, buf, sizeof(buf), 0, (struct sockaddr*)&ser, ser_length);
					break;
				}
				else
					continue;
			}
		}
		else if (choice == "2")
		{
			system("cls");
			cout << "请设置用户名:";
			string username;
			getline(cin, username);
			cout << "请设置登录密码:";
			string password;
			getline(cin, password);
			string init_infortemp = "R" + username + "#" + password;
			char *init_infor = new char[init_infortemp.length() + 1];
			strcpy(init_infor, init_infortemp.c_str());
			//向服务器发送注册用户信息
			iSend = sendto(sClient, init_infor, init_infortemp.length() + 1, 0, (struct sockaddr*)&ser, ser_length);
			//接收服务器回应的消息
			iRecv = recvfrom(sClient, recv_buf, sizeof(recv_buf), 0, (struct sockaddr*)&communication, &communication_length);
			if (recv_buf[0] == 'Y')
			{
				cout << "注册成功" << endl;
				continue;
			}
			else
			{
				cout << "用户名已存在,注册失败,请输入Y返回首页:" << endl;
				string ret;
				while (getline(cin, ret))
				{
					if (ret == "Y")break;
					cout << "用户名已存在,注册失败,请输入Y返回首页:";
				}
				continue;
			}
		}
		else if (choice == "3")
		{
			closesocket(sClient);
			WSACleanup;
			return;
		}
		else
			continue;
	}
}

int main()
{
	client x;
	if (x.Startup() == false)
		return 0;
	x.SetServerAddress();
	x.work();
}

客户端接收器的完整代码

#include<WinSock2.h>
#include<iostream>
#include<fstream>
#include<string>
#include<ctime>
#pragma comment(lib,"ws2_32.lib")
using namespace std;
#define DEFAULT_SPORT 5055
#define DEFAULT_CPORT 5056
#define BUFFER_LENGTH 1024

void main()
{
	WSADATA wsaData;
	if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
	{
		cout << "Failed to load Winsock." << endl;
		return;
	}
	//建立显示器端地址
	struct sockaddr_in receiver;
	//读取分配好的端口
	ifstream in("port.txt");
	string receiver_port;
	string username;
	getline(in, receiver_port);
	getline(in, username);
	in.close();
	remove("port.txt");
	int receiver_port_int = atoi(receiver_port.c_str());

	receiver.sin_family = AF_INET;
	receiver.sin_port = htons(receiver_port_int);    //htons()函数把一个双字节主机字节顺序的数转换为网络字节顺序的数
	receiver.sin_addr.s_addr = htonl(INADDR_ANY);    //htonl()函数把一个主机字节顺序的数转换为网络字节顺序的数   
	SOCKET rSocket = socket(AF_INET, SOCK_DGRAM, 0);
	if (rSocket == INVALID_SOCKET)
	{
		cout << "socket()Failed:" << WSAGetLastError() << endl;
		return;
	}
	if (bind(rSocket, (LPSOCKADDR)&receiver, sizeof(receiver)) == SOCKET_ERROR)
	{
		cout << "bind()Failed:" << WSAGetLastError() << endl;
		return;
	}

	char recv_buf[BUFFER_LENGTH];                  //接收数据的缓冲区
	memset(recv_buf, 0, sizeof(recv_buf));         //初始化接收缓冲区

	struct sockaddr_in ser;                        //客户端地址
	int ser_length = sizeof(ser);                  //客户端地址长度


	cout << "----------------------------------------" << endl << endl;
	cout << "           显示器---" << username << endl << endl << endl;
	cout << "----------------------------------------" << endl << endl;

	while (true)                                   //进入一个无限循环,进行数据接收和发送
	{
		int iRecv = recvfrom(rSocket, recv_buf, BUFFER_LENGTH, 0, (SOCKADDR*)&ser, &ser_length);
		string transmessage(recv_buf);
		if (iRecv == SOCKET_ERROR)
		{
			cout << "recvfrom()Failed:" << WSAGetLastError() << endl;
			break;
		}
		else if (transmessage == "exit") break;
		else
			cout << transmessage << endl;
	}
	closesocket(rSocket);
	WSACleanup();
}


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/128130.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SQL INNER JOIN:内连接

INNER JOIN 是 SQL 中最重要、最常用的表连接形式&#xff0c;只有当连接的两个或者多个表中都存在满足条件的记录时&#xff0c;才返回行。 SQL INNER JOIN 子句将 table1 和 table2 中的每一条记录进行比较&#xff0c;以找到满足条件的所有记录&#xff0c;然后将每一对满足…

YGG 公会发展计划(GAP)第二季总结

Yield Guild Games&#xff08;YGG&#xff09;正在结束其公会发展计划&#xff08;GAP&#xff09;的第二季&#xff0c;这是一个成就驱动的社区代币分配协议&#xff0c;奖励 YGG 成员通过优质贡献为公会提供价值。 第二季的 GAP 包括 117 个任务式的「成就」&#xff0c;为了…

有向图的概念和java代码实现

有向图定义 有向图是一副具有方向性的图&#xff0c;是由一组顶点和一组有方向的边组成的&#xff0c;每条方向的边都连着一对有序的顶点。 出度&#xff1a; 由某个顶点指出的边的个数称为该顶点的出度。 入度&#xff1a; 指向某个顶点的边的个数称为该顶点的入度。 有向路径…

重学redux之Redux Toolkit(四)

更新第四篇了,这篇是最终篇,没有基础的最好看看前几篇,篇幅有限,更多的是一个指导,如果有不清楚的地方,可评论区留言(最好自己找度娘问问),引用官方说话,先来了解一下 redux toolkit 是什么东西 Redux Toolkit 是什么? Redux Toolkit 是 Redux 官方强烈推荐,开箱…

用JavaScript实现网页雪花飘落特效

不知道大家有没有看到过别人的网页有雪花飘落的特效&#xff0c;我当时看到真的觉得好好看&#xff0c;于是乎就去借鉴别人做的特效代码&#xff0c;最终将这个特效成功放到自己做的网页上啦~代码放到下面啦&#xff0c;可以自己设置颜色&#xff0c;雪花形状的大小&#xff0c…

新政策|“信息科技”独立设置为新科目

近日&#xff0c;教育部印发义务教育课程方案和语文等16个课程标准(2022年版)。今天&#xff0c;教育部召开新闻发布会&#xff0c;介绍《义务教育课程方案和课程标准&#xff08;2022年版&#xff09;》。 教育部教材局局长田慧生表示&#xff0c;现行义务教育课程方案和课程…

Eth 02 -MAC如何访问PHY

文章目录 1 MII接口概述2 MDIO clause 223 MDIO Clause 45传送门 ==>> AutoSAR入门和实战系列总目录 1 MII接口概述 我们在【Eth 01 - Eth以太网控制器驱动概述和API讲解】已经提到了两个接口:MAC通过MII接口访问PHY芯片。 void Eth_WriteMii(uint8 CtrlIdx, uint8 T…

第二十三讲:神州路由器OSPF多区域路由的配置

实验拓扑图如下所示 操作步骤&#xff1a; 步骤1&#xff1a;连接网络拓扑图。 步骤2&#xff1a;设置计算机的IP地址、子网掩码和网关。 步骤3&#xff1a;配置Router-A的名称及其接口IP地址。 Router_config#hostname Router-A Router-A_config#int fa0/0 Router-A_con…

【HTML】再见2022!一起来写一个响应式跨年倒计时吧(附源码)

&#x1f482;作者简介&#xff1a; THUNDER王&#xff0c;一名热爱财税和SAP ABAP编程以及热爱分享的博主。目前于江西师范大学会计学专业大二本科在读&#xff0c;同时任汉硕云&#xff08;广东&#xff09;科技有限公司ABAP开发顾问。在学习工作中&#xff0c;我通常使用偏后…

excel排序求和:如何统计前几名数据合计 上篇

什么叫做统计前几名合计呢&#xff1f;先看看动画演示吧&#xff1a; 可以看到&#xff0c;数据按照销售数量从高往低排列&#xff0c;选择一个名次&#xff0c;就可以对前几名的销售数量求和。 这类问题在很多与数字排名有关的情况下都用得上&#xff0c;例如对前几名考试成绩…

GitFlow工作流

1.背景介绍 什么是Git工作流&#xff1f; Git工作流你可以理解为工作中团队成员遵守的一种代码管理方案&#xff0c;在Git中有以下几种工作流方案作为方案指导。 1、集中式工作流 2、功能分支工作流 3、Gitflow工作流 4、Forking工作流 2.知识剖析 1、集中式工作流 这…

ANR系列之五:Service类型ANR原理讲解

前言&#xff1a; ANR系列文章一共有有若干篇&#xff0c; 遵循这样的一个顺序&#xff1a; 1.先讲ANR的基本概念以及发生后的流程&#xff1b; 2.四种类型的ANR是如何发生的&#xff1b; 3.该如何排查和解决ANR类型问题。 想看整个系列的文章&#xff0c;可以参考该系列…

在线文档查看器:Gleamtech Document Viewer 6.6.1

DocumentUltimate Document Viewer and Converter for ASP.NET Core, MVC 和 WebForms 查看几乎任何文档类型&#xff08;70 多种文件格式&#xff0c;包括 PDF 和 Microsoft Office&#xff09;。 HTML5 零足迹查看器。在文档类型之间转换。 特征 适用于桌面和移动浏览器的通…

【XR】为挑战性环境优化6DoF控制器追踪

Oculus分享&#xff1a;为挑战性环境优化6DoF控制器追踪 尤其是针对具有挑战性的追踪环境 映维网曾在九月和十一月分享了关于Oculus Constellation追踪系统的相关细节&#xff0c;其中负责AR/VR设备输入追踪的Facebook工程经理安德鲁梅利姆&#xff08;Andrew Melim&#xff…

舆情监测平台都有哪些,舆情监测平台使用工作总结

舆情监测平台(public opinion monitoring platform)是一种用于监测和分析网络上的舆论动态的工具。这些平台通常通过爬取网络上的新闻、博客、论坛、社交媒体等信息来收集数据&#xff0c;并使用自然语言处理技术和数据挖掘技术来分析数据。舆情监测平台常常被用于政府、企业、…

牛客竞赛每日俩题 - Day12

目录 set的插入删除 vector<string>的应用 set的插入删除 数据库连接池__牛客网 [解题思路] 循环接收每组用例&#xff0c;对于每组用例进行如下操作&#xff1a; 1. 依次获取每个状态&#xff0c;如果该状态是"connect"&#xff0c;则将其id插入到set中&…

Adaboost模型的python实现

文章目录介绍Adaboost库参数介绍实例二分类问题多分类问题作者&#xff1a;李雪茸介绍 Adaboost算法是一种集成学习(ensemble learning)方法。在集成学习中&#xff0c;强学习器指的是由多个机器学习模型组合形成的精度更高的模型。而参与组合的模型就被称为是弱学习器。进行预…

数据治理:数据治理之道-数据战略

参考《一本书讲透数据治理》、《数据治理》等 文章目录数据战略定义DAMA对数据战略的定义DCMM对数据战略的定义《一本书讲透数据治理》对数据战略理解数据战略与企业战略、数据架构的关系数据战略的3个要素战略定位短期目标中期目标长期目标实施策略行动计划实施数据战略的5个步…

NFT存储使用NFTUp上传(NFT.Storage)

文章目录NFT存储使用NFTUp上传(NFT.Storage)NFT.StorageNFTUp安装和使用NFT存储使用NFTUp上传(NFT.Storage) NFT.Storage 官网&#xff1a;https://nft.storage/ NFT.Storage&#xff0c;它可以让用户免费使用IPFS与Filecoin存储NFT及元数据内容。 NFT.Storage&#xff0c;…

信息系统业务安全服务资质

信息系统业务安全服务能力评定是指依据《信息化建设企业信息系统业务安全服务能力评定标准》&#xff0c;对信息化建设企业信息系统业务安全服务能力的符合性评价&#xff0c;包括综合条件、财务状况、业绩要求、管理能力、技术实力、人才保障六个方面。通过评定&#xff0c;可…