1.6作业---基于udp的网络聊天室

功能:

有新用户登录,其他在线的用户可以收到登录信息

有用户群聊,其他在线的用户可以收到群聊信息

有用户退出,其他在线的用户可以收到退出信息

服务器可以发送系统信息

执行思路

udp客户端代码

 
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
 
#define ERR_MSG(msg) do{\
    fprintf(stderr, "line:%d\n", __LINE__);\
    perror(msg);\
}while(0)
 
#define IP "192.168.2.108"   //填你们自己的本机IP ifconfig查看
#define PORT 6666           //1024~49151
 
//创建数据结构体
typedef struct {
	char type;
	char name[20];
	char text[128];
}__attribute__((packed)) msg;
 
msg inform; 	//存储发送信息
 
msg inform_rec; 	//存储接收信息
 
struct sockaddr_in cin; //存储要发送的服务器信息
 
pthread_t pth_rec;
pthread_t pth_snd;
 
//发送线程函数
void *pthread_snd(void *p);
 
//接收线程函数
void *pthread_rec(void *p);
 
struct sockaddr_in rcvAddr; 	//存储接收到的数据包的地址信息
 
socklen_t addrlen = sizeof(rcvAddr); 	//接收到的数据包的地址信息的大小
 
 
 
int main(int argc, const char *argv[])
{
	//创建报式套接字
	int sfd = socket(AF_INET, SOCK_DGRAM, 0);
	if(sfd < 0)
	{
		ERR_MSG("socket");
		return -1;
	}
 
 
	//绑定客户端自身的地址信息结构体-->非必须绑定,建议不写
 
 
	cin.sin_family 		= AF_INET;
	cin.sin_port 		= htons(PORT);
	cin.sin_addr.s_addr = inet_addr(IP);
 
 
	char buf[128] = "";
	ssize_t res = 0;
 
	inform.type = 'L';
	printf("请输入用户名>>>");
	fgets(buf,sizeof(buf),stdin);
	buf[strlen(buf)-1] = 0;
	strcpy(inform.name,buf);
	//发送登录消息
	if(sendto(sfd,&inform,sizeof(inform),0,(struct sockaddr *)&cin,sizeof(cin)) < 0)
	{
		ERR_MSG("sendto");
		return -1;
	}
	//创建发送线程
	if(pthread_create(&pth_snd,NULL,pthread_snd,&sfd) < 0)
	{
		ERR_MSG("pthread_create");
		return -1;
	}
 
	//创建接收线程
	if(pthread_create(&pth_rec,NULL,pthread_rec,&sfd) < 0)
	{
		ERR_MSG("pthread_create");
		return -1;
	}
	//阻塞线程
	pthread_join(pth_rec,NULL);
	pthread_join(pth_snd,NULL);
	//关闭套接字
	close(sfd);
 
	return 0;
}
 
 
//发送线程
void *pthread_snd(void *p)
{
	int sfd = *(int *)p;
	ssize_t res = 0;
	while(1)
	{
		bzero(inform.text, sizeof(inform.text));
		inform.type = 'C';
		fgets(inform.text, sizeof(inform.text), stdin);
		inform.text[strlen(inform.text)-1] = 0;
		if(strcmp(inform.text,"quit") == 0)
		{
			inform.type = 'Q';
		}
 
		if(sendto(sfd, &inform, sizeof(inform), 0, (struct sockaddr*)&cin, sizeof(cin)) < 0)
		{
			ERR_MSG("sendto");
			return NULL;
		}
 
		if(strcmp(inform.text,"quit") == 0)
		{
			pthread_cancel(pth_rec);
			pthread_exit(NULL);
		}
		printf("sendto success\n");
 
	}
}
 
void *pthread_rec(void *p)
{
	char c = 0;
	int sfd = *(int *)p;
	ssize_t res = 0;
	while(1)
	{
		//接收
		bzero(&inform_rec, sizeof(inform_rec));
		res = recvfrom(sfd, &inform_rec, sizeof(inform_rec), 0, (struct sockaddr*)&rcvAddr, &addrlen);
		if(res < 0)
		{
			ERR_MSG("recvfrom");
			return NULL;
		}
		c = inform_rec.type;
		if('L' == c)
		{
			printf("-----[%s]已上线------\n",inform_rec.name);
		}
		else if('C' == c)
		{
			printf("[%s]:%s\n",inform_rec.name,inform_rec.text);
		}
		else if('Q' == c)
		{
			printf("-----[%s]已下线-----\n",inform_rec.name);
		}
	}
}

upd服务器代码

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
 
#define ERR_MSG(msg) do{\
    fprintf(stderr, "line:%d\n", __LINE__);\
    perror(msg);\
}while(0)
 
#define IP "192.168.2.108"   //填你们自己的本机IP ifconfig查看
#define PORT 6666           //1024~49151
 
//创建数据结构体
typedef struct {
	char type; 	//存储协议
	char name[20]; 	//用户名
	char text[128]; //数据
} __attribute__((packed)) msg;
 
//定义用户信息结构体
typedef struct Node{
	
	union{
		int len;        //头结点数据域
		struct sockaddr_in data;  //普通节点数据域
	};
	struct Node *next;  //指针域
}Linklist;
 
//链表头结点
Linklist *L;
 
 
msg inform;    //存储接收信息
msg inform_sen; 	//存储发送信息
 
//存储用户信息结构体
struct sockaddr_in cin;
 
//用户信息结构体的大小
socklen_t addr = sizeof(cin);
 
//报式套接字文件描述符
int sfd = 0;
 
//登录遍历链表函数
void list_show(Linklist *L,msg p);
 
//申请节点封装数据函数
Linklist *buy_node(struct sockaddr_in e);
 
 
//尾插函数
int list_insert_tail(Linklist *L,struct sockaddr_in e);
 
//发送信息遍历链表
void list_show1(Linklist *L,msg p,struct sockaddr_in sin);
 
 
//按值进行查找函数
int list_search_value(Linklist *L,struct sockaddr_in e);
 
 
//任意位置删除
int list_delete_pos(Linklist *L,int pos);
 
//用户下线遍历链表
void list_show2(Linklist *L,msg p);
 
//创建链表
Linklist *list_create();
 
//判空函数
int list_empty(Linklist *L);
 
//接收线程函数
void *pthread_rec(void *p);
 
//发送线程函数
void *pthread_sen(void *p);
 
//按位置查找返回结点
Linklist *find_node(Linklist *L,int pos);
 
int main(int argc, const char *argv[])
{
	//创建链表
	L = list_create();
	if(NULL == L)
	{
		printf("%d:创建失败\n",__LINE__);
		return -1;
	}
	
	//创建报式套接字
	sfd = socket(AF_INET,SOCK_DGRAM,0);
	if(sfd < 0)
	{
		ERR_MSG("socket");
		return -1;
	}
 
	//允许端口快速重用--->允许程序退出后,端口号能被新的进程快速覆盖
	//必须放在bind之前
	int reuse = 1;
	if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) <0)
	{
		ERR_MSG("setsockopt");
		return -1;
	}
	printf("允许端口快速重用成功\n");
 
	//填充服务器自身的地址信息结构体
	cin.sin_family 		= AF_INET;
	cin.sin_port 		= htons(PORT);
	cin.sin_addr.s_addr = inet_addr(IP);
 
	//绑定IP和端口
	if(bind(sfd,(struct sockaddr *)&cin,sizeof(cin)) < 0)
	{
		ERR_MSG("bind");
		return -1;
	}
	pthread_t pth_rec;
	pthread_t pth_sen;
	//创建接收线程
	if(pthread_create(&pth_rec,NULL,pthread_rec,&sfd) < 0)
	{
		ERR_MSG("pthread_create");
		return -1;
	}
	//创建发送线程
	if(pthread_create(&pth_sen,NULL,pthread_sen,&sfd) < 0)
	{
		ERR_MSG("pthread_create");
		return -1;
	}
 
	//阻塞等待线程退出
	pthread_join(pth_rec,NULL);
	pthread_join(pth_sen,NULL);
	//关闭文件描述符
	close(sfd);
	return 0;
}
 
//接收线程
void *pthread_rec(void *p)
{
	int sfd = *(int *)p;
	ssize_t res = 0;
	char c = 0;
	int a = 0;
	while(1)
	{
		res = recvfrom(sfd,&inform,sizeof(inform),0,(struct sockaddr *)&cin,&addr);
		if(res < 0)
		{
			ERR_MSG("recvfrom");
			return NULL;
		}
		c = inform.type;
		switch(c)
		{
		case 'L':
 
			//打印信息
			printf("---[%s]上线---\n",inform.name);
			
			//遍历函数发送数据
			list_show(L,inform);
 
			//添加结点
			list_insert_tail(L,cin);
			break;
		case 'C':
 
			//打印信息
			printf("---[%s]:%s---\n",inform.name,inform.text);
			
			//遍历转发数据
			list_show1(L,inform,cin);
			break;
		case 'Q':
 
			//打印信息
			printf("---[%s]下线---\n",inform.name);
			//按值查找返回位置
			a = list_search_value(L,cin);
			//任意位置删除
			list_delete_pos(L,a);
			//遍历输出数据
			list_show2(L,inform);
			break;
		}
	}
}
 
//发送线程
void *pthread_sen(void *p)
{
	int sfd = *(int *)p;
	char str[128] = "";
	inform_sen.type = 'C';
	strcpy(inform_sen.name,"system");
	while(1)
	{
		bzero(str,sizeof(str));
		fgets(str,sizeof(str),stdin);
		str[strlen(str)-1] = 0;
		strcpy(inform_sen.text,str);
		list_show2(L,inform_sen);
	}
}
 
//遍历链表
void list_show(Linklist *L,msg p)
{
	//判断逻辑
	if(NULL == L || list_empty(L))
	{
		//puts("遍历失败");
		return;
	}
 
	//遍历逻辑
	Linklist *q = L->next; //定义指针指向第一个结点
	while(q != NULL)
	{
		if(sendto(sfd,&p,sizeof(p),0,(struct sockaddr *)&q->data,sizeof(q->data)) < 0)
		{
			ERR_MSG("sendto");
			return ;
		}
		q = q->next;
	}
}
 
//申请节点封装数据函数
Linklist *buy_node(struct sockaddr_in e)
{
	//在堆区申请一个结点的空间
	Linklist *p = (Linklist *)malloc(sizeof(Linklist));
	if(NULL == p)
	{
		puts("空间申请失败");
		return NULL;
	}
	//将数据封装到结点的数据域中
	p->data = e;
	p->next = NULL;
	return p;
}
 
//尾插函数
int list_insert_tail(Linklist *L,struct sockaddr_in e)
{
	//判断逻辑
	if(NULL == L)
	{
		puts("所给链表不合法");
		return 0;
	}
	//申请节点封装数据
	Linklist *p = buy_node(e);
	//定义遍历指针,指向最后一个结点
	Linklist *q = L;
	while(q->next != NULL)
	{
		q = q->next;
	}
	//尾插操作
	p->next = q->next;
	q->next = p;
	//表长变化
	L->len++;
	return 1;
}
//遍历链表
void list_show1(Linklist *L,msg p,struct sockaddr_in sin)
{
	//判断逻辑
	if(NULL == L || list_empty(L))
	{
		//puts("遍历失败");
		return;
	}
	//遍历逻辑
	Linklist *q = L->next; //定义指针指向第一个结点
	while(q != NULL)
	{
		//printf("%c\t",q->data);
		if(memcmp(&q->data,&sin,sizeof(cin)) != 0)
		{
			if(sendto(sfd,&p,sizeof(p),0,(struct sockaddr *)&q->data,sizeof(q->data)) < 0)
			{
				ERR_MSG("sendto");
				return ;
			}
		}
 
		q = q->next;
	}
}
 
//按值进行查找函数
int list_search_value(Linklist *L,struct sockaddr_in e){
	//判断逻辑
	if(NULL == L || list_empty(L)){
		puts("查找失败");
		return -1;
	}
	//查找逻辑
	Linklist *q = L->next;  //从第一个结点出发
	for(int i=1;i<=L->len;i++){
		if(memcmp(&q->data,&e,sizeof(cin)) == 0){
			return i;
		}
		q = q->next;  //继续判断下一个结点
	}
	return 0;  
}
 
//任意位置删除
int list_delete_pos(Linklist *L,int pos){
	//判断逻辑
	if(NULL == L || list_empty(L) || pos<1 || pos>L->len){
		puts("删除失败");
		return 0;
	}
	//找到要删除结点的前驱
	Linklist *q = find_node(L,pos-1);
	if(NULL == q){
		puts("删除失败");
		return 0;
	}
	//删除操作
	Linklist *p = q->next;
	q->next = p->next;
	free(p);
	p = NULL;
	//表长变化
	L->len--;
	puts("删除成功");
	return 1;
}
 
//遍历链表
void list_show2(Linklist *L,msg p)
{
	//判断逻辑
	if(NULL == L || list_empty(L))
	{
		//puts("遍历失败");
		return;
	}
 
	//遍历逻辑
	Linklist *q = L->next; //定义指针指向第一个结点
	while(q != NULL)
	{
		
		if(sendto(sfd,&p,sizeof(p),0,(struct sockaddr *)&q->data,sizeof(q->data)) < 0)
		{
			ERR_MSG("sendto");
			return ;
		}
		q = q->next;
	}
}
 
//创建链表
Linklist *list_create(){
	Linklist *L = (Linklist *)malloc(sizeof(Linklist));
	if(NULL == L){
		puts("创建失败");
		return NULL;
	}
	//创建成功进行初始化
	L->len = 0;
	L->next = NULL;
 
	puts("创建成功");
	return L;
}
 
//判空函数
int list_empty(Linklist *L)
{
	return L->next == NULL;
}
 
//按位置查找返回结点
Linklist *find_node(Linklist *L,int pos){
	//判断逻辑
	if(pos<0 || pos>L->len){
		puts("查找失败");
		return NULL;
	}
	//查找逻辑
	//定义指针从头结点出发
	Linklist *q=L;
	for(int i=0;i<pos;i++){
		q = q->next;
	}
	return q;  //将找到的结点返回

}

执行结果

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值