C++ 实现Socket交互式服务端

在 Windows 操作系统中,原生提供了强大的网络编程支持,允许开发者使用 Socket API 进行网络通信,通过 Socket API,开发者可以创建、连接、发送和接收数据,实现网络通信。本文将深入探讨如何通过调用原生网络 API 实现同步远程通信,并介绍了一个交互式 Socket 类的封装,提升了编写交互式服务器的便利性。

1. 交互式套接字类

为了更好地利用原生网络 API,我们引入了一个交互式 Socket 类的封装。这个类抽象了底层的网络细节,提供了简单而强大的接口,使得服务器端的交互式功能更容易实现。我们将详细介绍这个类的设计和使用方法。

MySocket 类是一个 C++ 套接字类,封装了在 Windows 平台上使用原生网络 API 进行同步远程通信的基本功能,该类需要使用多字节编码模式,服务端与客户端均需要引入此类,在项目头文件中均需要新建MySocket.hpp文件。

粉丝福利, 免费领取C/C++ 开发学习资料包、技术视频/项目代码,1000道大厂面试题,内容包括(C++基础,网络编程,数据库,中间件,后端开发/音视频开发/Qt开发/游戏开发/Linuxn内核等进阶学习资料和最佳学习路线)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

完整代码如下所示;

#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++ 开发学习资料包、技术视频/项目代码,1000道大厂面试题,内容包括(C++基础,网络编程,数据库,中间件,后端开发/音视频开发/Qt开发/游戏开发/Linuxn内核等进阶学习资料和最佳学习路线)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

  • 18
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 好的,下面是关于 C 语言下 Socket 组播服务端与客户端代 码的示例: 服务端代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <sys/socket.h> #include <netinet/in.h> #include <unistd.h> #define PORT 8080 #define MAXLINE 1024 int main() { int sockfd; char buffer[MAXLINE]; char* message = "Hello, Multicast!"; struct sockaddr_in servaddr; // 创建套接字 sockfd = socket(AF_INET, SOCK_DGRAM, 0); memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(PORT); // 绑定套接字 if(bind(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) { perror("bind failed"); exit(EXIT_FAILURE); } // 循环发送数据 while(1) { if(sendto(sockfd, (const char*)message, strlen(message), MSG_CONFIRM, (const struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) { perror("sendto failed"); exit(EXIT_FAILURE); } sleep(1); } close(sockfd); return 0; } ``` 客户端代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <sys/socket.h> #include <netinet/in.h> #include <unistd.h> #define PORT 8080 #define MAXLINE 1024 int main() { int sockfd; char buffer[MAXLINE]; struct sockaddr_in servaddr; // 创建套接字 sockfd = socket(AF_INET, SOCK_DGRAM, 0); memset(&servaddr, 0, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = inet_addr("239.0.0.1"); servaddr.sin_port = htons(PORT); // 加入组播组 struct ip_mreq mreq; mreq.imr_multiaddr.s_addr = inet_addr("239.0.0.1"); mreq.imr_interface.s_addr = htonl(INADDR_ANY); if(setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { perror("setsockopt failed"); exit(EXIT_FAILURE); } // 循环接收数据 while(1) { memset(buffer, 0, MAXLINE); if(recvfrom(sockfd, (char*)buffer, MAXLINE, MSG_WAITALL, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) { perror("recvfrom failed"); exit(EXIT_FAILURE); } printf("Message received: %s\n", buffer); } close(sockfd); return 0; } ``` 这段示例代码展示了如何创建基于 UDP 协议的 Socket 组播服务端和客户端,其中服务端会不断向组播地址发送消息,而客户端会监听这个组播地址并接收到服务端发来的消息。 ### 回答2: 下面是一个使用Socket进行组播的服务端和客户端的代码示例: 服务端代码: ```python import socket # 创建UDP socket,并绑定端口 server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) server_socket.bind(('', 5000)) # 加入组播组 group_ip = '239.0.0.1' group_port = 5555 group = socket.inet_aton(group_ip) + socket.inet_aton('0.0.0.0') server_socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, group) while True: # 接收客户端发送的数据 data, address = server_socket.recvfrom(1024) # 打印接收到的数据 print(f"接收到来自 {address} 的消息: {data.decode()}") # 将接收到的数据发送给组播组 server_socket.sendto(data, (group_ip, group_port)) # 关闭socket连接 server_socket.close() ``` 客户端代码: ```python import socket # 创建UDP socket,并设置套接字为广播类型 client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) client_socket.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1) while True: # 获取用户输入消息 message = input("请输入要发送的消息(退出请输入q): ") if message == 'q': break # 将消息发送给组播组 client_socket.sendto(message.encode(), ('<broadcast>', 5000)) # 接收服务端发送的数据 data, address = client_socket.recvfrom(1024) # 打印接收到的数据 print(f"接收到来自 {address} 的回复: {data.decode()}") # 关闭socket连接 client_socket.close() ``` 以上代码中,服务端通过创建一个UDP socket,并绑定指定端口,然后加入指定的组播组。客户端通过创建一个UDP socket并设置为广播类型,然后发送消息给组播组。服务端接收到客户端发送的消息后,将消息发送给组播组,客户端收到组播消息后打印出来。通过这种方式,服务端和客户端可以进行组播通信。 ### 回答3: 组播(Multicast)是一种基于UDP的通信协议,它允许一个服务器同时向多个客户端发送数据。下面是Socket组播服务端与客户端的示例代码: 服务端代码: ```python import socket def multicast_server(): multicast_group = '224.0.0.1' server_address = ('', 10000) # 创建组播套接字 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 设置套接字为可复用 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 绑定地址和端口 sock.bind(server_address) # 将套接字加入组播组 group = socket.inet_aton(multicast_group) mreq = struct.pack('4sL', group, socket.INADDR_ANY) sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) while True: data, address = sock.recvfrom(1024) print(f'Received message: {data.decode()} from {address}') # 关闭套接字 sock.close() if __name__ == '__main__': multicast_server() ``` 客户端代码: ```python import socket def multicast_client(): multicast_group = '224.0.0.1' server_address = ('', 10000) # 创建组播套接字 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 设置套接字为可复用 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # 绑定地址和端口 sock.bind(server_address) # 将套接字加入组播组 group = socket.inet_aton(multicast_group) mreq = struct.pack('4sL', group, socket.INADDR_ANY) sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) while True: message = input('Enter message to send: ') # 发送消息到组播组 sock.sendto(message.encode(), (multicast_group, 10000)) # 关闭套接字 sock.close() if __name__ == '__main__': multicast_client() ``` 以上是一个基本的组播示例,服务端通过创建组播套接字绑定地址和端口,并加入组播组。客户端同样创建组播套接字并加入组播组,然后从用户输入获取消息并发送到组播组。通过组播可以实现向多个客户端同时发送相同的消息。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值