15.基于UDP协议的聊天室程序

使用UDP协议完成一个聊天室程序的小项目,大部分代码都有注释,一看就能看到的.


实现的功能:

              (1)查看/显示已经登陆的用户信息

              (2)向已登陆的用户发送消息

              (3)输出错误消息,给予提示

              (4)退出


共有三个文件:

chat_public.h


#ifndef _CHAT_PUB_H_
#define _CHAT_PUB_H_

//chat_public.h 

#include <list>
#include <algorithm>
using namespace std;


// C2S  客户端到服务器的
#define C2S_LOGIN			0x01
#define C2S_LOGOUT			0x02
#define C2S_ONLINE_USER		0x03

#define MSG_LEN				512


// S2C  服务器到客户端的
#define S2C_LOGIN_OK		0x01
#define S2C_ALREADY_LOGINED	0x02
#define S2C_SOMEONE_LOGIN	0x03
#define S2C_SOMEONE_LOGOUT	0x04
#define S2C_ONLINE_USER		0x05

// C2C  客户端到客户端的

#define C2C_CHAT			0x06

typedef struct message  // 传递的消息结构
{
	int cmd;
	char body[MSG_LEN];  // 消息的内容
} MESSAGE;

typedef struct user_info // 用户信息结构
{
	char username[16];  // 用户名
	unsigned int ip;    // IP地址
	unsigned short port;  //端口
} USER_INFO;

typedef struct chat_msg  //客户与客户之间的消息结构
{
	char username[16];
	char msg[100];
}CHAT_MSG;

typedef list<USER_INFO> USER_LIST;  // 用list<USER_INFO> 容器

#endif /* _PUB_H_ */


chatser.cpp


#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <sys/wait.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

#include "chat_public.h"

#define ERR_EXIT(m) \
        do \
        { \
                perror(m); \
                exit(EXIT_FAILURE); \
        } while(0)

// 聊天室成员列表
USER_LIST client_list;

void do_login(MESSAGE& msg, int sock, struct sockaddr_in *cliaddr);
void do_logout(MESSAGE& msg, int sock, struct sockaddr_in *cliaddr);
void do_sendlist(int sock, struct sockaddr_in *cliaddr);

void chat_srv(int sock)
{
	struct sockaddr_in cliaddr;
	socklen_t clilen;
	int n;
	MESSAGE msg;
	while (1)
	{
		memset(&msg, 0, sizeof(msg));
		clilen = sizeof(cliaddr);
		n = recvfrom(sock, &msg, sizeof(msg), 0, (struct sockaddr *)&cliaddr, &clilen);  // 接收客户到服务器的消息  
		if (n < 0)
		{
			if (errno == EINTR)
				continue;
			ERR_EXIT("recvfrom");
		}

		int cmd = ntohl(msg.cmd); // 将命令转换成主机字节序
		switch (cmd)// 判断是是什么消息
		{
		case C2S_LOGIN:  // 登陆
			do_login(msg, sock, &cliaddr);
			break;
		case C2S_LOGOUT:  // 退出
			do_logout(msg, sock, &cliaddr);
			break;
		case C2S_ONLINE_USER:  // 点对点的客户连接
			do_sendlist(sock, &cliaddr);
			break;
		default:
			break;
		}
	}
}


// 登陆函数 
void do_login(MESSAGE& msg, int sock, struct sockaddr_in *cliaddr)
{
	USER_INFO user;  // 定义用户结构体变量 
	strcpy(user.username, msg.body); // 取出用户名 
	user.ip = cliaddr->sin_addr.s_addr; // ip地址
	user.port = cliaddr->sin_port; // 端口号 
	
	// 查找当前登陆正在的用户的用户名是否在当前的用户列表中 (防止重名)
	USER_LIST::iterator it; // 定义迭代器
	for (it=client_list.begin(); it != client_list.end(); ++it)
	{
		if (strcmp(it->username,msg.body) == 0)
		{
			break;  // 如果找到就退出循环  
		}
	}

	if (it == client_list.end()) //遍历到结尾,没有找到
	{
		printf("has a user login : %s <-> %s:%d\n", msg.body, inet_ntoa(cliaddr->sin_addr), ntohs(cliaddr->sin_port));
		client_list.push_back(user);

		// 登录成功应答
		MESSAGE reply_msg;
		memset(&reply_msg, 0, sizeof(reply_msg));
		reply_msg.cmd = htonl(S2C_LOGIN_OK);
		sendto(sock, &reply_msg, sizeof(msg), 0, (struct sockaddr *)cliaddr, sizeof(struct sockaddr_in));

		int count = htonl((int)client_list.size());
		// 发送在线人数
		sendto(sock, &count, sizeof(int), 0, (struct sockaddr *)cliaddr, sizeof(struct sockaddr_in));

		printf("sending user list information to: %s <-> %s:%d\n", msg.body, inet_ntoa(cliaddr->sin_addr), ntohs(cliaddr->sin_port));
		// 发送在线列表
		for (it=client_list.begin(); it != client_list.end(); ++it)
		{
			sendto(sock, &*it, sizeof(USER_INFO), 0, (struct sockaddr *)cliaddr, sizeof(struct sockaddr_in));
		}

		// 向其他用户通知有新用户登录
		for (it=client_list.begin(); it != client_list.end(); ++it)
		{
			if (strcmp(it->username,msg.body) == 0)
				continue;

			struct sockaddr_in peeraddr;
			memset(&peeraddr, 0, sizeof(peeraddr));
			peeraddr.sin_family = AF_INET;
			peeraddr.sin_port = it->port;
			peeraddr.sin_addr.s_addr = it->ip;

			msg.cmd = htonl(S2C_SOMEONE_LOGIN);
			memcpy(msg.body, &user, sizeof(user));

			if (sendto(sock, &msg, sizeof(msg), 0, (struct sockaddr *)&peeraddr, sizeof(peeraddr)) < 0)
				ERR_EXIT("sendto");

		}
	}
	else // 没有遍历到结尾, 找到用户 
	{
		printf("user %s has already logined\n", msg.body);

		MESSAGE reply_msg;
		memset(&reply_msg, 0, sizeof(reply_msg));
		reply_msg.cmd = htonl(S2C_ALREADY_LOGINED);
		sendto(sock, &reply_msg, sizeof(reply_msg), 0, (struct sockaddr *)cliaddr, sizeof(struct sockaddr_in));
	}
}

void do_logout(MESSAGE& msg, int sock, struct sockaddr_in *cliaddr)
{
	printf("has a user logout : %s <-> %s:%d\n",  msg.body, inet_ntoa(cliaddr->sin_addr), ntohs(cliaddr->sin_port));

	USER_LIST::iterator it;
	for (it=client_list.begin(); it != client_list.end(); ++it)
	{
		if (strcmp(it->username,msg.body) == 0)
			break;
	}

	if (it != client_list.end())
		client_list.erase(it);

	// 向其他用户通知有用户登出
	for (it=client_list.begin(); it != client_list.end(); ++it)
	{
		if (strcmp(it->username,msg.body) == 0)
			continue;

		struct sockaddr_in peeraddr;
		memset(&peeraddr, 0, sizeof(peeraddr));
		peeraddr.sin_family = AF_INET;
		peeraddr.sin_port = it->port;
		peeraddr.sin_addr.s_addr = it->ip;

		msg.cmd = htonl(S2C_SOMEONE_LOGOUT);

		if (sendto(sock, &msg, sizeof(msg), 0, (struct sockaddr *)&peeraddr, sizeof(peeraddr)) < 0)
			ERR_EXIT("sendto");

	}
}

void do_sendlist(int sock, struct sockaddr_in *cliaddr)
{
	MESSAGE msg;
	msg.cmd = htonl(S2C_ONLINE_USER);
	sendto(sock, (const char*)&msg, sizeof(msg), 0, (struct sockaddr *)cliaddr, sizeof(struct sockaddr_in));

	int count = htonl((int)client_list.size());
	/* 发送在线用户数 */
	sendto(sock, (const char*)&count, sizeof(int), 0, (struct sockaddr *)cliaddr, sizeof(struct sockaddr_in));
	/* 发送在线用户列表 */
	for (USER_LIST::iterator it=client_list.begin(); it != client_list.end(); ++it)
	{
		sendto(sock, &*it, sizeof(USER_INFO), 0, (struct sockaddr *)cliaddr, sizeof(struct sockaddr_in));
	}
}

int main(void)
{
	int sock;
	if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
		ERR_EXIT("socket");

	struct sockaddr_in servaddr;
	memset(&servaddr, 0, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(5188);
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);

	if (bind(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
		ERR_EXIT("bind");

	chat_srv(sock);
	return 0;
}



chatcli.cpp


#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>

#include "chat_public.h"

#define ERR_EXIT(m) \
        do \
        { \
                perror(m); \
                exit(EXIT_FAILURE); \
        } while(0)

// 当前用户名
char username[16];

// 聊天室成员列表
USER_LIST client_list;


void do_someone_login(MESSAGE& msg);
void do_someone_logout(MESSAGE& msg);
void do_getlist();
void do_chat();

void parse_cmd(char* cmdline, int sock, struct sockaddr_in *servaddr);
bool sendmsgto(int sock, char* username, char* msg);

void parse_cmd(char* cmdline, int sock, struct sockaddr_in *servaddr)
{
	char cmd[10]={0};
	char *p;
	p = strchr(cmdline, ' ');
	if (p != NULL)
		*p = '\0';

	strcpy(cmd, cmdline);
	
	if (strcmp(cmd, "exit") == 0) // 退出命令
	{
		MESSAGE msg;
		memset(&msg,0,sizeof(msg));
		msg.cmd = htonl(C2S_LOGOUT);
		strcpy(msg.body, username);

		if (sendto(sock, &msg, sizeof(msg), 0, (struct sockaddr *)servaddr, sizeof(struct sockaddr_in)) < 0)
			ERR_EXIT("sendto");

		printf("user %s has logout server\n", username);
		exit(EXIT_SUCCESS);

	}
	else if (strcmp(cmd, "send") == 0)  // send命令
	{
		char peername[16]={0};
		char msg[MSG_LEN]={0};

		// send  user  msg  
		while (*p++ == ' ') ;
		char *p2;
		//extern char *strchr(const char *s,char c);查找字符串s中首次出现字符c的位置。
		p2 = strchr(p, ' ');
		if (p2 == NULL)
		{
			printf("bad command\n");
			printf("\nCommands are:\n");
			printf("send username msg\n");
			printf("list\n");
			printf("exit\n");
			printf("\n");
			return;
		}
		*p2 = '\0';
		strcpy(peername, p);

		while (*p2++ == ' ') ;
		strcpy(msg, p2);
		sendmsgto(sock, peername, msg); // 向用户发送消息
	}
	else if (strcmp(cmd, "list") == 0)  // list命令
	{
		MESSAGE msg;
		memset(&msg, 0, sizeof(msg));
		msg.cmd = htonl(C2S_ONLINE_USER);

		if (sendto(sock, &msg, sizeof(msg), 0, (struct sockaddr *)servaddr, sizeof(struct sockaddr_in)) < 0)
			ERR_EXIT("sendto");
	}
	else
	{
		printf("bad command\n");
		printf("\nCommands are:\n");
		printf("send username msg\n");
		printf("list\n");
		printf("exit\n");
		printf("\n");
	}
}

bool sendmsgto(int sock, char* name, char* msg)
{
	if (strcmp(name, username) == 0) // 不能向自己发送消息
	{
		printf("can't send message to self\n");
		return false;
	}

	USER_LIST::iterator it;
	for (it=client_list.begin(); it != client_list.end(); ++it)
	{
		if (strcmp(it->username,name) == 0)
			break;
	}

	if (it == client_list.end())
	{
		printf("user %s has not logined server\n", name);
		return false;
	}

	MESSAGE m;
	memset(&m,0,sizeof(m));
	m.cmd = htonl(C2C_CHAT);

	CHAT_MSG cm;
	strcpy(cm.username, username);
	strcpy(cm.msg, msg);

	memcpy(m.body, &cm, sizeof(cm));
	//strcpy(m.body,msg);

	struct sockaddr_in	peeraddr;
	memset(&peeraddr,0,sizeof(peeraddr));
	peeraddr.sin_family      = AF_INET;
	peeraddr.sin_addr.s_addr = it->ip;
	peeraddr.sin_port        = it->port;

	in_addr tmp;
	tmp.s_addr = it->ip;

	printf("sending message [%s] to user [%s] <-> %s:%d\n",  msg, name, inet_ntoa(tmp), ntohs(it->port));

	sendto(sock, (const char*)&m, sizeof(m), 0, (struct sockaddr *)&peeraddr, sizeof(peeraddr));
	return true;
}

void do_getlist(int sock)
{
	int count;
	recvfrom(sock, &count, sizeof(int), 0, NULL, NULL);
	printf("has %d users logined server\n", ntohl(count));// 打印有在线用户人数
	client_list.clear(); // 清空当前用户列表

	int n = ntohl(count);
	for (int i=0; i<n; i++)// 一个个接收用户列表信息,插入到用户列表中
	{
		USER_INFO user;
		recvfrom(sock,&user, sizeof(USER_INFO), 0, NULL, NULL);
		client_list.push_back(user);
		in_addr tmp;
		tmp.s_addr = user.ip;

		printf("%s <-> %s:%d\n", user.username, inet_ntoa(tmp), ntohs(user.port));
	}
}

void do_someone_login(MESSAGE& msg)
{
	USER_INFO *user = (USER_INFO*)msg.body;
	in_addr tmp;
	tmp.s_addr = user->ip;
	printf("%s <-> %s:%d has logined server\n", user->username, inet_ntoa(tmp), ntohs(user->port));
	client_list.push_back(*user);
}

void do_someone_logout(MESSAGE& msg)
{
	USER_LIST::iterator it;
	for (it=client_list.begin(); it != client_list.end(); ++it)
	{
		if (strcmp(it->username,msg.body) == 0)
			break;
	}

	if (it != client_list.end())
		client_list.erase(it);  // 用户列表清除

	printf("user %s has logout server\n", msg.body);
}

void do_chat(const MESSAGE& msg)
{
	CHAT_MSG *cm = (CHAT_MSG*)msg.body;
	printf("recv a msg [%s] from [%s]\n", cm->msg, cm->username);
	//recvfrom(sock, &count, sizeof(int), 0, NULL, NULL);
}

void chat_cli(int sock)
{
	struct sockaddr_in servaddr;
	memset(&servaddr, 0, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(5188);
	servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");

	struct sockaddr_in peeraddr;
	socklen_t peerlen;

	MESSAGE msg;
	while (1)
	{
		memset(username,0,sizeof(username));
		printf("please inpt your name:");
		fflush(stdout);
		scanf("%s", username);

		
		memset(&msg, 0, sizeof(msg));
		msg.cmd = htonl(C2S_LOGIN);
		strcpy(msg.body, username);

		sendto(sock, &msg, sizeof(msg), 0, (struct sockaddr *)&servaddr, sizeof(servaddr));

		memset(&msg, 0, sizeof(msg));
		recvfrom(sock, &msg, sizeof(msg), 0, NULL, NULL);
		int cmd = ntohl(msg.cmd); // 转换成主机字节序
		if (cmd == S2C_ALREADY_LOGINED)
			printf("user %s already logined server, please use another username\n", username);
		else if (cmd == S2C_LOGIN_OK)
		{
			printf("user %s has logined server\n", username);
			break;
		}

	}
	int count;
	recvfrom(sock, &count, sizeof(int), 0, NULL, NULL);

	int n = ntohl(count);
	printf("has %d users logined server\n", n);


	for (int i=0; i<n; i++)
	{
		USER_INFO user;
		recvfrom(sock, &user, sizeof(USER_INFO), 0, NULL, NULL);
		client_list.push_back(user);
		in_addr tmp;
		tmp.s_addr = user.ip;

		printf("%d %s <-> %s:%d\n", i, user.username, inet_ntoa(tmp), ntohs(user.port));
	}


	printf("\nCommands are:\n");
	printf("send username msg\n");
	printf("list\n");
	printf("exit\n");
	printf("\n");

	fd_set rset;
	FD_ZERO(&rset);
	int nready;
	while (1)
	{
		FD_SET(STDIN_FILENO, &rset);
		FD_SET(sock, &rset);
		nready = select(sock+1, &rset, NULL, NULL, NULL);
		if (nready == -1)
				ERR_EXIT("select");

		if (nready == 0)
				continue;

		if (FD_ISSET(sock, &rset))
		{
			peerlen = sizeof(peeraddr);
			memset(&msg,0,sizeof(msg));
			recvfrom(sock, &msg, sizeof(msg), 0, (struct sockaddr *)&peeraddr, &peerlen);
			int cmd = ntohl(msg.cmd);
			switch (cmd)
			{
			case S2C_SOMEONE_LOGIN:
				do_someone_login(msg);
				break;
			case S2C_SOMEONE_LOGOUT:
				do_someone_logout(msg);
				break;
			case S2C_ONLINE_USER:  // 用户列表消息
				do_getlist(sock);
				break;
			case C2C_CHAT:  // 点对点聊天程序
				do_chat(msg);
				break;
		default:
			break;

			}
		}
		if (FD_ISSET(STDIN_FILENO, &rset))
		{
			char cmdline[100] = {0};
			if (fgets(cmdline, sizeof(cmdline), stdin) == NULL)
				break;

			if (cmdline[0] == '\n')
				continue;
			cmdline[strlen(cmdline) - 1] = '\0';
			parse_cmd(cmdline, sock, &servaddr);
		}
	}

	memset(&msg,0,sizeof(msg));
	msg.cmd = htonl(C2S_LOGOUT);
	strcpy(msg.body, username);

	sendto(sock, (const char*)&msg, sizeof(msg), 0, (struct sockaddr *)&servaddr, sizeof(servaddr));
	close(sock);

}


int main(void)
{
	int sock;
	if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
		ERR_EXIT("socket");

	chat_cli(sock);

	return 0;
}

图片展示:

服务器:

客户1:cjl

客户2:yzq



  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,这是一个简单的Java基于UDP协议实现的聊天室程序,可以供你参考: 服务端代码: ``` import java.net.*; import java.io.*; public class UDPServer { public static void main(String args[]) { DatagramSocket aSocket = null; try { aSocket = new DatagramSocket(6789); byte[] buffer = new byte[1000]; System.out.println("Server is running..."); while (true) { DatagramPacket request = new DatagramPacket(buffer, buffer.length); aSocket.receive(request); String requestMessage = new String(request.getData()).trim(); System.out.println("Client : " + requestMessage); String replyMessage = "Server : " + requestMessage; DatagramPacket reply = new DatagramPacket(replyMessage.getBytes(), replyMessage.length(), request.getAddress(), request.getPort()); aSocket.send(reply); } } catch (SocketException e) { System.out.println("Socket: " + e.getMessage()); } catch (IOException e) { System.out.println("IO: " + e.getMessage()); } finally { if (aSocket != null) aSocket.close(); } } } ``` 客户端代码: ``` import java.net.*; import java.io.*; public class UDPClient { public static void main(String args[]) { DatagramSocket aSocket = null; try { aSocket = new DatagramSocket(); InetAddress aHost = InetAddress.getByName("localhost"); int serverPort = 6789; BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); while (true) { System.out.print("Client : "); String requestMessage = br.readLine(); DatagramPacket request = new DatagramPacket(requestMessage.getBytes(), requestMessage.length(), aHost, serverPort); aSocket.send(request); byte[] buffer = new byte[1000]; DatagramPacket reply = new DatagramPacket(buffer, buffer.length); aSocket.receive(reply); String replyMessage = new String(reply.getData()).trim(); System.out.println(replyMessage); } } catch (SocketException e) { System.out.println("Socket: " + e.getMessage()); } catch (IOException e) { System.out.println("IO: " + e.getMessage()); } finally { if (aSocket != null) aSocket.close(); } } } ``` 运行服务端代码后,再运行多个客户端代码即可实现简单的聊天室功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值