2-4 建立能持续处理请求的C/S程序
0-前言
【C++百万并发网络通信】系列是跟着【张远东】老师的视频来复现的
希望能通过博客的方式不断坚持学习,也希望偶然间看到这篇博客的你也能一起加油!
笔记目录:【C++百万并发网络通信-笔记目录】
1-步骤说明
相较于【简易的TCP C/S结构】,【1.1】版本将要实现Server循环接收客户端请求(recv)并且处理请求(strcmp)
2-服务端1.1
#define WIN32_LEAN_AND_MEAN
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include<Windows.h>
#include<WinSock2.h>
#include<iostream>
using namespace std;
#pragma comment(lib, "ws2_32.lib")//加入静态链接库
int main()
{
WORD ver = MAKEWORD(2, 2);//WORD版本号
WSADATA dat;//一种数据结构
//启动windows socket 2.x环境
WSAStartup(ver, &dat);
//-------------------
//--建立简易TCP服务端
// 1 建立socket
SOCKET _sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
// 2 绑定端口 bind
sockaddr_in _sin = {
};//网络端口地址
_sin.sin_family = AF_INET;
_sin.sin_port = htons(4567);//host to net unsigned short,服务器使用该端口进行监听
_sin.sin_addr.S_un.S_addr = INADDR_ANY;//随意ip地址//inet_addr("127.0.0.1");//本机地址,防止外网访问
if (SOCKET_ERROR == bind(_sock, (sockaddr*)&_sin, sizeof(_sin)))
{
cout << "Error:绑定用于接收客户端连接的网络端口失败" << endl;
}
else
{
cout << "Success:绑定网络端口成功..." << endl;
}
// 3 监听端口 listen
if (SOCKET_ERROR == listen(_sock, 5))
{
cout << "Error:监听网络端口失败" << endl;
}
else
{
cout << "Success:监听网络端口成功..." << endl;
}
// 4 等待客户端连接 accept
sockaddr_in clientAddr = {
};//远程客户端地址
int nAddrLen = sizeof(clientAddr);//结构长度
SOCKET _csock = INVALID_SOCKET;//无效的socket地址
_csock = accept(_sock, (sockaddr*)&clientAddr, &nAddrLen);//新加入的客户端socket
if (INVALID_SOCKET == _csock)
{
cout << "Error:接收到无效客户端socket..." << endl;
}
cout << "新客户端加入:socket = " << (int)_csock << " , IP = " << inet_ntoa(clientAddr.sin_addr) << endl;
while (true)
{
//5 接收客户端数据
char _recvBuf[128] = {
};
int nLen = recv(_csock, _recvBuf, 128, 0);//返回客户端发送的数据长度
if (nLen <= 0)
{
cout << "客户端已退出,任务结束!" << endl;
break;
}
cout << "收到命令: " << _recvBuf << endl;
//6 处理客户端请求,按照请求向客户端发送数据
if (0 == strcmp(_recvBuf, "getName"))
{
char msgBuf[] = "Xiao Qiang";
send(_csock, msgBuf, strlen(msgBuf) + 1, 0);
}
else if (0 == strcmp(_recvBuf, "getAge"))
{
char msgBuf[] = "20";
send(_csock, msgBuf, strlen(msgBuf) + 1, 0);
}
else
{
char msgBuf[] = "???";
send(_csock, msgBuf, strlen(msgBuf) + 1, 0);
}
}
// 7 关闭socket closesocket
closesocket(_sock);
//-------------------
//清除Windows socket环境
WSACleanup();//关闭windows socket网络环境
cout << "已退出,任务结束..." << endl;
system("pause");
return 0;
}
这里相比于上一个版本【简易的TCP C/S结构】,将【等待客户端连接】移除while循环,因此造成服务端只能接入一台客户端,并且这台客户端退出后,服务端不能再接受新的客户端
【循环接收客户端信息】:
在while循环中,加入recv()
函数来接收客户端发送的数据,注意当客户端退出时,还会向服务端发送一条数据,此时就是nLen<=0
的情况。
【循环处理客户端请求】:
将客户端发送的数据与已有的命令getName
、getAge
通过strcmp函数进行对比,对比成功的请求会返回相应的数据
strcmp函数
是string compare(字符串比较)的缩写,用于比较两个字符串并根据比较结果返回整数。基本形式为strcmp(str1,str2),若str1=str2,则返回零;若str1<str2,则返回负数;若str1>str2,则返回正数。
3-客户端1.1
#define WIN32_LEAN_AND_MEAN
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include<Windows.h>
#include<WinSock2.h>
#include<iostream>
using namespace std;
#pragma comment(lib, "ws2_32.lib")//加入静态链接库
int main()
{
WORD ver = MAKEWORD(2, 2);//WORD版本号
WSADATA dat;//一种数据结构
//启动windows socket 2.x环境
WSAStartup(ver, &dat);
//-------------------
//--建立简易TCP客户端
// 1 建立socket
SOCKET _sock = socket(AF_INET, SOCK_STREAM, 0);//0:不规定协议类型
if (INVALID_SOCKET == _sock)
cout << "Error:建立Socket失败!" << endl;
else
cout << "建立Socket成功..." << endl;
// 2 连接服务器 connect
sockaddr_in _sin = {
}; //能够将结构体快速初始化
_sin.sin_family = AF_INET;
_sin.sin_port = htons(4567);//客户端想要连接服务器的哪个端口
_sin.sin_addr.S_un.S_addr = inet_addr("127.0.0.1");//连接服务器的ip地址,127.0.0.1是本机地址
int res = connect(_sock, (sockaddr*)&_sin, sizeof(sockaddr_in));
if (SOCKET_ERROR == res)
cout << "Error:连接失败!" << endl;
else
cout << "连接成功..." << endl;
while (true)
{
//3 输入请求命令
char cmdBuf[128] = {
};
cin >> cmdBuf;
//4 处理请求
if (0 == strcmp(cmdBuf, "exit"))
{
cout << "收到exit命令,任务结束..." << endl;
break;
}
//5 向服务器发送请求
send(_sock, cmdBuf, strlen(cmdBuf) + 1, 0);
// 6 接收服务器信息 recv
char recvBuf[128] = {
};//接收数据缓冲区
int nLen = recv(_sock, recvBuf, 128, 0);//recv()返回接收数据的长度
if (nLen > 0)
cout << "接收到数据:" << recvBuf << endl;
}
// 7 关闭socket closesocket
closesocket(_sock);
//-------------------
//清除Windows socket环境
WSACleanup();//关闭windows socket网络环境
cout << "已退出。" << endl;
system("pause");
return 0;
}
【循环输入请求】
由用户输入命令到cmdBuf
字符串中,exit
命令可以直接退出客户端
【循环发送请求】
将cmdBuf
中的命令发送给客户端套接字_csock