C/C++ 实现Socket交互式服务端
在 Windows 操作系统中,原生提供了强大的网络编程支持,允许开发者使用 Socket API 进行网络通信,通过 Socket API,开发者可以创建、连接、发送和接收数据,实现网络通信。本文将深入探讨如何通过调用原生网络 API 实现同步远程通信,并介绍了一个交互式 Socket 类的封装,提升了编写交互式服务器的便利性。
1. 交互式套接字类
为了更好地利用原生网络 API,我们引入了一个交互式 Socket 类的封装。这个类抽象了底层的网络细节,提供了简单而强大的接口,使得服务器端的交互式功能更容易实现。我们将详细介绍这个类的设计和使用方法。
MySocket
类是一个 C++ 套接字类,封装了在 Windows 平台上使用原生网络 API 进行同步远程通信的基本功能,该类需要使用多字节编码模式,服务端与客户端均需要引入此类,在项目头文件中均需要新建MySocket.hpp
文件。
完整代码如下所示;
#pragma once
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")
class MySocket
{
protected:
SOCKET m_hSocket;
public:
// 获取对端Socket用户IP端口等
BOOL GetPeerName(char* rSocketAddress, UINT& rSocketPort)
{
sockaddr_in name = { AF_INET };
int lenname = sizeof(name);
if (getpeername(m_hSocket, (sockaddr*)&name, &lenname) < 0)
return false;
strcpy(rSocketAddress, inet_ntoa(name.sin_addr));
rSocketPort = htons(name.sin_port);
return true;
}
// 获取本机Socket用户IP端口等
BOOL GetSockName(char* rSocketAddress, UINT& rSocketPort)
{
sockaddr_in name = { AF_INET };
int lenname = sizeof(name);
if (getsockname(m_hSocket, (sockaddr*)&name, &lenname) < 0)
return false;
strcpy(rSocketAddress, inet_ntoa(name.sin_addr));
rSocketPort = htons(name.sin_port);
return true;
}
// 获取当前用户SocketID
BOOL GetSocketID()
{
return m_hSocket;
}
// 创建套接字
BOOL Create(UINT nSocketPort = 0, int nSockType = SOCK_STREAM, LPCTSTR lpszSocketAddress = NULL)
{
// 创建套接字
m_hSocket = socket(AF_INET, nSockType, 0);
if (m_hSocket == INVALID_SOCKET)
return false;
// 设置IP地址和端口
sockaddr_in sa = { AF_INET };
sa.sin_port = htons(nSocketPort);
if (lpszSocketAddress)
sa.sin_addr.s_addr = inet_addr(lpszSocketAddress);
// 绑定套接字和IP地址端口
return !bind(m_hSocket, (sockaddr*)&sa, sizeof(sa));
}
// 接受客户请求
BOOL Accept(MySocket& rConnectedSock, LPSTR szIp = NULL, UINT* nPort = NULL)
{
sockaddr_in sa = { AF_INET };
int nLen = sizeof(sa);
rConnectedSock.m_hSocket = accept(this->m_hSocket, (sockaddr*)&sa, &nLen);
if (rConnectedSock.m_hSocket == INVALID_SOCKET)
return false;
if (szIp)
strcpy(szIp, inet_ntoa(sa.sin_addr));
if (nPort)
*nPort = htons(sa.sin_port);
return true;
}
// 连接服务端
BOOL Connection(LPCSTR lpszHostAddress, UINT nPort)
{
sockaddr_in sa = { AF_INET };
sa.sin_port = htons(nPort);
sa.sin_addr.s_addr = inet_addr(lpszHostAddress);
return !connect(m_hSocket, (sockaddr*)&sa, sizeof(sa));
}
// 侦听
BOOL Listen(int nConnectionBacklog = 5)
{
return !listen(m_hSocket, nConnectionBacklog);
}
// 逐条发送
int Send(const void* lpBuf, int nBufLen, int nFlags = 0)
{
return send(m_hSocket, (LPCSTR)lpBuf, nBufLen, nFlags);
}
// 发送整个缓冲区
int SendTo(const void* lpBuf, int nBufLen, UINT nHostPort, LPCSTR lpszHostAddress = NULL,
int nFlags = 0)
{
sockaddr_in to = { AF_INET };
to.sin_port = htons(nHostPort);
to.sin_addr.s_addr = inet_addr(lpszHostAddress);
return sendto(m_hSocket, (LPCSTR)lpBuf, nBufLen, nFlags, (sockaddr*)&to, sizeof(to));
}
// 逐条接收
int Receive(void* lpBuf, int nBufLen, int nFlags = 0)
{
return recv(m_hSocket, (LPTSTR)lpBuf, nBufLen, nFlags);
}
// 接收整个缓冲区
int ReceiveFrom(void* lpBuf, int nBufLen, char* rSocketAddress, UINT& rSocketPort, int nFlags = 0)
{
sockaddr_in from = { AF_INET };
int lenFrom = sizeof(from);
int n = recvfrom(m_hSocket, (LPSTR)lpBuf, nBufLen, nFlags, (sockaddr*)&from, &lenFrom);
strcpy(rSocketAddress, inet_ntoa(from.sin_addr));
rSocketPort = htons(from.sin_port);
return n;
}
// 关闭套接字
void Close()
{
closesocket(m_hSocket);
m_hSocket = INVALID_SOCKET;
}
MySocket()
{
WSADATA wsaData;
WSAStartup(0x0202, &wsaData);
m_hSocket = INVALID_SOCKET;
}
~MySocket()
{
Close();
}
};
以下是对该类的概括:
- 类名:
MySocket
- 功能:提供了基本的网络通信功能,包括创建套接字、获取对端和本机的信息、接受客户端连接、连接服务端、监听连接请求、发送和接收数据。
- 成员变量:
SOCKET m_hSocket
:套接字句柄,用于标识一个套接字。
- 成员函数:
Create
:创建套接字,并可指定类型、本地端口和地址。Accept
:接受客户请求,返回连接的套接字。Connection
:连接到服务端。Listen
:开始监听连接请求。Send
:逐条发送数据。SendTo
:发送整个缓冲区到指定地址。Receive
:逐条接收数据。ReceiveFrom
:接收整个缓冲区,并获取发送端地址和端口。Close
:关闭套接字。
- 初始化和清理:
- 构造函数
MySocket
:初始化 Winsock 库和套接字句柄。 - 析构函数
~MySocket
:关闭套接字。
- 构造函数
- 使用注意事项:
- 适用于简单的同步网络通信场景。
该类提供了一些基本的网络编程功能,适合用于创建简单的服务器端和客户端。需注意,这是一个同步实现的套接字类,适用于一些较为简单的网络通信需求。
2. 实现简单的通信
通过具体的代码示例,我们将演示如何使用交互式 Socket 类在 Windows 操作系统上实现同步远程通信。代码将包括服务器端和客户端的实现,以及它们之间的交互过程。通过这些示例,读者将更好地理解如何在实际项目中应用这些概念。
2.1 服务端流程
如下代码是一个简单的服务端程序,通过 MySocket
类建立基于 TCP 协议的服务器,通过sock.Create()
创建套接字,然后通过sock.Accept()
接收套接字,当有新的套接字连入时自动调用_beginthread()
函数开启一个子线程维持套接字的运行,每一个子线程内部则都由ClientPro()
函数来实现交互。
#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <iostream>
#include <process.h>
#include "MySocket.hpp"
using namespace std;
void ClientPro(void* ptr)
{
// 初始化
MySocket* pSock = (MySocket*)ptr;
MySocket server_socket = *pSock;
server_socket.Send((const char *)"Welcome to LyServer", 19);
// 获取客户端信息
char sIp[20];
UINT nPort;
server_socket.GetPeerName(sIp, nPort);
while (true)
{
char szBuffer[4096] = { 0 };
// 接收客户返回消息
int ref = server_socket.Receive(szBuffer, sizeof(szBuffer));
if (ref <= 0)
{
std::cout << "客户: " << sIp << ":" << nPort << " [已断开]" << std::endl;
break;
}
std::cout << "地址: " << sIp << ":" << nPort << " 接收命令: " << szBuffer << std::endl;
// 选择不同的命令
if (strcmp(szBuffer, "list\n") == 0)
{
std::cout << "输出文件" << std::endl;
}
else if (strcmp(szBuffer, "download\n") == 0)
{
std::cout << "下载文件" << std::endl;
}
else if (strcmp(szBuffer, "upload\n") == 0)
{
std::cout << "上传文件" << std::endl;
}
// 返回给客户端
server_socket.Send((char*)"ok", 2);
}
}
int main(int argc, char *argv[])
{
MySocket sock;
if (!sock.Create(8233, SOCK_STREAM, "127.0.0.1"))
{
return -1;
}
// 获取本机信息
char sSevIp[20];
UINT nSevPort;
sock.GetSockName(sSevIp, nSevPort);
std::cout << "服务端: " << sSevIp << ":" << nSevPort << " 服务器启动成功" << std::endl;
sock.Listen(5);
// 获取客户端信息
char sIp[20];
UINT nPort;
MySocket ptr;
while (true)
{
// 当有新用户进来自动创建一个线程来维持会话
sock.Accept(ptr, sIp, &nPort);
std::cout << "客户: " << sIp << ":" << nPort << " [已登录]" << std::endl;
// 多线程
_beginthread(ClientPro, 0, &ptr);
}
return 0;
}
以下是对该代码的概括:
- 功能:实现一个简单的基于 TCP 的服务器,监听指定端口(8233),接受客户端连接,创建一个线程处理每个客户端的会话。
- 主要函数和过程:
ClientPro
函数:处理每个客户端的会话。向客户端发送欢迎消息,接收客户端发送的命令,根据不同的命令执行相应的操作,并向客户端发送响应。该函数通过多线程在后台运行,使得服务器能够同时处理多个客户端。main
函数:在主线程中创建MySocket
类实例sock
,并调用Create
函数创建服务器套接字。然后,通过Listen
函数监听客户端连接。在循环中,通过Accept
函数接受客户端连接,并为每个客户端创建一个新线程,用于处理客户端的会话。
- 通信协议:客户端和服务器之间通过简单的文本协议进行通信。客户端发送不同的命令("list"、"download"、"upload"),服务器接收命令并执行相应的操作,然后向客户端发送响应("ok")。
- 线程创建:使用
_beginthread
函数在每个新连接上创建一个线程,用于处理该客户端的会话。
2.2 客户端流程
如下代码是一个简单的客户端程序,通过 MySocket
类实现与服务端的基于 TCP 协议的通信,通过sock.Connection()
建立套接字链接,通过sock.Receive()
接收数据,通过sock.Send()
发送数据,其运行原理与原生套接字写法保持一致。
#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <iostream>
#include "MySocket.hpp"
using namespace std;
int main(int argc, char* argv[])
{
MySocket sock;
if (!sock.Create(0, SOCK_STREAM))
{
return -1;
}
// 获取本机信息
char sClientIp[20];
UINT nClientPort;
sock.GetSockName(sClientIp, nClientPort);
std::cout << "服务端: " << sClientIp << ":" << nClientPort << " 服务器启动成功" << std::endl;
if (!sock.Connection("127.0.0.1", 8233))
{
cout << "连接服务器失败" << GetLastError() << endl;
return -1;
}
char szBuffer[4096] = { 0 };
int ref = sock.Receive(szBuffer, sizeof(szBuffer));
szBuffer[ref] = 0;
std::cout << "服务端回应: " << szBuffer << std::endl;
while (true)
{
// 循环接受输入
input:
memset(szBuffer, 0, 4096);
std::cout << "Input CMD > ";
// 接收输入命令
int inputLine = 0;
while ((szBuffer[inputLine++] = getchar()) != '\n');
if (strlen(szBuffer) == 1)
goto input;
// 发送数据
sock.Send(szBuffer, 4096, 0);
// 接收回显
memset(szBuffer, 0, 4096);
sock.Receive(szBuffer, 4096, 0);
std::cout << "服务端回显: " << szBuffer << std::endl;
}
sock.Close();
return 0;
}
以下是对该代码的概括:
- 功能:实现一个基于 TCP 的客户端,连接到指定 IP 地址和端口(
127.0.0.1:8233
),与服务器建立连接后,可以输入命令并发送到服务器,接收并显示服务器的回显。 - 主要函数和过程:
main
函数:在主线程中创建MySocket
类实例sock
,并调用Create
函数创建客户端套接字。然后,通过Connection
函数连接到服务器。接着,通过Receive
函数接收服务器发送的欢迎消息,并显示在控制台。- 在一个无限循环中,通过标准输入接收用户输入的命令,将命令发送到服务器,然后接收并显示服务器的回显。
- 通信协议:客户端和服务器之间通过简单的文本协议进行通信。客户端发送用户输入的命令,服务器执行命令并将结果回显给客户端。
- 输入循环:通过一个无限循环,不断接收用户输入的命令,并发送到服务器。如果用户输入空命令,程序会跳转回
input
标签重新接收输入。 - 错误处理:在连接服务器失败时,通过
GetLastError()
输出详细错误信息。 - 关闭套接字:在程序结束时,通过
sock.Close()
关闭套接字。
依次运行服务端和客户端,然后当客户端连接成功后此时的服务端即可收到连接请求,此时客户端可以执行各类简单的命令,如下图所示;
3.实现登录服务器
上述代码只是一个简单的演示案例,用来演示如何使用套接字编写交互程序,如下我们将继续完善这段代码,实现一个简单的带有登录功能的登录服务器程序,使用户可以在执行命令前具备简单的登录认证功能。
3.1 服务端流程
如下代码是一个简单的基于 Windows 的多线程服务器程序,通过 MySocket
类实现与客户端的基于 TCP 协议的通信,在交互模式下用户可输入多种命令,登录登出以及登陆后的命令执行功能。
#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <iostream>
#include <process.h>
#include <vector>
#include "MySocket.hpp"
using namespace std;
// 登录状态记录
typedef struct
{
char UserName[32];
int SocketID;
}loginPool;
// ------------------------------------------------------------------------
// 用户登录验证代码部分
std::vector<loginPool> login_pool_vect;
// 检查用户ID是否存在与容器内,如果存在则返回用户名
bool is_login(std::vector<loginPool> &ptr, int socket_id)
{
for (int x = 0; x < ptr.size(); x++)
{
if (ptr[x].SocketID == socket_id)
{
return true;
}
}
return false;
}
// 用户登录验证
bool login(char *username, char *password, int socket_id)
{
if ((strcmp(username, "lyshark") == 0) && (strcmp(password, "123123") == 0))
{
// 如果在则增加一个socket登录标志
loginPool pool_ptr;
pool_ptr.SocketID = socket_id;
strcpy(pool_ptr.UserName, "lyshark");
login_pool_vect.push_back(pool_ptr);
return true;
}
else if ((strcmp(username, "admin") == 0) && (strcmp(password, "123456") == 0))
{
// 如果在则增加一个socket登录标志
loginPool pool_ptr;
pool_ptr.SocketID = socket_id;
strcpy(pool_ptr.UserName, "lyshark");
login_pool_vect.push_back(pool_ptr);
return true;
}
return false;
}
// 根据传入ID从容器内弹出一个节点
bool logout(std::vector<loginPool> &ptr, int socket_id)
{
for (vector<loginPool>::iterator it = ptr.begin(); it != ptr.end(); it++)
{
if (it->SocketID == socket_id)
{
// 弹出指定结构体
ptr.erase(it);
return true;
}
}
return false;
}
// ------------------------------------------------------------------------
// 响应客户端的子线程(主要功能实现部分)
void ClientPro(void* ptr)
{
// 初始化
MySocket* pSock = (MySocket*)ptr;
MySocket server_socket = *pSock;
server_socket.Send((const char *)"Welcome to LyShark Mini Server", 31);
// 获取客户端信息
char sIp[20];
UINT nPort;
server_socket.GetPeerName(sIp, nPort);
while (true)
{
char szBuffer[4096] = { 0 };
int sid = pSock->GetSocketID();
int ref = server_socket.Receive(szBuffer, sizeof(szBuffer));
if (ref <= 0)
{
logout(login_pool_vect, sid);
std::cout << "客户: " << sIp << ":" << nPort << " [已断开]" << std::endl;
break;
}
std::cout << "地址: " << sIp << ":" << nPort << " 接收命令: " << szBuffer << std::endl;
// 用户登录
if (strcmp(szBuffer, "login\n") == 0)
{
char recv_username[32] = { 0 };
char recv_password[32] = { 0 };
// 接收用户名和密码
pSock->Receive(recv_username, 32, 0);
pSock->Receive(recv_password, 32, 0);
// 验证登录状态
bool login_flag = login(recv_username, recv_password, sid);
if (login_flag == TRUE)
{
std::cout << "用户: " << recv_username << " 已登录" << std::endl;
pSock->Send("已登录", sizeof("已登录"), 0);
}
else
{
pSock->Send("账号或密码错误", sizeof("账号或密码错误"), 0);
}
}
// 用户登出
else if (strcmp(szBuffer, "logout\n") == 0)
{
// 验证是否登录成功
int login_flag = is_login(login_pool_vect, sid);
if (login_flag == TRUE)
{
std::cout << "用户已登出" << std::endl;
logout(login_pool_vect, sid);
pSock->Send("用户已登出", sizeof("用户已登出"), 0);
}
else
{
std::cout << "请先登录" << std::endl;
pSock->Send("请先登录", sizeof("请先登录"), 0);
}
}
// 遍历本机文件
else if (strcmp(szBuffer, "list\n") == 0)
{
// 验证是否登录成功
int login_flag = is_login(login_pool_vect, sid);
if (login_flag == TRUE)
{
std::cout << "用户已登录,输出本机文件" << std::endl;
pSock->Send("认证通过", sizeof("认证通过"), 0);
// 循环输出数据包
for (int x = 0; x < 10; x++)
{
char sz[1024] = { 0 };
sprintf(sz, "count -> %d", x);
pSock->Send(sz, sizeof(sz), 0);
}
}
else
{
std::cout << "请先登录" << std::endl;
pSock->Send("请先登录", sizeof("请先登录"), 0);
}
}
}
}
int main(int argc, char *argv[])
{
MySocket sock;
if (!sock.Create(8233, SOCK_STREAM, "127.0.0.1"))
{
return -1;
}
// 获取本机信息
char sSevIp[20];
UINT nSevPort;
sock.GetSockName(sSevIp, nSevPort);
std::cout << "服务端: " << sSevIp << ":" << nSevPort << " 服务器启动成功" << std::endl;
sock.Listen(5);
// 获取客户端信息
char sIp[20];
UINT nPort;
MySocket ptr;
while (true)
{
sock.Accept(ptr, sIp, &nPort);
std::cout << "客户: " << sIp << ":" << nPort << " [已登录]" << std::endl;
// 多线程
_beginthread(ClientPro, 0, &ptr);
}
return 0;
}
以下是对该代码的概括:
- 功能:
- 通过
MySocket
类实现基于 TCP 协议的多线程服务器,可以处理多个客户端的连接。 - 实现了用户登录验证功能,支持用户登录、登出和查看本机文件列表的操作。
- 通过
- 主要结构和功能:
- 登录状态记录结构体 (
loginPool
):记录用户登录状态,包括用户名和套接字 ID。 - 用户登录验证相关函数:
is_login
:检查指定套接字 ID 是否已登录。login
:验证用户名和密码,如果验证通过则将用户信息加入登录池。logout
:根据套接字 ID 从登录池中移除用户。
- 子线程主要处理函数
ClientPro
:- 初始化后发送欢迎消息给客户端。
- 接收客户端命令,处理用户登录、登出和查看本机文件列表的请求。
- 针对不同的命令进行相应的处理和回复。
- 主线程
main
:- 创建服务器套接字,并通过
Create
函数创建服务器套接字。 - 获取本机信息,包括 IP 地址和端口,并显示在控制台。
- 通过
Listen
函数监听客户端连接。 - 接受客户端连接,创建子线程处理每个客户端连接。
- 创建服务器套接字,并通过
- 登录状态记录结构体 (
- 通信协议:服务器与客户端之间通过简单的文本协议进行通信,支持用户登录、登出和查看本机文件列表的操作。
- 多线程处理:通过
_beginthread
创建子线程处理每个客户端的连接,实现了多客户端并发处理。 - 用户登录验证:支持用户登录验证功能,通过用户名和密码验证用户身份,记录登录状态,处理用户登录、登出的请求。
3.2 客户端流程
如下代码是一个基于 Windows 的客户端程序,通过 MySocket
类实现与服务器的基于 TCP 协议的通信。
#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
#include <iostream>
#include "MySocket.hpp"
using namespace std;
int main(int argc, char* argv[])
{
MySocket sock;
if (!sock.Create(0, SOCK_STREAM))
{
return -1;
}
// 获取本机信息
char sClientIp[20];
UINT nClientPort;
sock.GetSockName(sClientIp, nClientPort);
if (!sock.Connection("127.0.0.1", 8233))
{
cout << "连接服务器失败" << GetLastError() << endl;
return -1;
}
char szBuffer[4096] = { 0 };
int ref = sock.Receive(szBuffer, sizeof(szBuffer));
szBuffer[ref] = 0;
std::cout << "服务端回应: " << szBuffer << std::endl;
while (true)
{
input:
memset(szBuffer, 0, 4096);
std::cout << "CMD > ";
// 发送命令
int inputLine = 0;
while ((szBuffer[inputLine++] = getchar()) != '\n');
if (strlen(szBuffer) == 1)
goto input;
// 执行登录
if (strcmp(szBuffer, "login\n") == 0)
{
// 发送命令
sock.Send(szBuffer, 4096, 0);
char input_username[32] = { 0 };
char input_password[32] = { 0 };
// 发送用户名
printf("用户名: ");
scanf("%s", &input_username);
sock.Send(input_username, 32, 0);
// 发送密码
printf("密码: ");
scanf("%s", &input_password);
sock.Send(input_password, 32, 0);
// 获取登录状态
char recv_message[64] = { 0 };
sock.Receive(recv_message, 64, 0);
std::cout << recv_message << std::endl;
}
// 登出用户
else if (strcmp(szBuffer, "logout\n") == 0)
{
// 发送命令
sock.Send(szBuffer, 4096, 0);
// 获取返回消息
char recv_message[64] = { 0 };
sock.Receive(recv_message, 64, 0);
std::cout << recv_message << std::endl;
}
// 遍历本机文件
else if (strcmp(szBuffer, "list\n") == 0)
{
// 发送命令
sock.Send(szBuffer, 4096, 0);
// 获取返回消息
char recv_message[64] = { 0 };
sock.Receive(recv_message, 64, 0);
std::cout << recv_message << std::endl;
if (strcmp(recv_message, "请先登录") == 0)
{
goto input;
}
// 循环接收数据包
for (int x = 0; x < 10; x++)
{
char sz[1024] = { 0 };
sock.Receive(sz, 1024, 0);
std::cout << sz << std::endl;
}
}
}
sock.Close();
return 0;
}
以下是对该代码的概括:
- 功能:
- 通过
MySocket
类实现基于 TCP 协议的客户端,可以与服务器进行通信。 - 支持用户通过命令行输入与服务器进行简单的交互,包括登录、登出和查看本机文件列表的操作。
- 通过
- 主要结构和功能:
- 用户交互循环:
- 使用一个循环,通过命令行输入命令,将命令发送给服务器,并根据服务器的回应进行相应的操作。
- 支持登录、登出和查看本机文件列表的操作。
- 命令处理:
- 对用户输入的不同命令,通过
sock.Send
将命令发送给服务器,并通过sock.Receive
接收服务器的回应。 - 具体命令包括登录、登出和查看本机文件列表。
- 对用户输入的不同命令,通过
- 登录交互:
- 当用户输入 "login" 命令时,程序会提示用户输入用户名和密码,并将输入的用户名和密码发送给服务器进行登录验证。
- 接收服务器的回应,输出相应的登录状态信息。
- 登出交互:
- 当用户输入 "logout" 命令时,程序向服务器发送登出命令,接收服务器的回应并输出相应的信息。
- 查看本机文件列表交互:
- 当用户输入 "list" 命令时,程序向服务器发送查看本机文件列表的命令,接收服务器的回应并输出相应的信息。
- 如果用户未登录,则输出 "请先登录" 提示,并继续等待用户输入。
- 用户交互循环:
- 通信协议:客户端与服务器之间通过简单的文本协议进行通信,服务器回应的信息通过控制台输出。
与之前的程序不同,这段代码增加了简单的用户认证模式,当用户直接执行命令时则会提示客户端请先登录,无法执行命令;
此时通过login
命令,并输入用户名lyshark
密码123123
则会提示已登录,此时就可以执行任意的命令参数了,如下图所示,当结束时还需要使用logout
退出当前会话;
C/C++ 实现Socket交互式服务端的更多相关文章
- java socket实现服务端,客户端简单网络通信。Chat
之前写的实现简单网络通信的代码,有一些严重bug.后面详细写. 根据上次的代码,主要增加了用户注册,登录页面,以及实现了实时显示当前在登录状态的人数.并解决一些上次未发现的bug.(主要功能代码参见之 ...
- Socket客户端/服务端简单实例
1.client端 package demo.socket; import java.io.BufferedReader;import java.io.IOException;import java. ...
- java.net.SocketException:Software caused connection abort: recv failed 异常分析 +socket客户端&服务端代码
java.net.SocketException:Software caused connection abort: recv failed 异常分析 分类: 很多的技术 2012-01-04 12: ...
- JAVA Socket获取服务端信息
1.Socket.getInetAddress(),获取服务端地址. 2.Socket.getPort(),获取服务端端口.
- 简单的同步Socket程序服务端
首先,Socket是.Net提供的 System.Net.Sockets命名空间的Scoket类为网络通信提供了一套丰富的方法和属性 服务器按照Socket的基本流程 先创建Socket 在用Bind ...
- c#Socket Tcp服务端编程
创建一个socket服务类,绑定监听端口, 然后创建一个线程listen连接的客户端, 把监听到的客户端加入dictionary里面,以便于管理, 同时创建receive线程,循环接收数据加入list ...
- python网络编程:socket、服务端、客户端
本文内容: socket介绍 TCP: 服务端 客户端 UDP: 服务端 客户端 首发时间:2018-02-08 01:14 修改: 2018-03-20 :重置了布局,增加了UDP 什么是socke ...
- 利用Socket 客户端---->服务端 传送文件到指定路径,并返回一个友好的回馈
首先盲写的一个传输文件的方法,但测试发现了一个非常不容易发现的问题,这里先说明一下. 错误的代码如下: package com.TCP.java; import java.io.File; impor ...
- C# socket异步 服务端
转自: http://blog.csdn.net/qq_20282263/article/details/54310737 private Dictionary<string, Session ...
- python 学习笔记_2 模拟socket编程 服务端、客户端通信(参考核心编程2代码实现)
服务器端代码实现: #!/usr/bin/env python#coding=gbk'''接收客户端字符串,在字段串前面打上当前时间,然后返回server端采用 python2 linux下调试运行客 ...
随机推荐
- 【每日一题】21.边的染色 (DFS连通图 + 思维)
补题链接:Here 思维不够,看到这种陌生的题目无从下手. 这题应该做过一次的人会觉得它其实并不难. 主要思想:把边权->点权. 这样做的好处是,无论你怎么分配点权,在环内的异或值一定为 \(0 ...
- 技术分享 | 不同格式标准SBOM清单横评:SPDX、CDX和DSDX
为了保证安全性.降低开发.采购及维护的相关成本,复杂动态的现代软件供应链对软件资产透明度提出了更高的要求.使用清晰的软件物料清单(SBOM)收集和共享信息,并在此基础上进行漏洞.许可证和授权管理等,可 ...
- 文心一言 VS 讯飞星火 VS chatgpt (181)-- 算法导论13.4 4题
四.用go语言,在 RB-DELETE-FIXUP 代码的哪些行中,可能会检查或修改哨兵 T.nil ? 文心一言: RB-DELETE-FIXUP 是红黑树中的一个操作,用于在删除一个节点后进行必要 ...
- 2023 中国 Serverless 用户调查,邀您填写!
当前云计算已成为数字时代的基础设施,支撑众多企业进行数字化转型升级.随着企业上云的范围更加广泛,国内云计算正在迈向云原生时代.Serverless技术因其以应用为中心.屏蔽底层复杂逻辑,灵活扩展,按需 ...
- vue 状态管理 二、状态管理的基本使用
系列导航 vue 状态管理 一.状态管理概念和基本结构 vue 状态管理 二.状态管理的基本使用 vue 状态管理 三.Mutations和Getters用法 vue 状态管理 四.Action用法 ...
- node-sass安装失败,安装后无法使用 gyp verb check python checking for Python executable "python2" in the PATH
这个问题搞了一会想起开始安装node-sass时的一句被我忽略的提示:执行 npm rebuild node-sass 这行后就可以了. 再说说 node-sass 的安装问题,现在使用 yarn i ...
- 从输入URL后浏览器的渲染逻辑
从输入URL到浏览器渲染页面需要经过很多过程,本文简单说明下各个环节的内容 主要渲染节点如下: 一.浏览器进程说明 出于安全考虑,打开一个浏览器的Tab页签,会生成1个浏览器主进程.1个网络进程.1个 ...
- lucene.net全文检索(二)lucene.net 的封装
查询 public class LuceneQuery : ILuceneQuery { #region Identity private Logger logger = new Logger(typ ...
- python 设计模式 开篇 第1篇
1. 设计模式的定义 软件设计中普遍存在的 反复出现的 各种问题 所提出的解决方案 首先,什么是软件设计? 系统开发 项目开始的时候 需要做 需求分析 软件系统设计 软件建模 类的设计 接口的设计 等 ...
- 基于AHB_BUS的eFlash控制器RTL
eFlash控制器的RTL gvim 操作 gg -- 跳到首页 GG -- 按住shift,跳到尾部 ctrl+V --> 上下键选择行 --> shift+i -->输入 --& ...