前言
在上一篇中Socket网络通信基础(一):简易TCP服务端和客户端 server使用while循环来持续accept新的client连接 但是client连接上server后,在recv接收到server消息后,就退出了
本篇优化方向 1、让client可以发送自定义的消息,并在收到server消息后继续循环等待新的指令 2、server和client使用结构化的网络消息数据交互
一、优化内容
1、新增结构化消息DataPackage
struct DataPackage
{
int age;
char name[32];
};
2、server的while优化
根据收到的client命令向client发送不同的消息
while (true)
{
int nLen = recv(sock_client, recvBuf, 128, 0);
if (nLen <= 0)
{
printf("客户端已退出,任务结束。\n");
break;
}
printf("收到命令:%s \n", recvBuf);
if (0 == strcmp(recvBuf, "getInfo"))
{
DataPackage dp = { 80,"张三" };
send(sock_client, (const char*)&dp, sizeof(DataPackage), 0);
}
else
{
char msgBuf[] = "???.";
send(sock_client, msgBuf, strlen(msgBuf) + 1, 0);
}
}
3、client持续send逻辑
- 使用scanf来接收用户的命令输入
- 根据用户输入的指令分别执行退出、发送消息
- 根据server返回的消息内容转换成DataPackage结构体指针并打印输出
while (true)
{
char cmdBuf[128] = {};
scanf("%s", cmdBuf);
if (0 == strcmp(cmdBuf, "exit"))
{
printf("收到exit命令,任务结束。\n");
break;
}
else
{
send(sock_client, cmdBuf, strlen(cmdBuf) + 1, 0);
}
char recvBuf[128] = {};
int nLen = recv(sock_client, recvBuf, 128, 0);
if (nLen > 0)
{
DataPackage* dp = (DataPackage*)recvBuf;
printf("接收到数据:年龄=%d,姓名=%s \n", dp->age, dp->name);
}
}
二、测试结果
- 这里我们还发现个问题
当消息内容getInfo正确的时候,是可以正常解析的 但是当内容错误的话,client依然会收到server返回的消息 这时候再解析成DataPackage就出问题 这个后续我们再解决
三、server是如何知道client退出的
首先TCP协议的连接是经过三次握手,断开是经过四次握手(更具体大家可以查资料) 我们可以在client的closesocket处断点,然后在client中输入命令exit 这时候可以看到当client的closesocket执行后,server就已经收到recv的nLen=0 客户端退出
四、完整源码
1、server.cpp
#define WIN32_LEAN_AND_MEAN
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <Windows.h>
#include <WinSock2.h>
#include<stdio.h>
#pragma comment(lib,"ws2_32.lib")
struct DataPackage
{
int age;
char name[32];
};
int main()
{
WORD ver = MAKEWORD(2, 2);
WSADATA dat;
WSAStartup(ver, &dat);
SOCKET sock_server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == sock_server)
{
printf("socket 创建失败\n");
WSACleanup();
return -1;
}
sockaddr_in _sin = {};
_sin.sin_family = AF_INET;
_sin.sin_port = htons(4567);
_sin.sin_addr.S_un.S_addr = INADDR_ANY;
if (SOCKET_ERROR == bind(sock_server, (sockaddr*)&_sin, sizeof(_sin)))
{
printf("ERROR,绑定网络端口失败...\n");
closesocket(sock_server);
WSACleanup();
return -1;
}
else
{
printf("绑定网络端口成功...\n");
}
if (SOCKET_ERROR == listen(sock_server, 5))
{
printf("ERROR,监听网络端口失败...\n");
closesocket(sock_server);
WSACleanup();
return -1;
}
else
{
printf("监听网络端口成功...\n");
}
sockaddr_in clientAddr = {};
int nAddrLen = sizeof(clientAddr);
SOCKET sock_client = INVALID_SOCKET;
sock_client = accept(sock_server, (sockaddr*)&clientAddr, &nAddrLen);
if (INVALID_SOCKET == sock_client)
{
printf("错误,接受到无效客户端SOCKET...\n");
closesocket(sock_server);
WSACleanup();
return -1;
}
printf("新客户端加入:IP = %s \n", inet_ntoa(clientAddr.sin_addr));
char recvBuf[128] = {};
while (true)
{
int nLen = recv(sock_client, recvBuf, 128, 0);
if (nLen <= 0)
{
printf("<nLen=%d>,客户端已退出,任务结束。\n", nLen);
break;
}
printf("收到命令:%s \n", recvBuf);
if (0 == strcmp(recvBuf, "getInfo"))
{
DataPackage dp = { 80,"张三" };
send(sock_client, (const char*)&dp, sizeof(DataPackage), 0);
}
else
{
char msgBuf[] = "???.";
send(sock_client, msgBuf, strlen(msgBuf) + 1, 0);
}
}
closesocket(sock_server);
WSACleanup();
printf("已退出,任务结束。\n");
getchar();
return 0;
}
2、client.cpp
#define _CRT_SECURE_NO_WARNINGS
#define WIN32_LEAN_AND_MEAN
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <Windows.h>
#include <WinSock2.h>
#include<stdio.h>
#pragma comment(lib,"ws2_32.lib")
struct DataPackage
{
int age;
char name[32];
};
int main()
{
WORD ver = MAKEWORD(2, 2);
WSADATA dat;
WSAStartup(ver, &dat);
SOCKET sock_client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (INVALID_SOCKET == sock_client)
{
printf("socket 创建失败\n");
WSACleanup();
return -1;
}
sockaddr_in _sin = {};
_sin.sin_family = AF_INET;
_sin.sin_port = htons(4567);
_sin.sin_addr.s_addr = inet_addr("127.0.0.1");
int ret = connect(sock_client, (sockaddr*)&_sin, sizeof(_sin));
if (SOCKET_ERROR == ret)
{
printf("错误,连接服务器失败...\n");
closesocket(sock_client);
WSACleanup();
return -1;
}
else
{
printf("连接服务器成功...\n");
}
while (true)
{
char cmdBuf[128] = {};
scanf("%s", cmdBuf);
if (0 == strcmp(cmdBuf, "exit"))
{
printf("收到exit命令,任务结束。\n");
break;
}
else
{
send(sock_client, cmdBuf, strlen(cmdBuf) + 1, 0);
}
char recvBuf[128] = {};
int nLen = recv(sock_client, recvBuf, 128, 0);
if (nLen > 0)
{
DataPackage* dp = (DataPackage*)recvBuf;
printf("接收到数据:年龄=%d,姓名=%s \n", dp->age, dp->name);
}
}
closesocket(sock_client);
WSACleanup();
printf("已退出,任务结束。\n");
getchar();
return 0;
}
|