嵌入式学习

基于UDP的网络聊天室

        流程图。(注意:这里服务器和客户端运行的时候都需要在终端后面紧跟着IP和端口号。)

         服务器代码。

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <wait.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/un.h>
#include <poll.h>
#include <errno.h>
#include <sqlite3.h>

/*
#define SER_PORT 8888
#define SER_IP "192.168.125.122"
*/

typedef struct msg {
	char code;//操作码   'L'登录   'C'群聊   'Q'退出
	char name[20];
	char txt[128];
}msg_t;

typedef struct node_cin {
	struct sockaddr_in c_addr;
	struct node_cin *next;
}node_t;

//创建结点
int creat_node(node_t **p);
//登录
int do_login(struct sockaddr_in cin,node_t *phead,int sfd,msg_t msg);
//群聊
int do_chat(struct sockaddr_in cin,node_t *phead,int sfd,msg_t msg);
//退出
int do_quit(struct sockaddr_in cin,node_t *phead,int sfd,msg_t msg);

int main(int argc, const char *argv[])
{
	//创建套接字,UDP通信
	int sfd=socket(AF_INET,SOCK_DGRAM,0);
	if(-1 == sfd)
	{
		perror("socket error");
		return -1;
	}
	//printf("socket success\n");
	
	//绑定IP地址和端口号
	//准备填充地址结构体信息
	struct sockaddr_in sin;
	sin.sin_family=AF_INET;
	//sin.sin_port=htons(SER_PORT);
	sin.sin_port=htons(atoi(argv[2]));
	//sin.sin_addr.s_addr=inet_addr(SER_IP);
	sin.sin_addr.s_addr=inet_addr(argv[1]);
	socklen_t sin_len=sizeof(sin);
	//绑定工作
	if(bind(sfd,(struct sockaddr*)&sin,sizeof(sin)) == -1)
	{
		perror("bind error");
		return -1;
	}
	//printf("bind success\n");

	//保存客户端网络信息的结构体
	struct sockaddr_in cin;
	socklen_t cin_len=sizeof(cin);

	msg_t msg;

	pid_t pid=fork();
	if(pid>0)
	{
		//父进程发送系统消息
		//服务器发系统消息时,把父进程当做一个客户端,以群聊的方式向子进程发送系统消息
		msg.code='C';
		strcpy(msg.name,"server");
		while(1)
		{
			fgets(msg.txt,128,stdin);
			msg.txt[strlen(msg.txt)-1]='\0';

			if(sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&sin,sin_len) == -1)
			{
				perror("sendto error");
			}
		}

	}else if(0 == pid)
	{
		//子进程用来接收并处理数据
		node_t *phead=NULL;
		creat_node(&phead);
		phead->next=NULL;

		while(1)
		{
			memset(&msg,0,sizeof(msg));
			memset(&cin,0,sizeof(cin));
			if(recvfrom(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&cin,&cin_len) == -1)
			{
				perror("recvfrom error");
				return -1;
			}
			printf("%s发来消息-->%s\n",msg.name,msg.txt);
			switch(msg.code)
			{
				case 'L':
					//cin是收到的客户端的网络信息结构体
					//phead是链表的头结点
					//sfd是转发消息的套接字
					//msg是要转发给所有用户的数据
					do_login(cin,phead,sfd,msg);
					break;
				case 'C':
					do_chat(cin,phead,sfd,msg);
					break;
				case 'Q':
					do_quit(cin,phead,sfd,msg);
					break;
			}
		}
	}else {
		perror("fork error");
		return -1;
	}

	close(sfd);
	return 0;
}

//创建链表结点
int creat_node(node_t **p)
{
	*p=(node_t*)malloc(sizeof(node_t));
}

//登录
int do_login(struct sockaddr_in cin,node_t *phead,int sfd,msg_t msg)
{
	//先遍历链表 将 xxx登录 的消息发送给所有人
	node_t *ptemp=phead;
	while(ptemp->next != NULL)
	{
		ptemp=ptemp->next;
		if(sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&(ptemp->c_addr),sizeof(ptemp->c_addr)) == -1)
		{
			perror("sendto error");
			return -1;
		}
	}

	//将新登录的用户头插到链表中
	node_t *pnew=NULL;
	creat_node(&pnew);
	pnew->c_addr=cin;
	pnew->next=phead->next;
	phead->next=pnew;
	return 0;
}

//群聊
int do_chat(struct sockaddr_in cin,node_t *phead,int sfd,msg_t msg)
{
	//群聊。服务器只需要转发
	//遍历链表,将群聊的消息发送给除了自己之外的所有人
	node_t *ptemp=phead;
	while(ptemp->next != NULL)
	{
		ptemp=ptemp->next;
		if(memcmp(&cin,&(ptemp->c_addr),sizeof(cin)))
		{
			if(sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&(ptemp->c_addr),sizeof(ptemp->c_addr)) == -1)
			{
				perror("sendto error");
				return -1;
			}
		}
	}
}

//退出
int do_quit(struct sockaddr_in cin,node_t *phead,int sfd,msg_t msg)
{
	//把要退出的客户端从链表中删除,并且把退出的消息转发给其他所有人
	node_t *ptemp=phead;
	while(ptemp->next != NULL)
	{
		if(memcmp(&cin,&(ptemp->next->c_addr),sizeof(cin)))
		{
			//不是自己,就发送数据
			ptemp=ptemp->next;
			if(sendto(sfd,&msg,sizeof(msg),0,(struct sockaddr*)&(ptemp->c_addr),sizeof(ptemp->c_addr)) == -1)
			{
				perror("sendto error");
				return -1;
			}
		}else {
			//如果是自己,就将自己在链表中删除
			node_t *pdel=ptemp->next;
			ptemp->next=pdel->next;
			free(pdel);
			pdel=NULL;
		}
	}
}

         客户端代码。

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <semaphore.h>
#include <wait.h>
#include <signal.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <semaphore.h>
#include <sys/msg.h>
#include <sys/shm.h>
#include <sys/un.h>
#include <poll.h>
#include <errno.h>
#include <sqlite3.h>

typedef struct msg {
    char code;//操作码   'L'登录   'C'群聊   'Q'退出
    char name[20];
    char txt[128];
}msg_t;

int main(int argc, const char *argv[])
{
	//创建套接字
	int cfd=socket(AF_INET,SOCK_DGRAM,0);
	if(-1 == cfd)
	{
		perror("socket error");
		return -1;
	}
	printf("socket success\n");

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

	msg_t msg;
	memset(&msg,0,sizeof(msg));
	printf("请输入用户名:");
	fgets(msg.name,20,stdin);
	msg.name[strlen(msg.name)-1]='\0';                                       

	//给服务器发送登录的数据包
	msg.code='L';
	strcpy(msg.txt,"加入群聊");
	if(sendto(cfd,&msg,sizeof(msg),0,(struct sockaddr*)&cin,cin_len) == -1)
	{
		perror("sendto error");
		return -1;
	}

	pid_t pid=fork();
	if(pid>0)
	{
		//父进程发送数据
		while(1)
		{
			msg.code='C';//先默认一开始的时候都是群聊
			fgets(msg.txt,128,stdin);
			msg.txt[strlen(msg.txt)-1]='\0';

			//判断是否是退出
			if(!strcmp(msg.txt,"quit"))
			{
				msg.code='Q';
				strcpy(msg.txt,"退出群聊");
			}

			if(sendto(cfd,&msg,sizeof(msg),0,(struct sockaddr*)&cin,cin_len) == -1)
			{
				perror("sendto error");
				return -1;
			}

			if(!strcmp(msg.txt,"退出群聊"))
			{
				break;
			}
		}
		//父进程结束前,先杀死子进程
		kill(pid,SIGKILL);
		wait(NULL);
		close(cfd);
	}else if(0 == pid)
	{
		//子进程接收服务器发来的数据并打印
		while(1)
		{
			memset(&msg,0,sizeof(msg));
			if(recvfrom(cfd,&msg,sizeof(msg),0,NULL,NULL) == -1)
			{
				perror("recvfrom error");
				return -1;
			}
			printf("%s发来消息-->%s\n",msg.name,msg.txt);
		}
	}else {
		perror("fork error");
		return -1;
	}

	return 0;
}

  • 19
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值