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

这篇具有很好参考价值的文章主要介绍了Winsock套接字开发网络聊天室实例(C/S)模式。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

聊天室的基本要求

聊天器采用客户端/服务器(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架构实现多用户的聊天室程序。,TCP/IP,网络,服务器,udp

客户端的运行界面

基于c/s架构实现多用户的聊天室程序。,TCP/IP,网络,服务器,udp

用户上线提醒

基于c/s架构实现多用户的聊天室程序。,TCP/IP,网络,服务器,udp

用户注册提醒

基于c/s架构实现多用户的聊天室程序。,TCP/IP,网络,服务器,udp

 私聊功能的实现

基于c/s架构实现多用户的聊天室程序。,TCP/IP,网络,服务器,udp

 基于c/s架构实现多用户的聊天室程序。,TCP/IP,网络,服务器,udp

群聊功能的实现

基于c/s架构实现多用户的聊天室程序。,TCP/IP,网络,服务器,udp

 聊天室的整体设计 

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

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

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

基于c/s架构实现多用户的聊天室程序。,TCP/IP,网络,服务器,udp

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

基于c/s架构实现多用户的聊天室程序。,TCP/IP,网络,服务器,udp

 服务器要处理的五种消息类型对应着聊天器所要实现的五种功能,即登录功能,注册功能,群聊功能,私聊功能,关闭程序。下文将针对聊天器所要实现的功能给出设计的方案与实现。文章来源地址https://www.toymoban.com/news/detail-761162.html

 聊天室服务器完整代码

#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();
}

 

到了这里,关于Winsock套接字开发网络聊天室实例(C/S)模式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • lv7 嵌入式开发-网络编程开发 13 UNIX域套接字

    目录 1 UNIX 域流式套接字 2 UNIX 域数据报套接字 UNIX 域流式套接字(UNIX domain stream socket)是一种在同一台主机上的进程之间进行通信的机制。它不依赖于网络协议栈,而是使用文件系统作为通信的基础。 UNIX 域流式套接字提供可靠的、双向的、面向连接的通信方式。与传统的

    2024年02月07日
    浏览(49)
  • RK3568平台开发系列讲解(网络篇)Linux 的 socket 套接字

    🚀返回专栏总目录 沉淀、分享、成长,让自己和他人都能有所收获!😄 📢 Linux 内核支持的套接字如下: 我们创建套接字时,可以通过参数选择协议族,为应用程序指定不同的网络

    2024年02月02日
    浏览(55)
  • 网络编程套接字 | UDP套接字

    前面的文章中我们叙述了网络编程套接字的一些预备知识点,从本文开始我们就将开始UDP套接字的编写。本文中的服务端与客户端都是在阿里云的云服务器进行编写与测试的。 在v1的版本中我们先来使用一下前面讲过得一些接口,简单的构建一个udp服务器: 然后运行上述的程

    2024年02月09日
    浏览(100)
  • 套接字通信(附带单线程TCP套接字通信代码)

    1. 概念 1.1 局域网和广域网 局域网(LAN)和广域网(WAN)是两种不同范围的计算机网络,它们用于连接多台计算机以实现数据共享和通信。 局域网(LAN): 定义: 局域网是一个较小范围内的网络,通常限定在某个地理区域,比如一个办公室、学校或者家庭。 范围: LAN 的范

    2024年01月21日
    浏览(47)
  • 网络编程之 Socket 套接字(使用数据报套接字和流套接字分别实现一个小程序(附源码))

    网络编程是指网络上的主机,通过不同的进程,以编程的方式实现 网络通信(或称为网络数据传输) 只要满足不同的进程就可以进行通信,所以即便是在同一个主机,只要不同的进程,基于网络传输数据,也属于网络编程 在一次网络传输中: 发送端: 数据的 发送方进程

    2024年02月03日
    浏览(63)
  • 【JavaEE】网络编程之TCP套接字、UDP套接字

    目录 1.网络编程的基本概念 1.1为什么需要网络编程  1.2服务端与用户端 1.3网络编程五元组  1.4套接字的概念 2.UDP套接字编程 2.1UDP套接字的特点  2.2UDP套接字API 2.2.1DatagramSocket类 2.2.2DatagramPacket类  2.2.3基于UDP的回显程序 2.2.4基于UDP的单词查询  3.TCP套接字编程 3.1TCP套接字的特

    2023年04月20日
    浏览(75)
  • 【JaveEE】网络编程之TCP套接字、UDP套接字

    目录 1.网络编程的基本概念 1.1为什么需要网络编程  1.2服务端与用户端 1.3网络编程五元组  1.4套接字的概念 2.UDP套接字编程 2.1UDP套接字的特点  2.2UDP套接字API 2.2.1DatagramSocket类 2.2.2DatagramPacket类  2.2.3基于UDP的回显程序 2.2.4基于UDP的单词查询  3.TCP套接字编程 3.1TCP套接字的特

    2023年04月13日
    浏览(169)
  • 网络编程套接字(3)——Java数据报套接字(UDP协议)

    目录 一、Java数据报套接字通信模型 二、UDP数据报套接字编程 1、DatagramSocket         (1)DatagramSocket构造方法         (2)DatagramSocket方法 2、DatagramPacket         (1)DatagramPacket构造方法         (2)DatagramPacket方法 3、InetSocketAddress 三、代码示例:回显服务

    2024年03月12日
    浏览(99)
  • 【C/C++套接字编程】套接字的基本概念与基础语法

    TCP/UDP实验为牵引,学习套接字编程的相关知识,再进一步深化对TCP/UDP的理解 目录 前言 Socket编程语法 1. 套接字及创建 什么是套接字? 创建套接字 2. 端口绑定 3. 收发信息 与recv()函数的比较: 与send()函数的比较: 编程实例  总结 系列博客 【C/C++套接字编程】TCP协议通信的

    2024年02月09日
    浏览(47)
  • Python进阶篇(三)-- TCP套接字与UDP套接字编程

    1.1 介绍         本文将首先利用 Python 实现面向TCP连接的套接字编程基础知识:如何创建套接字,将其绑定到特定的地址和端口,以及发送和接收数据包。其次还将学习 HTTP 协议格式的相关知识。在此基础上,本篇将用 Python 语言开发一个简单的 Web 服务器,它仅能处理一

    2023年04月23日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包