网络聊天室项目

news/2024/5/18 13:38:21 标签: udp

 cli.c

#include"udp.h"
#define ERR_MSG(msg)  do{\
	fprintf(stderr, "__%d__:\n", __LINE__); \
	perror(msg);\
}while(0)

int main(int argc, const char *argv[])
{	

	pid_t pid=0;
	msg_t msg;//创建发送用户信息
	int cfd=socket(AF_INET,SOCK_DGRAM,0);
	if(cfd<0)
	{
		ERR_MSG("套接字创建失败");
		return -1;
	}
	struct sockaddr_in cin;
	memset(&cin,0,sizeof(cin));
	cin.sin_family =AF_INET;
	cin.sin_port =htons(3696);
	cin.sin_addr.s_addr=inet_addr("192.168.114.146");
	socklen_t caddr_len=sizeof(cin);

	//输入用户的姓名开始操作
	msg.ch='D';
	printf("请输入你的网名QVQ >>>>");
	fgets(msg.name,sizeof(msg.name),stdin);
	msg.name[strlen(msg.name)-1]='\0';
	//给服务器发送用户已经登录的操作
	sendto(cfd,&msg,sizeof(msg),0,(struct sockaddr*)&cin,caddr_len);
	//创建子进程,子进程接收,父进程发送
	pid=fork();
	if(pid==-1)
	{
		ERR_MSG("客户端创建进程失败");
	}else if(pid==0)
	{
		//子进程
		while(1)
		{
			memset(&msg,0,sizeof(msg));
			//接受服务器的信息
			if(recvfrom(cfd,&msg,sizeof(msg),0,NULL,NULL)==-1)
			{
				ERR_MSG("recvfrom");
			}
			printf("[%s]>>>>(%s)\n",msg.name,msg.text);
		}
	}else
	{
		//写入聊天的内容
		while(1)
		{
			memset(msg.text,0,sizeof(msg.text));
			fgets(msg.text,sizeof(msg.text),stdin);
			msg.text[strlen(msg.text)-1]='\0';
			if(strcmp(msg.text,"quit")==0)
			{
				msg.ch='Q';
			}else{
				msg.ch='L';
			}
			//将写好的内容发给服务器
			if(sendto(cfd,&msg,sizeof(msg),0,(struct sockaddr*)&cin,caddr_len)==-1)
			{
				ERR_MSG("聊天内容发送失败");
			}	
					//当识别停止时结束进程
			if(strcmp(msg.text,"quit")==0)
			{
				kill(pid,SIGKILL);
				close(cfd);
				exit(EXIT_SUCCESS);

			}
		}
	}
	return 0;

}

 ser.c

#include "udp.h"
#define ERR_MSG(msg)  do{\
	fprintf(stderr, "__%d__:\n", __LINE__); \
	perror(msg);\
}while(0)
//聊天用结构体

int main(int argc, const char *argv[])
{		
	//进程
	pid_t pid;
	msg_t msg;//进行各种操作
	msg_t faso;//用来发送信息
	//创建报式套接字
	int sfd=socket(AF_INET,SOCK_DGRAM,0);
	if(sfd<0)
	{
		ERR_MSG("创建服务器套接字失败");
		return -1;
	}


	//填充接收方的信息给bind用
	struct sockaddr_in sin;
	sin.sin_family =AF_INET;
	sin.sin_port =htons(3696);
	sin.sin_addr.s_addr=inet_addr("192.168.114.146");
	socklen_t sadd_len=sizeof(sin);
	//绑定信息结构体
	if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin))<0)
	{
		ERR_MSG("套接字绑定失败");
		return -1;
	}


	struct sockaddr_in cin;//存储发送方的信息
	cin.sin_family=AF_INET;
	socklen_t cadd_len=sizeof(cin);
	//创建一个进程
	pid=fork();
	if(pid==-1)
	{
		ERR_MSG("服务器进程创建失败");
	}
	else if(pid==0)
	{
		//子进程
		//创建一个单链表保存网络信息结构
		user_t *head;
		creat_head(&head);
		memset(&msg,0,sizeof(msg));
		while(1)
		{
			if(recvfrom(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&cin,&cadd_len)==-1)
			{
				ERR_MSG("接收客户端信息失败");
			}

			switch(msg.ch)
			{
			case 'D':
				//用户登录
				input_addr(head,msg,sfd,cin,cadd_len);
				break;
			case 'L':
				//用户聊天
				test_addr(head,msg,sfd,cin,cadd_len);
				break;
			case 'Q':
				//用户退出
				tuichu_addr(head,msg,sfd,cin,cadd_len);
				break;
			}


		}
	}
	else
	{
		while(1)
		{


			//发系统消息
			memset(&faso,0,sizeof(faso));
			fgets(faso.text,sizeof(faso.text),stdin);
			faso.text[strlen(faso.text)-1]='\0';
			faso.ch='L';
			sprintf(faso.name,"%s","系统消息");
			if(sendto(sfd,&faso,sizeof(faso),0,(struct sockaddr*)&sin,sadd_len)==-1)
			{
				ERR_MSG("发送系统消息失败");
			}
		}
	}
	return 0;
}

 upd.c

#include "udp.h"
#define ERR_MSG(msg)  do{\
    fprintf(stderr, "__%d__:\n", __LINE__); \
    perror(msg);\
}while(0)

//创建一个单链表头
int creat_head(user_t **head)
{
	if(head==NULL)
	{
		printf("传送错误,请检查\n");
		return -1;
	}
	(*head)=(userptr)malloc(sizeof(user_t));
	(*head)->next=NULL;
	return 0;
}
//记录用户登录的信息
int input_addr(user_t *head,msg_t msg,int sockfd,struct sockaddr_in cliaddr,socklen_t cliaddr_len)
{
	if(head==NULL)
	{
		printf("传送错误,请检查\n");
		return -1;
	}
	//把用户信息发给所有人
	snprintf(msg.text,sizeof(msg.text),"[%s]%s",msg.name,"来喽,快去找他聊天吧!");
	//用来记录头的地址
	user_t *user_head=head; 
	while(user_head->next!=NULL)
	{
		user_head=user_head->next;
		if(sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr *)&user_head->addr,cliaddr_len)==-1)
		{
			ERR_MSG("用户信息发送给所有人失败");

		}
	}
	//创建一个新的节点,并且把用户信息放入单链表
	user_t *temp=NULL;
	creat_head(&temp);
	temp->addr=cliaddr;
	//用头插法将用户插入链表
	temp->next=head->next;
	head->next=temp;
	return 0;
	
}
//发送信息给聊天室的所有人除了自己
int test_addr(user_t *head,msg_t msg,int sockfd,struct sockaddr_in cliaddr,socklen_t cliaddr_len)
{
	if(head==NULL)
	{
		printf("传送错误,请检查\n");
		return -1;
	}
	//将接受到的信息发送给除了自己以外的所有人
	user_t *user_head=head;
	while(user_head->next!=NULL)
	{
		user_head=user_head->next;
		if(0!=memcmp(&(user_head->addr),&cliaddr,sizeof(cliaddr)))
		{
			if(sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr*)&user_head->addr,cliaddr_len)==-1)
			{
				ERR_MSG("信息发送给所有人失败");
			}
		}
	}
	return 0;
}
//退出登录并且把消息发送给所有人
int tuichu_addr(user_t *head,msg_t msg,int sockfd,struct sockaddr_in cliaddr,socklen_t cliaddr_len)
{
	if(head==NULL)
	{
		printf("传输错误,请检查\n");
		return -1;
	}
	snprintf(msg.text,sizeof(msg.text),"%s%s",msg.name,"优雅的走喽~~~");
	user_t *user_head=head;
	user_t *pdel=NULL;
	while(user_head->next!=NULL)
	{
		if(0==memcmp(&(user_head->next->addr),&cliaddr,sizeof(cliaddr)))
		{
			pdel=user_head->next;
			user_head->next=pdel->next;
			free(pdel);
			pdel=NULL;
		}
		else
		{
			user_head=user_head->next;
			if(sendto(sockfd,&msg,sizeof(msg),0,(struct sockaddr*)&user_head->addr,cliaddr_len)==-1)
			{
				ERR_MSG("将退出信息告诉所有人失败");
			}
		
		}
	}
	return 0;
}	

 udp.h

#ifndef _UDP_H
#define _UDP_H
#include<myhead.h>
#define N 512
#define ERR_MSG(msg)  do{\
    fprintf(stderr, "__%d__:\n", __LINE__); \
    perror(msg);\
}while(0)
#define SER_PORT 3696//服务器的端口号
#define GRP_IP "224.1.2.3"//组播IP
#define LOL_IP "192.168.114.140"//本机IP
//聊天用结构体
typedef struct Msg
{
	char ch;//L聊天,q退出,登入d
	char name[128];//储存用户名字
	char text[N];//储存聊天内容
}msg_t;
typedef struct User//存储用户信息
{
	struct sockaddr_in addr;
	struct User *next;
}user_t,*userptr;
//创建一个单链表头
int creat_head(user_t **head);
//记录用户登录的信息
int input_addr(user_t *head,msg_t msg,int sockfd,struct sockaddr_in cliaddr,socklen_t cliaddr_len);
//发送信息给聊天室的所有人除了自己
int test_addr(user_t *head,msg_t msg,int sockfd,struct sockaddr_in cliaddr,socklen_t cliaddr_len);
//退出登录并且把消息发送给所有人
int tuichu_addr(user_t *head,msg_t msg,int sockfd,struct sockaddr_in cliaddr,socklen_t cliaddr_len);
#endif  


http://www.niftyadmin.cn/n/5015575.html

相关文章

docker概念、安装与卸载

第一章 docker概念 Docker 是一个开源的应用容器引擎。 Docker 诞生于2013年初&#xff0c;基于 Go 语言实现&#xff0c;dotCloud 公司出品&#xff0c;后改名为 Docker Inc。 Docker 可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中&#xff0c;然后发…

利用API商品数据接口可以创造多种便利,以下通过几个具体例子来说明

在当今的数字化时代&#xff0c;API商品数据接口为许多企业和开发者提供了强大的资源。通过API商品数据接口&#xff0c;我们可以轻松地获取到各种商品信息&#xff0c;包括但不限于价格、库存、商品描述等&#xff0c;从而为我们创造便利。本文将通过几个具体例子来探讨如何利…

怎样理解 Object 类中的 clone() 方法?

目录 1. clone() 方法说明 2. JVM 内存模型 3. 浅拷贝(浅克隆)与深拷贝(深克隆)的区别 4. 使用 clone() 克隆方法需要注意的事项 5. Object 类中的 clone() 方法为浅克隆 1. clone() 方法说明 clone() 方法是 Java.lang.Object 类中已经定义好的一个方法&#xff0c;该方…

二分搜索树深度优先遍历(Java 实例代码)

目录 二分搜索树深度优先遍历 Java 实例代码 src/runoob/binary/Traverse.java 文件代码&#xff1a; 二分搜索树深度优先遍历 二分搜索树遍历分为两大类&#xff0c;深度优先遍历和层序遍历。 深度优先遍历分为三种&#xff1a;先序遍历&#xff08;preorder tree walk&am…

一些 Conda 的常用命令

首先是 Miniconda 的下载地址&#xff0c;比 Anaconda 轻量多了&#xff0c;用起来更舒服&#xff0c;就是没有图形界面 Miniconda — miniconda documentation 总结一些常用命令&#xff0c;省得每次需要用的时候都得一通查&#xff0c;注意以下都是在 Windows 下的命令&…

【2023年11月第四版教材】第11章《成本管理》(合集篇)

第11章《成本管理》&#xff08;合集篇&#xff09; 1 章节说明2 管理基础3 管理过程3.1 管理ITTO汇总★★★ 4 规划成本管理4.1 成本管理计划★★★ 5 估算成本5.1 估算成本★★★ &#xff08;19上57&#xff09; &#xff08;19下35&#xff09;5.2 数据分析★★★5.4 成本估…

Stream流用法详解

文章目录 &#x1f412;个人主页&#x1f3c5;JavaSE系列专栏&#x1f4d6;前言&#xff1a;&#x1f380;什么是Stream流&#xff1f;&#x1f9f8;流的概念 &#x1f415;如何获取流&#x1f415;流的操作&#x1f3c5;中间操作&#xff08;返回类型都是流&#xff09;filter…

vue3动态绑定class

vue3动态绑定class 1.动态拼接class类名 1.动态拼接class类名 <span v-for"(locationStatus, index) in BASIC_LOCATION_STATUS" :key"index"><div :class"[left block_size color_ locationStatus.value]"></div> </spa…