1、客户端连接聊天机器人服务器。
2、消息发送:客户端发送消息给机器人服务器。
3、消息接收:客户端接收到机器人服务器发送给他的消息。
4、可以有多个客户端同时连接。
5、智能回复功能:根据用户发送的消息内容,稍微有点智能回复。
1、客户机需要连接到服务器后,才能发送消息给服务器,所以需要使用connect到服务器的ip地址;服务器需先listen到客户的请求,然后,然后在accept到客户机的ip地址,最后在相互收发信息。
2、客户机与服务器之间的消息发送采用一问一答形式,客户机询问一句,服务器打一句;关于智能回复功能使用的是C++文件读取,读取本地的智能语言库,进而实现智能回答。
3、采用listen对多个用户进行接入,限制最大连接客户数为5,采用多线程实现多个客户与服务器进行通信。
4、开发环境与开发工具的选择:
开发环境:Windows10
开发工具:Visual Studio 2019
网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。
建立网络通信连接至少要一对端口号(socket)。socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口;HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。
Socket的英文原义是“孔”或“插座”。作为BSD UNIX的进程通信机制,取后一种意思。通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。在Internet上的主机一般运行了多个服务软件,同时提供几种服务。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。Socket正如其英文原义那样,像一个多孔插座。一台主机犹如布满各种插座的房间,每个插座有一个编号,有的插座提供220伏交流电, 有的提供110伏交流电,有的则提供有线电视节目。 客户软件将插头插到不同编号的插座,就可以得到不同的服务。
TCP和UDP是OSI模型中的运输层中的协议。TCP提供可靠的通信传输,而UDP则常被用于让广播和细节控制交给应用的通信传输。
TCP:
TCP充分实现了数据传输时各种控制功能,可以进行丢包的重发控制,还可以对次序乱掉的分包进行顺序控制。而这些在UDP中都没有。此外,TCP作为一种面向有连接的协议,只有在确认通信对端存在时才会发送数据,从而可以控制通信流量的浪费。TCP通过检验和、序列号、确认应答、重发控制、连接管理以及窗口控制等机制实现可靠性传输。
UDP:
UDP不提供复杂的控制机制,利用IP提供面向无连接的通信服务。并且它是将应用程序发来的数据在收到的那一刻,立刻按照原样发送到网络上的一种机制。即使是出现网络拥堵的情况下,UDP也无法进行流量控制等避免网络拥塞的行为。此外,传输途中如果出现了丢包,UDO也不负责重发。甚至当出现包的到达顺序乱掉时也没有纠正的功能。如果需要这些细节控制,那么不得不交给由采用UDO的应用程序去处理。换句话说,UDP将部分控制转移到应用程序去处理,自己却只提供作为传输层协议的最基本功能。UDP有点类似于用户说什么听什么的机制,但是需要用户充分考虑好上层协议类型并制作相应的应用程序。
Client/Server结构(C/S结构)是大家熟知的客户机和服务器结构。它是软件系统体系结构,通过它可以充分利用两端硬件环境的优势,将任务合理分配到Client端和Server端来实现,降低了系统的通讯开销。目前大多数应用软件系统都是Client/Server形式的两层结构,由于现在的软件应用系统正在向分布式的Web应用发展,Web和Client/Server 应用都可以进行同样的业务处理,应用不同的模块共享逻辑组件;因此,内部的和外部的用户都可以访问新的和现有的应用系统,通过现有应用系统中的逻辑可以扩展出新的应用系统。这也就是目前应用系统的发展方向。
1、WinSock头文件以及Socket库的引入#include
#pragma comment(lib,“ws2_32.lib”)//链接ws2_32.lib库文件到此项目中
2、Socket库的初始化,创建客服端和服务端Socket,以及客户端和服务端的地址包的初始化
3、绑定IP地址、端口等信息到socket上,用函数bind();* 可选。
4、设置要连接的对方的IP地址和端口等属性;
5、连接服务器,用函数connect();
6、收发数据,用函数send()和recv(),或者read()和write();
7、关闭网络连接,使用函数closesocket();
1、WinSock头文件以及Socket库的引入#include
#pragma comment(lib,“ws2_32.lib”)//链接ws2_32.lib库文件到此项目中
2、Socket库的初始化,创建客户端和服务端Socket,以及客户端和服务端的地址包的初始化
3、创建一个socket,用函数socket();
4、设置socket属性,用函数setsockopt(); * 可选
5、绑定IP地址、端口等信息到socket上,用函数bind();
6、开启监听,用函数listen();
7、接收客户端上来的连接,用函数accept();
8、收发数据,用函数send()和recv(),或者read()和write();
9、关闭网络连接,使用函数closesocket();
10、关闭监听;
1、建立一个info.txt文件作为智能回复语言库。
2、当服务器接收到客户机发来的消息时,先与智能语言库中的语言进行匹配,若匹配,则发送txt文件中已写好的回复消息给客户机;若不匹配,则进行服务端实行人工回复。
注:每次匹配到一个智能语言或读取到文件末尾时,文件流指针需返回文件首,这样方便下一次匹配。
3、通话结束,关闭info.txt文件。
4、效果展示:
1、编写线程函数DWORD WINAPI ServerThread(LPVOID lpParameter)/服务器线程/,实现单个客户的智能回复
2、在主函中调用CreateThread(NULL, 0, &ServerThread,sockCli, 0, NULL);这个多线程函数实现多个用户接入到服务端实现通信聊天
3、效果展示:
#include
#include
#include
#include
using namespace std;
#include //WinSocket库
#pragma comment(lib,"ws2_32.lib") //链接ws2_32.lib库文件
/*===================全局变量==================*/
const int BUF_SIZE = 2048;
SOCKADDR_IN addrSer, addrCli; //address
int naddr = sizeof(SOCKADDR_IN); //服务器长度
char sendbuf[BUF_SIZE] = {0}; //发送数据
char recvbuf[BUF_SIZE] = {0}; //接送数据
/*=============================================*/
/*
LPVOIP的类型是void *
DWOERD类型是unsigned long
*/
DWORD WINAPI ServerThread(LPVOID lpParameter)/*服务器线程*/
{
SOCKET *sockCli = (SOCKET *)lpParameter;
ifstream in("info.txt", ifstream::in);//读取文件
//ofstream out("info.txt", ifstream::out | ifstream::app);//写入文件
if (!in)
{
cout << "智能回复语言库打开失败!" << endl;
system("pause");
return -1;
}
while (1)
{
int receByt = recv(*sockCli, recvbuf, sizeof(recvbuf), 0);
if (receByt > 0)
cout << "客户:" << recvbuf << "\t端口号为:" << *sockCli << endl;
else
{
cout << "接收消息结束!" << endl;
break;
}
if (recvbuf[0] == 'n' || recvbuf[0] == 'N')
{
cout << "客户" << *socket << "已下线!" << endl;
MessageBox(NULL, "客户端程序关闭",LPCSTR(*socket), MB_OK);
break;
}
//cout << inet_ntoa(addrCli.sin_addr) << ":" << recvbuf << endl;
string line;
while (getline(in, line))
{
const char *temp = line.c_str();
if (temp[0] == 'Q')
{
char szTemp[BUF_SIZE] = { 0 };
strcpy(szTemp, temp + 2);
if (strcmp(szTemp, recvbuf) == 0)
{
getline(in, line);
temp = line.c_str();
//send(sockCli, temp + 2, strlen(temp)-1, 0);
strcpy_s(sendbuf, temp + 2);
//in.set_rdbuf(0);
in.clear();
streampos sp = in.tellg();//文件的大小
in.seekg(-sp, ios::cur);//文件指针返回第一行
sp = in.tellg();
break;
}
}
if (temp[0] == '#')
{
cout << "您说的东西我听不懂,我请主人为您解答!" << endl;
//in.set_rdbuf(0);
in.clear();
streampos sp = in.tellg();
in.seekg(-sp, ios::cur);
break;
}
if (temp[0] == 'A')
continue;
}
/*清空字符缓存*/
memset(recvbuf, 0, sizeof(recvbuf));
/*服务器回复消息*/
if (strlen(sendbuf) != 0)
{
cout << "服务器:" << sendbuf << endl;
}
else
{
cout << "服务器:";
cin >> sendbuf;
cin.clear();
/*写入智能语言库*/
//out << "Q:" << recvbuf << endl;
//out << "A:" << sendbuf << endl;
}
int k=send(*sockCli, sendbuf, sizeof(sendbuf), 0);
if (k < 0) {
cout << "发送失败" << endl;
}
memset(sendbuf, '\0', sizeof(sendbuf));
}//while
/*关闭智能语言库 */
//out.close();
in.close();
closesocket(*sockCli);
free(sockCli);
return 0;
}
int main()
{
system("color 05");
/*初始化socket库*/
WSADATA wsadata;
if (0 != WSAStartup(MAKEWORD(2, 2), &wsadata))
{
cout << "socket库载入失败!" << endl;
system("pause");
return -1;
}
else cout << "socket库载入成功!" << endl;
/*创建套接字socket*/
SOCKET sockSer = socket(AF_INET, SOCK_STREAM, 0);//服务器套接字
//SOCKET sockCli; //客服端套接字
/*套接字的初始化*/
//addrSer.sin_addr.S_un.S_addr = INADDR_ANY; //所有ip地址
addrSer.sin_addr.s_addr = inet_addr("192.168.43.65");
addrSer.sin_family = AF_INET; //IP地址族
addrSer.sin_port = htons(1111); //端口号
/*套接字绑定ip地址*/
bind(sockSer, (SOCKADDR *)& addrSer, sizeof(SOCKADDR));
/*开始监听来自客户的服务请求,最多连接5个客户*/
listen(sockSer, 5);
/*进入监听状态,接收客服机发送请求*/
cout << "服务器开启成功,等待客户连接..." << endl;
while (1)
{
SOCKET *sockCli = new SOCKET;
*sockCli= accept(sockSer, (SOCKADDR *)& addrCli, &naddr);
cout << "一个服务器已连接客户端,端口号为:" << *sockCli << endl;
CreateThread(NULL, 0, &ServerThread,sockCli, 0, NULL);
}
/*关闭套接字接口*/
closesocket(sockSer);
//closesocket(sockCli);
WSACleanup();
}
#include
#include
#include //WindowsSocket编程头文件
#include
#include
#pragma comment(lib,"ws2_32.lib")//链接ws2_32.lib库文件到此项目中
using namespace std;
//================全局常量==================
//创建缓冲区
const int BUF_SIZE = 2048;
//================全局变量==================
SOCKET sockSer, sockCli; //套接字
SOCKADDR_IN addrSer, addrCli; //address client,service
int naddr = sizeof(SOCKADDR_IN); //
char zh[] = "好的,结束通话!";
char sendbuf[BUF_SIZE]; //发送数据
char recvbuf[BUF_SIZE]; //接收数据
int main() {
//加载socket库
system("color 03");
cout << "客户端启动" << endl;
WSADATA wsadata;
if (WSAStartup(MAKEWORD(2, 2), &wsadata) != 0)
{
//输出出错信息
cout << "载入socket库失败!" << endl;
system("pause");
return 0;
}
else {
cout << "载入socket库成功!" << endl;
}
//创建Soucket;
/*
函数原型:SOCKET socket(int af,int type,int protocol);
af:int类型,代表套接字使用的那种网络地址,对于IP地址族,该参数一般
*/
sockCli = socket(AF_INET, SOCK_STREAM, 0);
//描述协议族,INET属于ipv4;
//sock_stream创建套接字类型:tcp;
//0不指定协议,常用的协议有tcp、udp等
//初始化客户端地址包
addrCli.sin_addr.s_addr = inet_addr("127.0.0.1");
addrCli.sin_family = AF_INET;
addrCli.sin_port = htons(1111);
//初始化服务器地址
addrSer.sin_addr.s_addr = inet_addr("192.168.43.65");
addrSer.sin_family = AF_INET;
addrSer.sin_port = htons(1111);
while (true)
{
cout << "开始连接..." << endl;
/*connect到服务端*/
if (connect(sockCli, (SOCKADDR *)& addrSer, sizeof(addrSer)) != SOCKET_ERROR)
{
cout << "客户端连接成功" << endl;
cout << "欢迎来到聊天室,输入n结束聊天!" << endl;
while (true)
{
//发送给服务器信息
cout << "客户:";
cin >> sendbuf;
cout << "等待服务器响应..." << endl;
/*客户端发送信息*/
send(sockCli, sendbuf, sizeof(sendbuf), 0);
/*接收服务器发来的消息*/
recv(sockCli, recvbuf, sizeof(recvbuf), 0);
if (recvbuf == zh)/*结束通话*/
{
MessageBox(NULL, "客户端程序关闭", "客户端", MB_OK);
return 0;
}
cout << "服务器:" << recvbuf << endl;
}
}
else
{
cout << "客户端连接失败" << endl;
}
}
closesocket(sockSer);
closesocket(sockCli);
WSACleanup();
return 0;
}