基于udp的多人聊天室

服务端

相当于一个服务器,接收用户发送的过来的消息(登录消息,文本消息,退出登录消息),然后将其转发给其用户。

基本功能:

    1.把新注册用户登陆消息告诉其它用户

    2.把新用户插入到用户链表中

    3.服务器打印一下

    4.把聊天信息发给其它用户

    5.发给自己一份

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
typedef struct node_t
{
	struct sockaddr_in caddr; //数据域
	struct node_t *next;	  //指针域,指向自身结构体的指针
} link_node_t, *link_list_t;
//1.创建一个空的单向链表(有头单向链表)
link_node_t *CreateEpLinkList();
//2.向单向链表插入数据
void InsertIntoPostLinkList(link_node_t *p, struct sockaddr_in caddr,struct sockaddr_in clientaddr);
//3.遍历单向链表
void ShowLinkList(link_node_t *p, struct sockaddr_in caddr,  int sockfd,struct sockaddr_in clientaddr);
//4.删除单向链表中出现的指定数据
void DeleteDataLinkList(link_node_t *p, struct sockaddr_in caddr,  int sockfd,struct sockaddr_in clientaddr);
struct mychat
{
	char type;
	char name[32];
	char message[32];
} chat;
int main(int argc, const char *argv[])
{
	int sockfd;
	pid_t pid;
	struct sockaddr_in serveraddr, clientaddr;
	socklen_t len = sizeof(clientaddr);
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (sockfd < 0)
	{
		perror("socket err");
		return -1;
	}
	//printf("sockfd ok\n");
	if (argc != 2)
	{
		printf("Usage:%s <port>\n", argv[0]);可执行文件+端口号
		return -1;
	}
	//填充结构体
	serveraddr.sin_family = AF_INET;
	serveraddr.sin_port = htons(atoi(argv[1]));
	//addr.sin_addr.s_addr = inet_addr(argv[1]);
	//自动获取主机ip
	//addr.sin_addr.s_addr = inet_addr("0.0.0.0");
	serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
	//绑定
	if (bind(sockfd, (struct sockaddr *)&serveraddr, len) < 0)
	{
		perror("bind err\n");
		return -1;
	}
	//printf("bind ok\n");
	printf("Server starts success\n");
	//创建父子进程
	pid = fork();
	if (pid == -1)
	{
		perror("fork error");
		return -1;
	}
	else if (pid == 0)
	{
		int recvbyte;
		link_node_t *p = CreateEpLinkList();
		while (1)
		{
			if ((recvbyte = recvfrom(sockfd, &chat, sizeof(chat), 0, (struct sockaddr *)&clientaddr, &len)) < 0)
			{
				perror("recvfrom err");
				return -1;
			}
			if (chat.type == 'L')//'L'表示用户登录
			{
				if (chat.name[strlen(chat.name) - 1] == '\n')
					chat.name[strlen(chat.name) - 1] = '\0';
				printf("connect ip is %s\n", inet_ntoa(clientaddr.sin_addr));
				printf("%s login\n", chat.name);
				InsertIntoPostLinkList(p, clientaddr,clientaddr);
				ShowLinkList(p, clientaddr,  sockfd,clientaddr);
			}
			else if (chat.type == 'B')//'B'聊天
			{
				printf("connect ip is %s\n", inet_ntoa(clientaddr.sin_addr));
				printf("%s said:%s\n", chat.name, chat.message);
				ShowLinkList(p, clientaddr,  sockfd,clientaddr);
			}
			else if (chat.type == 'Q')//'Q'用户退出
			{
				printf("%s offline\n", chat.name);
				DeleteDataLinkList(p, clientaddr,  sockfd,clientaddr);
				ShowLinkList(p, clientaddr,  sockfd,clientaddr);
			}
		}
	}
	else
	{
		while (1)
		{
			strcpy(chat.name, "server");
			chat.type = 'B';
			fgets(chat.message, 128, stdin);
			if (chat.message[strlen(chat.message) - 1] == '\n')
				chat.message[strlen(chat.message) - 1] = '\0';
			sendto(sockfd, &chat, sizeof(chat), 0, (struct sockaddr *)&serveraddr, sizeof(serveraddr));//将内容发送到子进程
		};
	}
	close(sockfd);
	return 0;
}
//1.创建一个空的单向链表(有头单向链表)
link_node_t *CreateEpLinkList()
{
	link_list_t p = (link_list_t)malloc(sizeof(link_node_t));
	if (NULL == p)
	{
		printf("creat error\n");
		return NULL;
	}
	p->next = NULL;
	return p;
}
//2.向单向链表插入数据
void InsertIntoPostLinkList(link_node_t *p, struct sockaddr_in caddr,struct sockaddr_in clientaddr)
{
	link_list_t pnew = (link_list_t)malloc(sizeof(link_node_t));
	while (p->next != NULL)
	{
		p = p->next;
	}
	pnew->caddr = clientaddr;
	pnew->next = NULL;
	p->next = pnew;
}
//3.遍历单向链表
void ShowLinkList(link_node_t *p, struct sockaddr_in caddr,  int sockfd,struct sockaddr_in clientaddr)
{
	p = p->next;
	while (p != NULL)
	{
		if (((p->caddr.sin_addr.s_addr) != (clientaddr.sin_addr.s_addr)) || ((p->caddr.sin_port) != (clientaddr.sin_port))) //不给自己发消息
		{
			sendto(sockfd, &chat, sizeof(chat), 0, (struct sockaddr *)&(p->caddr), sizeof(p->caddr));
		}
		p = p->next;
	}
}
//4.删除单向链表中出现的指定数据
void DeleteDataLinkList(link_node_t *p, struct sockaddr_in caddr,  int sockfd,struct sockaddr_in clientaddr)
{
	link_node_t *pdel = NULL;
	while (p->next != NULL)
	{
		if ((p->next->caddr.sin_addr.s_addr == clientaddr.sin_addr.s_addr) && (p->next->caddr.sin_port == clientaddr.sin_port)) //说明需要删除q指向的节点
		{
			pdel = p->next;
			p->next = pdel->next;
			free(pdel);
			pdel = NULL;
		}
		else
		{
			sendto(sockfd, &chat, sizeof(chat), 0, (struct sockaddr *)&p->caddr, sizeof(p->caddr));
			p = p->next;
		}
	}
}

客户端

该部分会发送消息和接受服务器转发过来其他人发送的消息登录消息,文本消息,退出登录消息)。

基本功能:

       1. 输入用户名,并发给服务器登录信息

       2.将文本消息发送给服务器

       3.接收服务器转发的其他用户消息

#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <sys/wait.h>
struct mychat
{
	char type;
	char name[32];
	char message[32];
} chat;
int main(int argc, const char *argv[])
{
	int sockfd;
	pid_t pid;
	struct sockaddr_in addr;
	socklen_t len;
	sockfd = socket(AF_INET, SOCK_DGRAM, 0);
	if (sockfd < 0)
	{
		perror("socket err");
		return -1;
	}
	if (argc != 3)
	{
		printf("Usage:%s <ip> <port>\n", argv[0]);//可执行文件+ip+端口号
		return -1;
	}
	//填充结构体
	addr.sin_family = AF_INET;
	addr.sin_port = htons(atoi(argv[2]));
	addr.sin_addr.s_addr = inet_addr(argv[1]);
	len = sizeof(struct sockaddr_in);
	printf("name:");
	fgets(chat.name, sizeof(chat.name), stdin);
	if (chat.name[strlen(chat.name) - 1] == '\n')
		chat.name[strlen(chat.name) - 1] = '\0';
	chat.type = 'L';
	sendto(sockfd, &chat, sizeof(chat), 0, (struct sockaddr *)&addr, len);
	pid = fork();
	if (pid == -1)
	{
		perror("fork error");
		return -1;
	}
	else if (pid == 0)
	{
		while (1)
		{
			printf("input:");
			fgets(chat.message, 128, stdin);
			if (chat.message[strlen(chat.message) - 1] == '\n')
				chat.message[strlen(chat.message) - 1] = '\0';
			if (strncmp(chat.message, "quit", 4) == 0)
			{
				chat.type = 'Q';
				sendto(sockfd, &chat, sizeof(chat), 0, (struct sockaddr *)&addr, len);
				kill(getppid(), SIGKILL);
				exit(-1);
			}
			else
			{
				chat.type = 'B';
				sendto(sockfd, &chat, sizeof(chat), 0, (struct sockaddr *)&addr, len);
			}
		}
	}
	else
	{
		int recvbyte;
		while (1)
		{
			if ((recvbyte = recvfrom(sockfd, &chat, sizeof(chat), 0, NULL, NULL)) < 0)
			{
				perror("recvfrom err");
				return -1;
			}
			if (chat.type == 'L')
			{
				printf("%s login\n", chat.name);
			}
			else if (chat.type == 'B')
				printf("%s said:%s\n", chat.name, chat.message);
			else if (strncmp(chat.message, "quit", 4) == 0)
			{
				printf("%s offline\n", chat.name);
			}
		}
		wait(NULL);
	}
	close(sockfd);
	return 0;
}

改了几次了,可能还有没改到位的地方,欢迎指正。

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值