任务目标
- 聊天器采用客户端/服务器(C/S)模式;
- 客户端利用UDP与服务器相连,客户端与客户端之间通过UDP相互通信;
- 服务器端具有服务器端口设置维护客户端个人信息,记录客户端状态,分配账号等;
- 客户端具有服务器地址及端口设置,用户注册,用户登陆,添加好友和删除好友,查看好友信息,给好友发送消息等功能;
- 服务器与客户端间、客户端之间的交互采用控制台方式或GUI窗口方式均可;
完成情况
采用了客户器/服务器模式,实现了基于UDP客户端之间的相互通信,其优点有:在服务器端具有维护客户端个人信息,记录客户端状态,分配账号,服务器地址和端口的配置等。客户端上也实现了,地址及端口的设置用户注册和用户登录,添加好友及删除好友,查看好友是否在线,给好友发送消息等。但是不足的是,我们没有设计GUI窗口界面没有更加美观,而是用简洁的代码直接在运行中显示菜单栏;没有做出我们理想中的黑名单,我们本意上是打算实现黑名单功能,设置成黑名单的人不能发消息给我,除了黑名单的都可以发,只允许好友发,这个打算因为能力有限,并没有实现;接收缓冲区有延迟,处理不及时,需要先接收完上次数据才可以继续接受数据;群聊功能没有实现,只能私聊单个客户端对客户端的通信;发送和接受不能以多线程的方式同时进行,消息需要我们主动去接受。
运行效果图
服务器菜单:
服务器端口配置:
查看当前所有账户:
开启服务器:
客户端菜单:
修改IP和端口号:
注册账号:
登录:
查看好友列表(空):
ID1003添加好友:
查看1003和1004好友:
状态值为1为在线,0为离线:
1004发送信息至1003:
1003接受信息:
删除好友:
理论基础
UDP是OSI 参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务, UDP在IP报文的协议号是17。UDP协议的主要作用是将网络数据流量压缩成数据包的形式。一个典型的数据包就是一个二进制数据的传输单位。每一个数据包的前8个字节用来包含报头信息,剩余字节则用来包含具体的传输数据。
UDP报文没有可靠性保证、顺序保证和流量控制字段等,可靠性较差。但是正因为UDP协议的控制选项较少,在数据传输过程中延迟小、数据传输效率高,适合对可靠性要求不高的应用程序,或者可以保障可靠性的应用程序,如DNS、TFTP、SNMP等。
客户端/服务器(C/S)模式结构的基本原则是将计算机应用任务分解成多个子任务,由多台计算机分工完成,即采用“功能分布”原则。客户端完成数据处理,数据表示以及用户接口功能;服务器端完成DBMS(数据库管理系统)的核心功能。这种客户请求服务、服务器提供服务的处理方式是一种新型的计算机应用模式。
工作原理
采用客户端/服务器(C/S)模式;客户端利用UDP与服务器连接,客户端与客户端之间通过UDP互相通讯;服务器端具有服务器端口设置,维护客户端个人信息,记录客户端状态,分配账号等功能。客户端具有服务器地址及端口设置,用户注册,用户登陆,添加好友和删除好友,查看好友信息,给好友发送消息等功能;服务器与客户端间、客户端之间的交互采用控制台方式方式。主要是用规定好的格式发送字符信息。
核心代码
服务器端:
data.h
#include <Winsock2.h>
struct Friends
{
int id;
int ava; //是否有效 1 有效 ,0 无效
};
struct User
{
int id;
char name[15];
int online; //是否在线 1 在线 ,0 离线
char passwd[15];
sockaddr_in addr;
Friends friends[50];
};
server.h
#include "data.h"
#include <string>
using namespace std;
//#include "net.cpp"
//net.cpp
void serverStart(User user[], int po);
//user.cpp
void saveFile(User user[]);
void readFile(User user[]);
int login(User user[], char str[]);
int reg(User user[], char str[]);
string watch(User user[], char str[]);
int delF(User user[], char str[]);
int addF(User user[], char str[]);
user.cpp
#include "data.h"
#include <string.h>
#include <stdio.h>
#include <string>
using namespace std;
//#include <stdio.h>
//从文件读取
void readFile(User user[])
{
}
//保存到文件
void saveFile(User user[])
{
}
//用户登录判断
int login(User user[], char str[])
{
int id;
char passwd[15];
//char passwd1[15];
sscanf(str, "%d %[^'\0']", &id, passwd); //接收用户发送的id 和 密码
//sprintf(passwd,"%s'\0'",passwd1);
printf("登录判断:%d %s \n", id, passwd);
//int size = sizeof(user) / sizeof(user[0]);
for (int i = 0; user[i].id != -1; i++)
{
printf("当前 id : %d %s \n", user[i].id, user[i].passwd);
if (user[i].id == id)
{ //id匹配判断
printf("id == id : %d %s \n", user[i].id, user[i].passwd);
if (strcmp(user[i].passwd, passwd) == 0)
{ //密码匹配判断
user[i].online = 1;
return i;
}
else
return -1;
//return i;
}
}
return -1;
}
//用户注册
int reg(User user[], char str[])
{
//User p;
//int id = 1000;
char name[15];
char passwd[15];
sscanf(str, "%s %s", name, passwd); //读取用户名 密码
for (int i = 0; user[i].id != -1; i++) //找到user表最后一个(id=-1)
{