2022.8.12 作业

小项目:基于UDP的网络聊天室

 单链表存储数据:linklist.c

#include "linklist.h"

//头节点的创建
linkList *list_creat()
{
	linkList *h=(linkList *)malloc(sizeof(linkList));
	if(NULL==h){
		return NULL;
	}
	h->next=NULL;
	return h;
}

//申请节点
linkList *node_buy(struct sockaddr_in addr)
{
	linkList *p=(linkList *)malloc(sizeof(linkList));
	if(NULL==p){
		return NULL;
	}
	p->tar_addr=addr;
	p->next=NULL;
	return p;
}

//判空
int list_empty(linkList *h)
{
	if(NULL==h){
		return -1;
	}
	return h->next==NULL ? 1 : 0;
}

//头插法
int list_insert_head(linkList *h,struct sockaddr_in addr)
{
	if(NULL==h){
		return -1;
	}
	linkList *l=node_buy(addr);
	if(NULL==l){
		return -2;
	}
	l->next=h->next;
	h->next=l;
	return 0;
}

//删除目标
int list_delete(linkList *h,struct sockaddr_in addr)
{
	if(NULL==h){
		return -1;
	}
	if(list_empty(h)){
		return -2;
	}

	linkList *p=h->next;
	while(p->next!=NULL){
		if(memcmp(&(p->tar_addr),&addr,sizeof(addr))==0)
			break;
		else
			p=p->next;
	}
	while(h->next!=p){
		h=h->next;
	}
	h->next=p->next;
	free(p);
	return 0;
}

//寻找目标是否存在
int list_find_exist(linkList *h,struct sockaddr_in addr)
{
	if(NULL==h){
		return -1;
	}
	if(list_empty(h)){
		return 1;
	}

	while(h->next!=NULL){
		h=h->next;
		if(memcmp(&(h->tar_addr),&addr,sizeof(addr))==0)
			return -1;
	}
	return 1;
}

//头删
int list_delete_head(linkList *h)
{
	if(NULL==h){
		return -1;
	}
	if(!list_empty(h)){
		linkList *p=h->next;
		h->next=p->next;
		free(p);
	}else{
		return -1;
	}
	return 0;
}

//销毁链表
int list_destroy(linkList *h)
{
	while(list_delete_head(h)==0);
	return 0;
}

单链表头文件:linklist.h

#ifndef __LINKLIST_H__
#define __LINKLIST_H__

#include <stdio.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <string.h>

typedef struct Node{
	struct sockaddr_in tar_addr;
	struct Node *next;
}linkList;

linkList *list_creat();

linkList *node_buy(struct sockaddr_in addr);

int list_empty(linkList *h);

int list_insert_head(linkList *h,struct sockaddr_in addr);

int list_delete(linkList *h,struct sockaddr_in addr);

int list_find_exist(linkList *h,struct sockaddr_in addr);

int list_delete_head(linkList *h);

int list_destroy(linkList *h);

#endif

客户端:chatClient.c

#include <stdio.h>	
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include "./linklist.h"
#include <semaphore.h>

#define ERR_MSG(msg) do{\
	fprintf(stderr,"__%s__ __%d__\n",__func__,__LINE__);\
	perror(msg);\
}while(0)

#define N 32
#define MAX 128

typedef struct {
	char flag;
	char username[N];
	char buf[MAX];
}Msgbag;


int cfd;
int label=0;

//给服务器发送消息
void *callBackSnd(void *arg)
{
	Msgbag bag;
	struct sockaddr_in tar=*(struct sockaddr_in *)arg;

	memset(&bag,0,sizeof(bag));
	bag.flag='u';
	printf("请输入你的用户名>>>");
	fgets(bag.username,sizeof(bag.username),stdin);
	bag.username[strlen(bag.username)-1]=0;
	if(sendto(cfd,&bag,sizeof(bag),0,(struct sockaddr *)&tar,sizeof(tar))<0){
		ERR_MSG("sendto");
		exit(-1);
	}

	while(1){
		bzero(bag.buf,sizeof(bag.buf));
		fgets(bag.buf,sizeof(bag.buf),stdin);
		bag.buf[strlen(bag.buf)-1]=0;
		if(label){
			continue;
		}
		if(sendto(cfd,&bag,sizeof(bag),0,(struct sockaddr *)&tar,sizeof(tar))<0){
			ERR_MSG("sendto");
			exit(-1);
		}
		if(strcasecmp(bag.buf,"quit")==0){
			exit(0);
		}
	}
	pthread_exit(NULL);
}

//客户端接受来自服务器的消息
void *callBackRcv(void *arg)
{
	Msgbag bag;
	struct sockaddr_in tar=*(struct sockaddr_in *)arg;
	while(1){
		memset(&bag,0,sizeof(bag));
		if(recv(cfd,&bag,sizeof(bag),0)<0){
			ERR_MSG("recv");
			exit(-1);
		}
		if(bag.flag=='s'){
			printf("%s\n",bag.buf);
		}else if(bag.flag=='t'){
			if(strcasecmp(bag.username,"system")==0){
				printf("系统提醒:%s\n",bag.buf);
			}else if(strcasecmp(bag.username,"stop")==0){
				label=1;
				printf("系统提示:全体禁言\n");
			}else if(strcasecmp(bag.username,"continue")==0){
				label=0;
				printf("系统提示:解除全体禁言\n");
			}else{
				printf("系统提示:群聊解散\n");
				exit(0);
			}
		}else{
			printf("%s:%s\n",bag.username,bag.buf);
		}
	}
}

int main(int argc, const char *argv[])
{
	if(argc<3){
		fprintf(stderr,"[传参不足]:%s IP PORT\n",argv[0]);
		return -1;
	}
	
	//创建UDP套接字
	cfd=socket(AF_INET,SOCK_DGRAM,0);
	if(cfd<0){
		ERR_MSG("socket");
		return -1;
	}

	//填充目标服务器的地址信息结构体
	struct sockaddr_in tar;
	tar.sin_family=AF_INET;
	tar.sin_port=htons(atoi(argv[2]));
	tar.sin_addr.s_addr=inet_addr(argv[1]);

	//创建两个线程执行不同任务
	pthread_t rid,sid;
	if(pthread_create(&rid,NULL,callBackRcv,(void *)&tar)!=0){
		ERR_MSG("pthread_create");
		return -1;
	}

	if(pthread_create(&sid,NULL,callBackSnd,(void *)&tar)!=0){
		ERR_MSG("pthread_create");
		return -1;
	}

	pthread_join(rid,NULL);
	pthread_join(sid,NULL);
    close(cfd);

	return 0;
}

服务器:chatServe.c

#include <stdio.h>	
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include "./linklist.h"

#define ERR_MSG(msg) do{\
	fprintf(stderr,"__%s__ __%d__\n",__func__,__LINE__);\
	perror(msg);\
}while(0)

#define N 32
#define MAX 128

typedef struct {
	char flag;
	char username[N];
	char buf[MAX];
}Msgbag;

int sfd;

int sendtoall(linkList *p,struct sockaddr_in *temp,Msgbag bag)
{
	int flag=0;
	if(NULL==temp)
		flag=1;
	while(!list_empty(p)){
		p=p->next;
		if(!flag){
			if(memcmp(&(p->tar_addr),temp,sizeof(struct sockaddr_in))==0)
				continue;
		}
		if(sendto(sfd,&bag,sizeof(bag),0,(struct sockaddr *)&(p->tar_addr),\
					sizeof(p->tar_addr))<0){
			ERR_MSG("sendto");
			exit(-1);
		}
	}
	return 0;
}

void *callBackRcv(void *arg)
{
	linkList *userdata=(linkList *)arg;
	struct sockaddr_in temp;
	socklen_t addrlen=sizeof(temp);
	Msgbag bag;	
	while(1){
		memset(&bag,0,sizeof(bag));
		if(recvfrom(sfd,&bag,sizeof(bag),0,(struct sockaddr*)&temp,&addrlen)<0){
			ERR_MSG("recv");
			exit(-1);
		}
		if(list_find_exist(userdata,temp)==1){
			list_insert_head(userdata,temp);
			printf("%s [ %s : %d ] join\n",\
					bag.username,inet_ntoa(temp.sin_addr),ntohs(temp.sin_port));
			bag.flag='s';
			sprintf(bag.buf,"*****%s加入群聊*****",bag.username);
			sendtoall(userdata,&temp,bag);
		}else{
			//将消息转发给除自己外的所有用户
			if(strcasecmp(bag.buf,"quit")==0){
				list_delete(userdata,temp);
				printf("%s [ %s : %d ] quit\n",\
						bag.username,inet_ntoa(temp.sin_addr),ntohs(temp.sin_port));
				bag.flag='s';
				sprintf(bag.buf,"*****%s退出群聊*****",bag.username);
				sendtoall(userdata,NULL,bag);
			}else{
				bag.flag='u';
				printf("%s [ %s : %d ] chat\n",\
						bag.username,inet_ntoa(temp.sin_addr),ntohs(temp.sin_port));
				sendtoall(userdata,&temp,bag);
			}
		}	
	}
	pthread_exit(NULL);
}

//发送系统消息
void *callBackSnd(void *arg)
{
	linkList *userdata=(linkList *)arg;
	Msgbag bag;
	bag.flag='t';
	while(1){
		strcpy(bag.username,"system");
		bzero(bag.buf,sizeof(bag.buf));
		fgets(bag.buf,sizeof(bag.buf),stdin);
		bag.buf[strlen(bag.buf)-1]=0;

		if(strcasecmp(bag.buf,"stop")==0){
			strcpy(bag.username,bag.buf);
		}else if(strcasecmp(bag.buf,"continue")==0){
			strcpy(bag.username,bag.buf);
		}else if(strcasecmp(bag.buf,"quit")==0){
			strcpy(bag.username,bag.buf);
		}
		sendtoall(userdata,NULL,bag);
		if(strcasecmp(bag.buf,"quit")==0){
			list_destroy(userdata);
		}
	}
	pthread_exit(NULL);
}

int main(int argc, const char *argv[])
{
	if(argc<3){
		fprintf(stderr,"[传参不足]:%s IP PORT\n",argv[0]);
		return -1;
	}

	//创建UDP套接字
	sfd=socket(AF_INET,SOCK_DGRAM,0);
	if(sfd<0){
		ERR_MSG("socket");
		return -1;
	}

	//填充地址信息结构体
	struct sockaddr_in sin;
	sin.sin_family=AF_INET;
	sin.sin_port=htons(atoi(argv[2]));
	sin.sin_addr.s_addr=inet_addr(argv[1]);

	//绑定ip和端口号
	if(bind(sfd,(struct sockaddr *)&sin,sizeof(sin))<0){
		ERR_MSG("bind");
		return -1;
	}

	//创建一个链表保存用户及其主机信息
	linkList *userdata=list_creat();

	//创建两个线程执行不同任务
	pthread_t rid,sid;
	if(pthread_create(&rid,NULL,callBackRcv,(void *)userdata)!=0){
		ERR_MSG("pthread_create");
		return -1;
	}

	if(pthread_create(&sid,NULL,callBackSnd,(void *)userdata)!=0){
		ERR_MSG("pthread_create");
		return -1;
	}

	pthread_join(rid,NULL);
	pthread_join(sid,NULL);
	
	return 0;
}

Makefile:

TARGET?=main
SER:=ser
CLI:=cli
CAN:=-c -o
CC:=gcc
PD:=-pthread
OBJS:=chatServe.o linklist.o
OBJC:=chatClient.o linklist.o

$(TARGET):$(OBJS) $(OBJC)
	$(CC) $(OBJS) -o $(SER) $(PD)
	$(CC) $(OBJC) -o $(CLI) $(PD)

%.o:%.c
	$(CC) $< $(CAN) $@

clean:
	rm -rf $(OBJS) $(OBJC) $(SER) $(CLI)

测试:

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值