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

 

1. 需求
项目需求:
1. 如果有用户登录,其他用户可以收到这个人的登录信息
2. 如果有人发送信息,其他用户可以收到这个人的群聊信息
3. 如果有人下线,其他用户可以收到这个人的下线信息
4. 服务器可以发送系统信息

 

 

 服务器端

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

typedef struct sockaddr_in IN;
typedef struct Node1
{ 
	char name[20];
	char msg1[128];
	int S;
}msg;

int len_msg =sizeof(msg);
int len_addr=sizeof(struct sockaddr_in);
typedef struct user_list
{
	struct sockaddr_in cin;
	struct user_list *next;
}Linklist;

IN ser_addr,cli_addr;

Linklist* list;

//打印错误新的宏函数
#define ERR_MSG(msg)  do{\
	fprintf(stderr, " __%d__ ", __LINE__);\
	perror(msg);\
}while(0)

//初始化链表
 void init();
//尾插
void insert_tail(IN cin);

void* thread_user(void *arg );
//删除用户 
void list_delete(IN cin);

void user_log(int sfd,msg msg_user, IN cli_addr);   //用户登录

void user_send(int sfd,msg msg_user,IN cli_addr);  //用户发送消息

void user_quit(int sfd,msg msg_user,IN cli_addr);   //用户退出

void ser_send(int sfd,char *buf);  //服务端发送给用户
//初始化链表
void init()
{
	list=(Linklist*)malloc(sizeof(Linklist));


	list->next=NULL;
	// printf("创建成功\n");
}

//尾插
void insert_tail(IN cin )
{
	Linklist *q=list;
	int i=1;
	while(q->next !=NULL)
	{
		q=q->next;
	}

	Linklist *qq=(Linklist*)malloc(sizeof(cin));
	qq->cin=cin;
	qq->next=q->next;
	q->next=qq;
	
   
}


//删除用户 
void list_delete(IN cin)
{
   Linklist *p=list->next;
   Linklist *q=list;
   int i=1;
   while(p!=NULL && p->next!=NULL)
   {
	   if(p->cin.sin_port ==cin.sin_port &&p->cin.sin_addr.s_addr ==cli_addr.sin_addr.s_addr)
		   break;

	   p=p->next;
	   q=q->next;

   }

   q->next=p->next;
   free(p);

}

void user_log(int sfd,msg msg_user,IN cli_addr)   //用户登录
{
    char buf[128]="";
	Linklist* P=list->next;
    char name1[30]={0};
	strcat(name1,msg_user.name);
	strcat(name1,"登录\n");

	printf("%s",name1);
	while(P)
	{
		sendto(sfd,name1,strlen(name1),0,(struct sockaddr*)&P->cin,len_addr);
		P=P->next;
	}
    insert_tail(cli_addr);
}

void user_send(int sfd,msg msg_user,IN cli_addr)  //用户发送消息
{

    char buf[128]="";
	Linklist* P=list->next;
	strcat(buf,msg_user.name);
	strcat(buf," send: ");
	strcat(buf,msg_user.msg1);  //将信息打包进buf
	buf[strlen(buf)]='\0';
	printf("%s",msg_user.msg1);

	//发送给别的客户端
	while(P)
	{
		//判断是不是用户本身
		if((P->cin.sin_port !=cli_addr.sin_port) ||(P->cin.sin_addr.s_addr !=  cli_addr.sin_addr.s_addr) )
		{
		   sendto(sfd,buf,strlen(buf),0,(struct sockaddr*)&(P->cin),len_addr);	
		}
		P=P->next;

	}



}

void user_quit(int sfd,msg msg_user,IN cli_addr)   //用户退出
{
	Linklist *P=list->next;
	char buf[128]="";
    strcat(buf,msg_user.name);
	strcat(buf,"退出\n");
	printf("%s",buf);
	while(P)
	{
		sendto(sfd,buf,strlen(buf),0,(struct sockaddr*)&(P->cin),len_addr);
		P=P->next;
	}
    list_delete(cli_addr);

	//ser_send(sfd,buf);

}



void ser_send(int sfd,char *buf)  //服务端发送给用户
{
	Linklist *P=list->next;
	while(P)
	{
		sendto(sfd,buf,strlen(buf),0,(struct sockaddr*)&P->cin,len_addr);
		P=P->next;
	}
}




//子线程,存储用户的信息地址结构体
void* thread_user(void *arg )
{

	int sfd=*(int *)arg;

	while(1)
	{
		msg msg_user;
		int len_msg=sizeof(msg_user);
		//接收用户端的信息,保存在结点中
		int res=recvfrom(sfd,&msg_user,sizeof(msg_user),0,(struct sockaddr*)&cli_addr,&len_addr);
		if(res < 0)
		{
			perror("recvfrom");
			exit(0);
		}
        //printf("%s:",msg_user.name);
		switch(msg_user.S)
		{
		case 1:
			user_log(sfd,msg_user,cli_addr);   //用户登录
			break;
		case 2:

        printf("%s:",msg_user.name);
			user_send(sfd,msg_user,cli_addr);  //用户发送消息
			break;
		case 3:
			user_quit(sfd,msg_user,cli_addr);   //用户退出
			break;

		}
	}

}

int main(int argc, const char *argv[])
{
	if(argc < 3)
	{
		fprintf(stderr, "请输入IP port\n");
		return -1;
	}

	init();


	//将获取到的端口号字符串,转换成整形
	int port = atoi(argv[2]);
	if(port < 1024 || port > 49151)
	{
		fprintf(stderr, "port %d input error!! 1024~49151\n", port);
		return -1;
	}

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


	//填充服务器的IP地址以及端口号
	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]);

	pthread_t thread;
	//绑定服务器的地址信息结构体
	if(bind(sfd, (struct sockaddr*)&sin, sizeof(sin)) < 0)
	{
		ERR_MSG("bind");
		return -1;
	}
	printf("bind success\n");
    
	pthread_create(&thread,NULL,thread_user,&sfd);
    while(1)
	{
		char buf[140]="";
		char msg1[128]="";
		bzero(buf,sizeof(buf));
		bzero(msg1,sizeof(msg1));
		strcat(buf,"系统:");
		fgets(msg1,128,stdin);
		strcat(buf,msg1);
		ser_send(sfd,buf);
	

	}


	//关闭套接字
	close(sfd);
	return 0;
}

客户端

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/wait.h>
int sfd=0;
typedef struct sockaddr_in IN;
typedef struct 
{
	char name[20];
	char msg1[128];
	int S;
}msg;


//打印错误新的宏函数
#define ERR_MSG(msg)  do{\
	fprintf(stderr, " __%d__ ", __LINE__);\
	perror(msg);\
}while(0)

void* recv1(void* arg)
{

	pthread_detach(pthread_self());
	IN cin;
	char buf[140]="";
	int addrlen=sizeof(cin);

	while(1)
	{
		char buf[140]="";
		//接收服务器发送过来的数据包
		recvfrom(sfd, buf, sizeof(buf), 0, (struct sockaddr*)&cin, &addrlen);
		if(strcmp(buf,"quit")==0)
		{
			exit(0);
		}
		printf("%s", buf);
	}

}

int main(int argc, const char *argv[])
{
	if(argc < 3)
	{
		fprintf(stderr, "请输入IP port\n");
		return -1;
	}

	msg msg_user;
	//将获取到的端口号字符串,转换成整形
	int port = atoi(argv[2]);
	if(port < 1024 || port > 49151)
	{
		fprintf(stderr, "port %d input error!! 1024~49151\n", port);
		return -1;
	}
	//int addrlen= sizeof(cin);


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


	//填充客户端的IP地址以及端口号 -->因为客户端要主动发送数据包给服务器
	struct sockaddr_in cin;
	cin.sin_family 		= AF_INET;
	cin.sin_port 		= htons(6666);
	cin.sin_addr.s_addr = inet_addr(argv[1]);


	//绑定客户端自身的地址信息结构体

/*	if(bind(sfd,(struct sockaddr*)&cin,sizeof(cin))<0)
			{
			ERR_MSG("bind");
			return -1;
			}
			*/

			//填充服务器的IP地址以及端口号 -->因为客户端要主动发送数据包给服务器
			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]);

			//登录信息输入
			char name[20];
			printf("输入你用户名>>>");
			fgets(name,20,stdin);
			name[strlen(name)-1]='\0';
			strcpy(msg_user.name,name);
			msg_user.S=1;
			sendto(sfd,&msg_user,sizeof(msg_user),0,(struct sockaddr*)&sin,sizeof(sin));

			pthread_t tid;
			if(pthread_create(&tid,NULL,recv1,(void*)&sin)<0)
			{
				ERR_MSG("pthread_create");
				return -1;
			}
			//发送消息
			char sendmsg[128]="";
			while(1)
			{   //输入信息
				bzero(sendmsg,sizeof(sendmsg));
				fgets(sendmsg,128,stdin);
				strcpy(msg_user.name,name);
				strcpy(msg_user.msg1,sendmsg);

				//如果遇到quit\n退出
				if(strcmp(sendmsg,"quit\n")==0)
				{
					msg_user.S=3;
	if(sendto(sfd,&msg_user,sizeof(msg_user),0,(struct sockaddr*)&sin,sizeof(sin))<0)
				{
					perror("sento");
					exit(0);
				}
					exit(0);
				}
				else 
				{
					msg_user.S=2;
				}

				//发送到服务端
				if(sendto(sfd,&msg_user,sizeof(msg_user),0,(struct sockaddr*)&sin,sizeof(sin))<0)
				{
					perror("sento");
					exit(0);
				}
							//  waitpid(-1, NULL, WNOHANG);

			}

			close(sfd);
			return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值