前言:最近在学进程间通信,所以做了一个小项目练习一下。主要用消息队列和socket(UDP)实现这个系统,并数据库存储数据,对C语言操作不熟悉的可以参照我的这篇博客:https://www.cnblogs.com/liudw-0215/p/9593414.html,所有代码提交我的Github上,地址:https://github.com/ldw0215/Chat-System.git,可以自行下载,然后make一下就可以了。

  一、架构解析

  主要有客户端和服务端,客户端发送请求,服务端回应请求,客户端实现的功能主要见下图:

  

  注册、登录使用消息队列进行通信的,聊天是通过socket(UDP)实现的!数据存在数据库中,需要一张数据表,建表数据语句如下:

  

CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(64) NOT NULL DEFAULT '',
`password` varchar(64) NOT NULL DEFAULT '',
`check` varchar(64) NOT NULL DEFAULT '',
PRIMARY KEY (`id`),
UNIQUE KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=52 DEFAULT CHARSET=utf8;

  二、客户端实现

  client.c创建不同的消息队列的键,根据不同的消息类型的进行发送,并等待服务端响应,client.c代码如下:

  

#include "my.h"

Msg m;
Msg_stoc msg_stoc; static int msgid_ctos;
static int msgid_stoc; void showmenu()
{
puts("-------CHAT----------");
puts("| 1:发送 2:接收 |");
puts("| 3:退出 |");
puts("--------------------");
} void show()
{
puts("-------CHAT----------");
puts("| 1:注册 2:登录 |");
puts("| 0:退出 |");
puts("--------------------");
} void send1()
{
printf("%s","send");
char buf[] = {'\0'};
char str[] = {'\0'};
struct sockaddr_in dui,zj;
int n;
short x;
int sockfd = socket(AF_INET,SOCK_DGRAM,);
if(sockfd < )
{
perror("socket");
exit(-); }
zj.sin_family = AF_INET;
zj.sin_port = htons();
zj.sin_addr.s_addr = htonl(INADDR_ANY);
n = bind(sockfd,(struct sockaddr *)&zj,sizeof(zj));
if(n < )
{
close(sockfd);
perror("bind");
exit(-); }
//puts("请输入对方号码 端口 IP ");
//scanf("%hd%s",&x,buf);
getchar();
dui.sin_addr.s_addr = inet_addr("10.10.3.129");
dui.sin_port = htons();
dui.sin_family = AF_INET;
puts("请输入想要发送的内容:");
//gets(str);
fgets(str,,stdin); n = sendto(sockfd,str,sizeof(str),,(struct sockaddr *)&dui,sizeof(dui));
if(n <= )
{
close(sockfd);
perror("sendto");
exit(-); }
close(sockfd);
return; }
#if 1
void asend(int sockfd,struct sockaddr_in dui)
{
char buf[] = {'\0'};
int n;
puts("请输入要回复的内容:");
fgets(buf,,stdin);
//gets(buf);
n = sendto(sockfd,buf,sizeof(buf),,(struct sockaddr*)&dui,sizeof(dui));
if(n <= )
{
perror("sendto");
close(sockfd);
exit(-); }
close(sockfd);
return ;
} void choose1(char ch ,int sockfd,struct sockaddr_in dui)
{
switch(ch)
{
case 'a':
asend(sockfd,dui);
break;
case 'n':
break;
default:
puts("input error!");
break; }
}
#endif
void recv1()
{
struct sockaddr_in dui,zj;
socklen_t len = sizeof(dui);
int n;
char buf[] = {'\0'};
char ch;
int sockfd = socket(AF_INET,SOCK_DGRAM,);
if(sockfd < )
{
perror("socket");
exit(-); }
zj.sin_family = AF_INET;
zj.sin_port = htons();
zj.sin_addr.s_addr = htonl(INADDR_ANY);
n = bind(sockfd,(struct sockaddr *)&zj,sizeof(zj));
if(n < )
{
close(sockfd);
perror("bind");
exit(-); }
n = recvfrom(sockfd,buf,sizeof(buf),,(struct sockaddr *)&dui,&len);
if(n <= )
{
close(sockfd);
perror("recvfrom");
exit(-);
}
puts(buf);
puts("是否要回复: 回复-》a,不回复-》n");
ch = getchar();
getchar();
choose1(ch,sockfd,dui);
} void choose(int ch)
{
printf("%d",ch);
switch(ch)
{
case :
m.type = ;
msgsnd(msgid_ctos,&m,sizeof(m)-sizeof(m.type),);
send1();
break;
case :
recv1();
break;
case :
exit();
break;
default:
puts("input error!");
break; }
} void regis()
{
printf("请输入姓名:");
scanf("%s",m.name); printf("请输入密码:");
scanf("%s",m.passwd);
m.type=;
} void login(void)
{
printf("请输入用户名:");
scanf("%s",&m.name); printf("请输入密码:");
scanf("%s",&m.passwd);
m.type=;
} int main(int argc ,char *argv[])
{
msgid_ctos=get_ctos_msg();
msgid_stoc=get_stoc_msg(); int temptype; while()
{
show();
int a = ;
scanf("%d",&a);
getchar();
switch(a)
{
case :return ;
case :regis();temptype=;break; //注册
case :login();temptype=;break; //登录
} int ret;
long type=; printf("name:%s,passwd:%s\n", m.name,m.passwd);
msgsnd(msgid_ctos,&m,sizeof(m)-sizeof(m.type),); sleep();
msgrcv(msgid_stoc,&msg_stoc,sizeof(msg_stoc),temptype,); if( == strcmp(msg_stoc.check,"yes"))
{
printf("%s",msg_stoc.info);
break;
}
} while()
{
showmenu();
int ch;
puts("请输入功能");
scanf("%d",&ch);
getchar();
printf("%d",ch);
choose(ch);
}
return ;
}

  注意,不同的通信,要用创建不同消息队列的键,并且消息类型也要不同!

  三、服务端实现

  服务端主要接送并响应客户端,主要创建不同的子进程,然后调用exec族函数,调用二进制文件,并通过消息队列接收阻塞执行,并建立信号,检测Ctrl+c信号,是进程退出,代码如下:

  

#include"my.h"

static pid_t sub_pid[];

static int msgid_ctos;
static int msgid_stoc; void sigint(int signum)
{
int i;
for(i=;i<;i++)
{
kill(sub_pid[i],SIGKILL);
} } int main(int argc,char*argv[])
{
signal(SIGINT,sigint); msgid_ctos=get_ctos_msg();
msgid_stoc=get_stoc_msg(); sub_pid[]=vfork();
if(==sub_pid[])
{
execl("register","register",NULL);
} sub_pid[]=vfork();
if(==sub_pid[])
{
execl("login","login",NULL);
} sub_pid[]=vfork();
if(==sub_pid[])
{
execl("chat","chat",NULL);
} wait(NULL);
return ;
}

  四、各模块及数据库解析

  数据库是通过数据库函数实现的,需要头文件<mysql.h>,并链上动态库-I/usr/include/mysql -L/usr/lib64/mysql -lmysqlclient,数据量比较小,之后还要考虑优化的问题;

  注册、登录、聊天都是不同的.c文件生成二进制实现的:

  注册通过消息队列接收用户名和密码存入数据库,代码如下:

  

#include"my.h"

Msg per;
Msg_stoc msg_stoc; static int msgid_ctos;
static int msgid_stoc; void open_cli()
{
MYSQL conn;
int res;
//MYSQL_RES * result;
//MYSQL_ROW row;
mysql_init(&conn); //第三、四和五个参数,需要自己修改一下
if (mysql_real_connect(&conn, "localhost", "root", "p@s#0fSPV", "pvault", , NULL, )) {
printf("coneect mysql successful\n");
char insert_query[];
//insert
memset(insert_query, , sizeof(insert_query));
strcat(insert_query, "insert into user(name,password) values('");
strcat(insert_query, per.name);
strcat(insert_query, "','");
strcat(insert_query, per.passwd);
strcat(insert_query, "')");
printf("SQL语句: %s\n", insert_query);
res = mysql_query(&conn, insert_query);
if (!res) {
printf("insert %lu rows\n", (unsigned long)mysql_affected_rows(&conn));
sprintf(msg_stoc.check,"%s","no");
}
else {
printf("insert error\n");
} }
} int main()
{
msgid_ctos = get_ctos_msg();
msgid_stoc = get_stoc_msg(); //int sockfd = socket_rcv();
while()
{
int n;
long type;
//per.type = 1;
msgrcv(msgid_ctos,&per,sizeof(per)-sizeof(type),,);
printf("name:%s,passwd:%s\n", per.name,per.passwd);
sleep();
open_cli();
//Msg m;
msg_stoc.type = ;
sprintf(msg_stoc.check,"%s","no");
printf("check:%s", msg_stoc.check);
msgsnd(msgid_stoc,&msg_stoc,sizeof(msg_stoc)-sizeof(type),);
}
}

  登录也是通过消息队列接收用户名和密码,并从查询出数据,进行对比,是否可以登录,代码如下:

  

#include"my.h"

Msg per;
Msg_stoc msg_stoc; static int msgid_ctos;
static int msgid_stoc; void login(void)
{
MYSQL conn;
int res;
MYSQL_RES * result;
MYSQL_ROW row;
mysql_init(&conn); //第三、四和五个参数,需要自己修改一下
if (mysql_real_connect(&conn, "localhost", "root", "p@s#0fSPV", "pvault", , NULL, )) {
printf("coneect mysql successful\n");
char select_query[] = {};
snprintf(select_query,,"select name,password from user where name='%s'",per.name);
printf("SQL语句: %s\n", select_query);
if (mysql_query(&conn, select_query) != ) {
fprintf(stderr, "查询失败\n");
exit();
}
else {
if ((result = mysql_store_result(&conn)) == NULL) {
fprintf(stderr, "保存结果集失败\n");
exit();
}
else {
while ((row = mysql_fetch_row(result)) != NULL) {
printf("name is %s , ", row[]);
printf("age is %s\n", row[]);
if(( == strcmp(row[],per.name)) && ( == strcmp(row[],per.passwd)))
strcpy(per.info,"login success!");
}
}
} }
} int main()
{
msgid_ctos = get_ctos_msg();
msgid_stoc = get_stoc_msg(); //int sockfd = socket_rcv();
while()
{
int n;
long type;
//per.type = 3;
msgrcv(msgid_ctos,&per,sizeof(per)-sizeof(type),,);
//printf("name:%s,passwd:%s\n", per.name,per.passwd);
sleep();
login();
//Msg m;
msg_stoc.type = ;
sprintf(msg_stoc.check,"%s","yes");
printf("check:%s", per.info);
msgsnd(msgid_stoc,&msg_stoc,sizeof(msg_stoc)-sizeof(type),);
}
}

  聊天是通过socket(UDP)实现的。

  总结:通过做这个小项目学到了很多,也发现许多不足,最重要的就是架构能力,之前都是做一小块,没有大局观,虽然项目小,但五张俱全,很锻炼人,继续找项目做!

  

用消息队列和socket实现聊天系统的更多相关文章

  1. 消息队列、socket(UDP)实现简易聊天系统

    前言: 最近在学进程间通信,所以做了一个小项目练习一下.主要用消息队列和socket(UDP)实现这个系统,并数据库存储数据,对C语言操作数据库不熟悉的可以参照我的这篇博客:https://www.c ...

  2. 【网络编程基础】Linux下进程通信方式(共享内存,管道,消息队列,Socket)

    在网络课程中,有讲到Socket编程,对于tcp讲解的环节,为了加深理解,自己写了Linux下进程Socket通信,在学习的过程中,又接触到了其它的几种方式.记录一下. 管道通信(匿名,有名) 管道通 ...

  3. 7月目标 socket , 一致性哈希算法 ; mongodb分片; 分布式消息队列; 中间件的使用场景

      分布式的基础:一致性哈希  路由算法的一致性hash http://www.jiacheo.org/blog/174 http://www.tuicool.com/articles/vQVbmai ...

  4. 8.7 进程间的通讯:管道、消息队列、共享内存、信号量、信号、Socket

    进程间的通讯 进程间为什么需要通讯? 共享数据.数据传输.消息通知.进程控制 进程间的通讯有哪些类型? 首先,联系前面讲过的知识,进程之间的用户地址空间是相互独立的,不能进行互相访问,但是,内核空间却 ...

  5. 【windows 操作系统】进程间通信(IPC)简述|无名管道和命名管道 消息队列、信号量、共享存储、Socket、Streams等

    一.进程间通信简述 每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进 ...

  6. Java消息队列--JMS概述

    1.什么是JMS JMS即Java消息服务(Java Message Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送 ...

  7. 消息队列性能对比——ActiveMQ、RabbitMQ与ZeroMQ(译文)

    Dissecting Message Queues 概述: 我花了一些时间解剖各种库执行分布式消息.在这个分析中,我看了几个不同的方面,包括API特性,易于部署和维护,以及性能质量..消息队列已经被分 ...

  8. 开源消息队列:NetMQ

    NetMQ 是  ZeroMQ的C#移植版本. ZeroMQ是一个轻量级的消息内核,它是对标准socket接口的扩展.它提供了一种异步消息队列,多消息模式,消息过滤(订阅),对多种传输协议的无缝访问. ...

  9. EQueue - 一个纯C#写的分布式消息队列介绍2

    一年前,当我第一次开发完EQueue后,写过一篇文章介绍了其整体架构,做这个框架的背景,以及架构中的所有基本概念.通过那篇文章,大家可以对EQueue有一个基本的了解.经过了1年多的完善,EQueue ...

随机推荐

  1. Prometheus监控⼊⻔简介

    文档目录: • prometheus是什么?• prometheus能为我们带来些什么• prometheus对于运维的要求• prometheus多图效果展示 1) Prometheus是什么pro ...

  2. bzoj 2759一个动态树好题

    真的是动态树好题,如果把每个点的父亲设成p[x],那么建出来图应该是一个环套树森林,拆掉一条边,就变成了动态树,考虑维护什么,对于LCT上每个节点,维护两组k和b,一组是他到他父亲的,一组是他LCT子 ...

  3. bzoj 2822 [AHOI2012]树屋阶梯 卡特兰数

    因为规定n层的阶梯只能用n块木板 那么就需要考虑,多出来的一块木板往哪里放 考虑往直角处放置新的木板 不管怎样,只有多的木板一直扩展到斜边表面,才会是合法的新状态,发现,这样之后,整个n层阶梯就被分成 ...

  4. 带着新人看java虚拟机07(多线程篇)

    这一篇说一下比较枯燥的东西,为什么说枯燥呢,因为我写这都感觉很无聊,无非就是几个阻塞线程的方法和唤醒线程的方法... 1.线程中断 首先我们说一说怎么使得一个正在运行中的线程进入阻塞状态,这也叫做线程 ...

  5. 流程控制之while循环

    目录 语法(掌握) while+break while+continue while循环的嵌套(掌握) tag控制循环退出(掌握) while+else(了解) 语法(掌握) 循环就是一个重复的过程, ...

  6. Spring+SpringMVC+MyBatis的pom.xml依赖

    <!-- 集中定义依赖版本号 --> <!-- 已经依据maven仓库给出的版本兼容信息,调节好合适的spring.mybatis-spring.mybatis.pagehelper ...

  7. Python:轻量级 ORM 框架 peewee 用法详解(二)——增删改查

    说明:peewee 中有很多方法是延时执行的,需要调用 execute() 方法使其执行.下文中不再特意说明这个问题,大家看代码. 本文中代码样例所使用的 Person 模型如下: class Per ...

  8. OSPF 高级实验

    一.环境准备 1. 软件:GNS3 2. 路由:c7200 二.实验操作 实验要求: 1.理解 OSPF 虚链路原理及何时需要使用虚链路. 2.掌握 OSPF 虚链路配置方法. 3.掌握 OSPF 的 ...

  9. BFPRT算法

    解决的问题:在一个数组中找到最小的k个数 常规解法:1.排序,输出前k个数,时间复杂度O(n*log(n)). 2.利用一个大小为k的大根堆,遍历数组维持大根堆,最后返回大根堆就可以了,时间复杂度O( ...

  10. 为什么HTTPS比HTTP更安全?

    摘要: 理解HTTPS. 作者:浪里行舟 Fundebug经授权转载,版权归原作者所有. 前言 近几年,互联网发生着翻天覆地的变化,尤其是我们一直习以为常的HTTP协议,在逐渐的被HTTPS协议所取代 ...