欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

基于C++實(shí)現(xiàn)Socket交互式服務(wù)端

 更新時(shí)間:2024年02月27日 08:31:12   作者:程序員老舅  
在?Windows?操作系統(tǒng)中,原生提供了強(qiáng)大的網(wǎng)絡(luò)編程支持,允許開發(fā)者使用?Socket?API?進(jìn)行網(wǎng)絡(luò)通信,本文將深入探討如何通過調(diào)用原生網(wǎng)絡(luò)?API?實(shí)現(xiàn)同步遠(yuǎn)程通信,并介紹了一個(gè)交互式?Socket?類的封裝,需要的可以參考下

在 Windows 操作系統(tǒng)中,原生提供了強(qiáng)大的網(wǎng)絡(luò)編程支持,允許開發(fā)者使用 Socket API 進(jìn)行網(wǎng)絡(luò)通信,通過 Socket API,開發(fā)者可以創(chuàng)建、連接、發(fā)送和接收數(shù)據(jù),實(shí)現(xiàn)網(wǎng)絡(luò)通信。本文將深入探討如何通過調(diào)用原生網(wǎng)絡(luò) API 實(shí)現(xiàn)同步遠(yuǎn)程通信,并介紹了一個(gè)交互式 Socket 類的封裝,提升了編寫交互式服務(wù)器的便利性。

1. 交互式套接字類

為了更好地利用原生網(wǎng)絡(luò) API,我們引入了一個(gè)交互式 Socket 類的封裝。這個(gè)類抽象了底層的網(wǎng)絡(luò)細(xì)節(jié),提供了簡(jiǎn)單而強(qiáng)大的接口,使得服務(wù)器端的交互式功能更容易實(shí)現(xiàn)。我們將詳細(xì)介紹這個(gè)類的設(shè)計(jì)和使用方法。

MySocket 類是一個(gè) C++ 套接字類,封裝了在 Windows 平臺(tái)上使用原生網(wǎng)絡(luò) API 進(jìn)行同步遠(yuǎn)程通信的基本功能,該類需要使用多字節(jié)編碼模式,服務(wù)端與客戶端均需要引入此類,在項(xiàng)目頭文件中均需要新建MySocket.hpp文件。

完整代碼如下所示;

#pragma once
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")
 
class MySocket
{
protected:
  SOCKET m_hSocket;
public:
 
  // 獲取對(duì)端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;
  }
 
  // 獲取本機(jī)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;
  }
 
  // 獲取當(dāng)前用戶SocketID
  BOOL GetSocketID()
  {
    return m_hSocket;
  }
 
  // 創(chuàng)建套接字
  BOOL Create(UINT nSocketPort = 0, int nSockType = SOCK_STREAM, LPCTSTR lpszSocketAddress = NULL)
  {
 
    // 創(chuàng)建套接字
    m_hSocket = socket(AF_INET, nSockType, 0);
    if (m_hSocket == INVALID_SOCKET)
      return false;
 
    // 設(shè)置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));
  }
 
  // 接受客戶請(qǐng)求
  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;
  }
 
  // 連接服務(wù)端
  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);
  }
 
  // 逐條發(fā)送
  int Send(const void* lpBuf, int nBufLen, int nFlags = 0)
  {
    return send(m_hSocket, (LPCSTR)lpBuf, nBufLen, nFlags);
  }
 
  // 發(fā)送整個(gè)緩沖區(qū)
  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);
  }
 
  // 接收整個(gè)緩沖區(qū)
  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;
  }
 
  // 關(guān)閉套接字
  void Close()
  {
    closesocket(m_hSocket);
    m_hSocket = INVALID_SOCKET;
  }
  MySocket()
  {
    WSADATA wsaData;
    WSAStartup(0x0202, &wsaData);
    m_hSocket = INVALID_SOCKET;
 
  }
  ~MySocket()
  {
    Close();
  }
};

以下是對(duì)該類的概括:

類名:MySocket

功能:提供了基本的網(wǎng)絡(luò)通信功能,包括創(chuàng)建套接字、獲取對(duì)端和本機(jī)的信息、接受客戶端連接、連接服務(wù)端、監(jiān)聽連接請(qǐng)求、發(fā)送和接收數(shù)據(jù)。

成員變量:

SOCKET m_hSocket:套接字句柄,用于標(biāo)識(shí)一個(gè)套接字。

成員函數(shù):

Create:創(chuàng)建套接字,并可指定類型、本地端口和地址。

Accept:接受客戶請(qǐng)求,返回連接的套接字。

Connection:連接到服務(wù)端。

Listen:開始監(jiān)聽連接請(qǐng)求。

Send:逐條發(fā)送數(shù)據(jù)。

SendTo:發(fā)送整個(gè)緩沖區(qū)到指定地址。

Receive:逐條接收數(shù)據(jù)。

ReceiveFrom:接收整個(gè)緩沖區(qū),并獲取發(fā)送端地址和端口。

Close:關(guān)閉套接字。

初始化和清理:

構(gòu)造函數(shù) MySocket:初始化 Winsock 庫(kù)和套接字句柄。

析構(gòu)函數(shù) ~MySocket:關(guān)閉套接字。

使用注意事項(xiàng):

適用于簡(jiǎn)單的同步網(wǎng)絡(luò)通信場(chǎng)景。

該類提供了一些基本的網(wǎng)絡(luò)編程功能,適合用于創(chuàng)建簡(jiǎn)單的服務(wù)器端和客戶端。需注意,這是一個(gè)同步實(shí)現(xiàn)的套接字類,適用于一些較為簡(jiǎn)單的網(wǎng)絡(luò)通信需求。

2. 實(shí)現(xiàn)簡(jiǎn)單的通信

通過具體的代碼示例,我們將演示如何使用交互式 Socket 類在 Windows 操作系統(tǒng)上實(shí)現(xiàn)同步遠(yuǎn)程通信。代碼將包括服務(wù)器端和客戶端的實(shí)現(xiàn),以及它們之間的交互過程。通過這些示例,讀者將更好地理解如何在實(shí)際項(xiàng)目中應(yīng)用這些概念。

2.1 服務(wù)端流程

如下代碼是一個(gè)簡(jiǎn)單的服務(wù)端程序,通過 MySocket 類建立基于 TCP 協(xié)議的服務(wù)器,通過sock.Create()創(chuàng)建套接字,然后通過sock.Accept()接收套接字,當(dāng)有新的套接字連入時(shí)自動(dòng)調(diào)用_beginthread()函數(shù)開啟一個(gè)子線程維持套接字的運(yùn)行,每一個(gè)子線程內(nèi)部則都由ClientPro()函數(shù)來實(shí)現(xiàn)交互。

#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;
    }
 
    // 獲取本機(jī)信息
    char sSevIp[20];
    UINT nSevPort;
    sock.GetSockName(sSevIp, nSevPort);
    std::cout << "服務(wù)端: " << sSevIp << ":" << nSevPort << " 服務(wù)器啟動(dòng)成功" << std::endl;
 
    sock.Listen(5);
 
    // 獲取客戶端信息
    char sIp[20];
    UINT nPort;
 
    MySocket ptr;
    while (true)
    {
        // 當(dāng)有新用戶進(jìn)來自動(dòng)創(chuàng)建一個(gè)線程來維持會(huì)話
        sock.Accept(ptr, sIp, &nPort);
        std::cout << "客戶: " << sIp << ":" << nPort << " [已登錄]" << std::endl;
 
        // 多線程
        _beginthread(ClientPro, 0, &ptr);
    }
    return 0;
}

以下是對(duì)該代碼的概括:

功能:實(shí)現(xiàn)一個(gè)簡(jiǎn)單的基于 TCP 的服務(wù)器,監(jiān)聽指定端口(8233),接受客戶端連接,創(chuàng)建一個(gè)線程處理每個(gè)客戶端的會(huì)話。

主要函數(shù)和過程:

ClientPro 函數(shù):處理每個(gè)客戶端的會(huì)話。向客戶端發(fā)送歡迎消息,接收客戶端發(fā)送的命令,根據(jù)不同的命令執(zhí)行相應(yīng)的操作,并向客戶端發(fā)送響應(yīng)。該函數(shù)通過多線程在后臺(tái)運(yùn)行,使得服務(wù)器能夠同時(shí)處理多個(gè)客戶端。

main 函數(shù):在主線程中創(chuàng)建 MySocket 類實(shí)例 sock,并調(diào)用 Create 函數(shù)創(chuàng)建服務(wù)器套接字。然后,通過 Listen 函數(shù)監(jiān)聽客戶端連接。在循環(huán)中,通過 Accept 函數(shù)接受客戶端連接,并為每個(gè)客戶端創(chuàng)建一個(gè)新線程,用于處理客戶端的會(huì)話。

通信協(xié)議:客戶端和服務(wù)器之間通過簡(jiǎn)單的文本協(xié)議進(jìn)行通信??蛻舳税l(fā)送不同的命令(“list”、“download”、“upload”),服務(wù)器接收命令并執(zhí)行相應(yīng)的操作,然后向客戶端發(fā)送響應(yīng)(“ok”)。

線程創(chuàng)建:使用 _beginthread 函數(shù)在每個(gè)新連接上創(chuàng)建一個(gè)線程,用于處理該客戶端的會(huì)話。

2.2 客戶端流程

如下代碼是一個(gè)簡(jiǎn)單的客戶端程序,通過 MySocket 類實(shí)現(xiàn)與服務(wù)端的基于 TCP 協(xié)議的通信,通過sock.Connection()建立套接字鏈接,通過sock.Receive()接收數(shù)據(jù),通過sock.Send()發(fā)送數(shù)據(jù),其運(yùn)行原理與原生套接字寫法保持一致。

#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;
  }
 
  // 獲取本機(jī)信息
  char sClientIp[20];
  UINT nClientPort;
  sock.GetSockName(sClientIp, nClientPort);
  std::cout << "服務(wù)端: " << sClientIp << ":" << nClientPort << " 服務(wù)器啟動(dòng)成功" << std::endl;
 
  if (!sock.Connection("127.0.0.1", 8233))
  {
    cout << "連接服務(wù)器失敗" << GetLastError() << endl;
    return -1;
  }
 
  char szBuffer[4096] = { 0 };
  int ref = sock.Receive(szBuffer, sizeof(szBuffer));
  szBuffer[ref] = 0;
  std::cout << "服務(wù)端回應(yīng): " << szBuffer << std::endl;
 
  while (true)
  {
 
  // 循環(huán)接受輸入
  input:
    memset(szBuffer, 0, 4096);
    std::cout << "Input CMD > ";
 
    // 接收輸入命令
    int inputLine = 0;
    while ((szBuffer[inputLine++] = getchar()) != '\n');
    if (strlen(szBuffer) == 1)
      goto input;
 
    // 發(fā)送數(shù)據(jù)
    sock.Send(szBuffer, 4096, 0);
 
    // 接收回顯
    memset(szBuffer, 0, 4096);
    sock.Receive(szBuffer, 4096, 0);
    std::cout << "服務(wù)端回顯: " << szBuffer << std::endl;
 
  }
  sock.Close();
  return 0;
}

以下是對(duì)該代碼的概括:

功能:實(shí)現(xiàn)一個(gè)基于 TCP 的客戶端,連接到指定 IP 地址和端口(127.0.0.1:8233),與服務(wù)器建立連接后,可以輸入命令并發(fā)送到服務(wù)器,接收并顯示服務(wù)器的回顯。

主要函數(shù)和過程:

main 函數(shù):在主線程中創(chuàng)建 MySocket 類實(shí)例 sock,并調(diào)用 Create 函數(shù)創(chuàng)建客戶端套接字。然后,通過 Connection 函數(shù)連接到服務(wù)器。接著,通過 Receive 函數(shù)接收服務(wù)器發(fā)送的歡迎消息,并顯示在控制臺(tái)。

在一個(gè)無限循環(huán)中,通過標(biāo)準(zhǔn)輸入接收用戶輸入的命令,將命令發(fā)送到服務(wù)器,然后接收并顯示服務(wù)器的回顯。

通信協(xié)議:客戶端和服務(wù)器之間通過簡(jiǎn)單的文本協(xié)議進(jìn)行通信。客戶端發(fā)送用戶輸入的命令,服務(wù)器執(zhí)行命令并將結(jié)果回顯給客戶端。

輸入循環(huán):通過一個(gè)無限循環(huán),不斷接收用戶輸入的命令,并發(fā)送到服務(wù)器。如果用戶輸入空命令,程序會(huì)跳轉(zhuǎn)回 input 標(biāo)簽重新接收輸入。

錯(cuò)誤處理:在連接服務(wù)器失敗時(shí),通過 GetLastError() 輸出詳細(xì)錯(cuò)誤信息。

關(guān)閉套接字:在程序結(jié)束時(shí),通過 sock.Close() 關(guān)閉套接字。

依次運(yùn)行服務(wù)端和客戶端,然后當(dāng)客戶端連接成功后此時(shí)的服務(wù)端即可收到連接請(qǐng)求,此時(shí)客戶端可以執(zhí)行各類簡(jiǎn)單的命令,如下圖所示;

3.實(shí)現(xiàn)登錄服務(wù)器

上述代碼只是一個(gè)簡(jiǎn)單的演示案例,用來演示如何使用套接字編寫交互程序,如下我們將繼續(xù)完善這段代碼,實(shí)現(xiàn)一個(gè)簡(jiǎn)單的帶有登錄功能的登錄服務(wù)器程序,使用戶可以在執(zhí)行命令前具備簡(jiǎn)單的登錄認(rèn)證功能。

3.1 服務(wù)端流程

如下代碼是一個(gè)簡(jiǎn)單的基于 Windows 的多線程服務(wù)器程序,通過 MySocket 類實(shí)現(xiàn)與客戶端的基于 TCP 協(xié)議的通信,在交互模式下用戶可輸入多種命令,登錄登出以及登陸后的命令執(zhí)行功能。

#define _CRT_SECURE_NO_WARNINGS
#define _WINSOCK_DEPRECATED_NO_WARNINGS
 
#include <iostream>
#include <process.h>
#include <vector>
#include "MySocket.hpp"
 
using namespace std;
 
// 登錄狀態(tài)記錄
typedef struct
{
  char UserName[32];
  int SocketID;
}loginPool;
 
// ------------------------------------------------------------------------
// 用戶登錄驗(yàn)證代碼部分
 
std::vector<loginPool> login_pool_vect;
 
// 檢查用戶ID是否存在與容器內(nèi),如果存在則返回用戶名
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;
}
 
// 用戶登錄驗(yàn)證
bool login(char *username, char *password, int socket_id)
{
  if ((strcmp(username, "lyshark") == 0) && (strcmp(password, "123123") == 0))
  {
    // 如果在則增加一個(gè)socket登錄標(biāo)志
    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))
  {
    // 如果在則增加一個(gè)socket登錄標(biāo)志
    loginPool pool_ptr;
    pool_ptr.SocketID = socket_id;
    strcpy(pool_ptr.UserName, "lyshark");
 
    login_pool_vect.push_back(pool_ptr);
    return true;
  }
  return false;
}
 
// 根據(jù)傳入ID從容器內(nèi)彈出一個(gè)節(jié)點(diǎn)
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)
    {
      // 彈出指定結(jié)構(gòu)體
      ptr.erase(it);
      return true;
    }
  }
  return false;
}
 
// ------------------------------------------------------------------------
// 響應(yīng)客戶端的子線程(主要功能實(shí)現(xiàn)部分)
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);
 
      // 驗(yàn)證登錄狀態(tài)
      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("賬號(hào)或密碼錯(cuò)誤", sizeof("賬號(hào)或密碼錯(cuò)誤"), 0);
      }
    }
 
    // 用戶登出
    else if (strcmp(szBuffer, "logout\n") == 0)
    {
      // 驗(yàn)證是否登錄成功
      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 << "請(qǐng)先登錄" << std::endl;
        pSock->Send("請(qǐng)先登錄", sizeof("請(qǐng)先登錄"), 0);
      }
    }
 
    // 遍歷本機(jī)文件
    else if (strcmp(szBuffer, "list\n") == 0)
    {
      // 驗(yàn)證是否登錄成功
      int login_flag = is_login(login_pool_vect, sid);
      if (login_flag == TRUE)
      {
        std::cout << "用戶已登錄,輸出本機(jī)文件" << std::endl;
        pSock->Send("認(rèn)證通過", sizeof("認(rèn)證通過"), 0);
 
        // 循環(huán)輸出數(shù)據(jù)包
        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 << "請(qǐng)先登錄" << std::endl;
        pSock->Send("請(qǐng)先登錄", sizeof("請(qǐng)先登錄"), 0);
      }
    }
  }
}
 
int main(int argc, char *argv[])
{
  MySocket sock;
  if (!sock.Create(8233, SOCK_STREAM, "127.0.0.1"))
  {
    return -1;
  }
 
  // 獲取本機(jī)信息
  char sSevIp[20];
  UINT nSevPort;
  sock.GetSockName(sSevIp, nSevPort);
  std::cout << "服務(wù)端: " << sSevIp << ":" << nSevPort << " 服務(wù)器啟動(dòng)成功" << 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;
}

以下是對(duì)該代碼的概括:

功能:

通過 MySocket 類實(shí)現(xiàn)基于 TCP 協(xié)議的多線程服務(wù)器,可以處理多個(gè)客戶端的連接。

實(shí)現(xiàn)了用戶登錄驗(yàn)證功能,支持用戶登錄、登出和查看本機(jī)文件列表的操作。

主要結(jié)構(gòu)和功能:

登錄狀態(tài)記錄結(jié)構(gòu)體 (loginPool):記錄用戶登錄狀態(tài),包括用戶名和套接字 ID。

用戶登錄驗(yàn)證相關(guān)函數(shù):

is_login:檢查指定套接字 ID 是否已登錄。

login:驗(yàn)證用戶名和密碼,如果驗(yàn)證通過則將用戶信息加入登錄池。

logout:根據(jù)套接字 ID 從登錄池中移除用戶。

子線程主要處理函數(shù) ClientPro:

初始化后發(fā)送歡迎消息給客戶端。

接收客戶端命令,處理用戶登錄、登出和查看本機(jī)文件列表的請(qǐng)求。

針對(duì)不同的命令進(jìn)行相應(yīng)的處理和回復(fù)。

主線程 main:

創(chuàng)建服務(wù)器套接字,并通過 Create 函數(shù)創(chuàng)建服務(wù)器套接字。

獲取本機(jī)信息,包括 IP 地址和端口,并顯示在控制臺(tái)。

通過 Listen 函數(shù)監(jiān)聽客戶端連接。

接受客戶端連接,創(chuàng)建子線程處理每個(gè)客戶端連接。

通信協(xié)議:服務(wù)器與客戶端之間通過簡(jiǎn)單的文本協(xié)議進(jìn)行通信,支持用戶登錄、登出和查看本機(jī)文件列表的操作。

多線程處理:通過 _beginthread 創(chuàng)建子線程處理每個(gè)客戶端的連接,實(shí)現(xiàn)了多客戶端并發(fā)處理。

用戶登錄驗(yàn)證:支持用戶登錄驗(yàn)證功能,通過用戶名和密碼驗(yàn)證用戶身份,記錄登錄狀態(tài),處理用戶登錄、登出的請(qǐng)求。

3.2 客戶端流程

如下代碼是一個(gè)基于 Windows 的客戶端程序,通過 MySocket 類實(shí)現(xiàn)與服務(wù)器的基于 TCP 協(xié)議的通信。

#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;
  }
 
  // 獲取本機(jī)信息
  char sClientIp[20];
  UINT nClientPort;
  sock.GetSockName(sClientIp, nClientPort);
 
  if (!sock.Connection("127.0.0.1", 8233))
  {
    cout << "連接服務(wù)器失敗" << GetLastError() << endl;
    return -1;
  }
 
  char szBuffer[4096] = { 0 };
  int ref = sock.Receive(szBuffer, sizeof(szBuffer));
  szBuffer[ref] = 0;
  std::cout << "服務(wù)端回應(yīng): " << szBuffer << std::endl;
 
  while (true)
  {
  input:
    memset(szBuffer, 0, 4096);
    std::cout << "CMD > ";
 
    // 發(fā)送命令
    int inputLine = 0;
    while ((szBuffer[inputLine++] = getchar()) != '\n');
    if (strlen(szBuffer) == 1)
      goto input;
 
    // 執(zhí)行登錄
    if (strcmp(szBuffer, "login\n") == 0)
    {
      // 發(fā)送命令
      sock.Send(szBuffer, 4096, 0);
 
      char input_username[32] = { 0 };
      char input_password[32] = { 0 };
 
      // 發(fā)送用戶名
      printf("用戶名: ");
      scanf("%s", &input_username);
      sock.Send(input_username, 32, 0);
 
      // 發(fā)送密碼
      printf("密碼: ");
      scanf("%s", &input_password);
      sock.Send(input_password, 32, 0);
 
      // 獲取登錄狀態(tài)
      char recv_message[64] = { 0 };
      sock.Receive(recv_message, 64, 0);
      std::cout << recv_message << std::endl;
    }
 
    // 登出用戶
    else if (strcmp(szBuffer, "logout\n") == 0)
    {
      // 發(fā)送命令
      sock.Send(szBuffer, 4096, 0);
 
      // 獲取返回消息
      char recv_message[64] = { 0 };
      sock.Receive(recv_message, 64, 0);
      std::cout << recv_message << std::endl;
    }
 
    // 遍歷本機(jī)文件
    else if (strcmp(szBuffer, "list\n") == 0)
    {
      // 發(fā)送命令
      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, "請(qǐng)先登錄") == 0)
      {
        goto input;
      }
 
      // 循環(huán)接收數(shù)據(jù)包
      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;
}

以下是對(duì)該代碼的概括:

功能:

通過 MySocket 類實(shí)現(xiàn)基于 TCP 協(xié)議的客戶端,可以與服務(wù)器進(jìn)行通信。

支持用戶通過命令行輸入與服務(wù)器進(jìn)行簡(jiǎn)單的交互,包括登錄、登出和查看本機(jī)文件列表的操作。

主要結(jié)構(gòu)和功能:

用戶交互循環(huán):

使用一個(gè)循環(huán),通過命令行輸入命令,將命令發(fā)送給服務(wù)器,并根據(jù)服務(wù)器的回應(yīng)進(jìn)行相應(yīng)的操作。

支持登錄、登出和查看本機(jī)文件列表的操作。

命令處理:

對(duì)用戶輸入的不同命令,通過 sock.Send 將命令發(fā)送給服務(wù)器,并通過 sock.Receive 接收服務(wù)器的回應(yīng)。

具體命令包括登錄、登出和查看本機(jī)文件列表。

登錄交互:

當(dāng)用戶輸入 “login” 命令時(shí),程序會(huì)提示用戶輸入用戶名和密碼,并將輸入的用戶名和密碼發(fā)送給服務(wù)器進(jìn)行登錄驗(yàn)證。

接收服務(wù)器的回應(yīng),輸出相應(yīng)的登錄狀態(tài)信息。

登出交互:

當(dāng)用戶輸入 “logout” 命令時(shí),程序向服務(wù)器發(fā)送登出命令,接收服務(wù)器的回應(yīng)并輸出相應(yīng)的信息。

查看本機(jī)文件列表交互:

當(dāng)用戶輸入 “list” 命令時(shí),程序向服務(wù)器發(fā)送查看本機(jī)文件列表的命令,接收服務(wù)器的回應(yīng)并輸出相應(yīng)的信息。

如果用戶未登錄,則輸出 “請(qǐng)先登錄” 提示,并繼續(xù)等待用戶輸入。

通信協(xié)議:客戶端與服務(wù)器之間通過簡(jiǎn)單的文本協(xié)議進(jìn)行通信,服務(wù)器回應(yīng)的信息通過控制臺(tái)輸出。

與之前的程序不同,這段代碼增加了簡(jiǎn)單的用戶認(rèn)證模式,當(dāng)用戶直接執(zhí)行命令時(shí)則會(huì)提示客戶端請(qǐng)先登錄,無法執(zhí)行命令;

此時(shí)通過login命令,并輸入用戶名lyshark密碼123123則會(huì)提示已登錄,此時(shí)就可以執(zhí)行任意的命令參數(shù)了,如下圖所示,當(dāng)結(jié)束時(shí)還需要使用logout退出當(dāng)前會(huì)話;

以上就是基于C++實(shí)現(xiàn)Socket交互式服務(wù)端的詳細(xì)內(nèi)容,更多關(guān)于C++ Socket交互式服務(wù)端的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C語言實(shí)現(xiàn)隨機(jī)抽取紙牌程序

    C語言實(shí)現(xiàn)隨機(jī)抽取紙牌程序

    這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)隨機(jī)抽取紙牌程序,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • C++實(shí)現(xiàn)猜數(shù)字游戲

    C++實(shí)現(xiàn)猜數(shù)字游戲

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)猜數(shù)字游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-07-07
  • 解決在Mac下直接解壓C++靜態(tài)庫(kù)出現(xiàn)的問題

    解決在Mac下直接解壓C++靜態(tài)庫(kù)出現(xiàn)的問題

    最近在研究C++的各種編譯構(gòu)建過程,學(xué)習(xí)了一下cmake,gyp/ninja這些自動(dòng)化構(gòu)建工具后,想著自己試下用純命令行跑一遍編譯流程。在試圖把C++靜態(tài)庫(kù)編譯為動(dòng)態(tài)庫(kù)的過程中遇到了棘手的問題,找了好久后發(fā)現(xiàn)是跟Mac平臺(tái)相關(guān)的,這里記錄一下,望對(duì)遇到類似問題的童鞋有幫助。
    2016-12-12
  • 成員函數(shù)的重載、覆蓋與隱藏詳細(xì)解析

    成員函數(shù)的重載、覆蓋與隱藏詳細(xì)解析

    成員函數(shù)的重載、覆蓋(override)與隱藏很容易混淆,C++程序員必須要搞清楚概念,否則錯(cuò)誤將防不勝防
    2013-10-10
  • C++實(shí)現(xiàn)數(shù)組的排序/插入重新排序/以及逆置操作詳解

    C++實(shí)現(xiàn)數(shù)組的排序/插入重新排序/以及逆置操作詳解

    將新的數(shù)字與已經(jīng)排序好的數(shù)組中的數(shù)字一一比較,直到找到插入點(diǎn),然后將插入點(diǎn)以后的數(shù)字都向后移動(dòng)一個(gè)單位(a[i+1]=a[i]),然后將數(shù)據(jù)插入即可
    2013-10-10
  • Qt Creator + CMake 構(gòu)建教程的方法步驟

    Qt Creator + CMake 構(gòu)建教程的方法步驟

    本文主要介紹了Qt Creator + CMake 構(gòu)建教程的方法步驟,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-02-02
  • 基于C語言的庫(kù)封裝發(fā)布技術(shù)詳解

    基于C語言的庫(kù)封裝發(fā)布技術(shù)詳解

    在編程的過程中,使用已經(jīng)封裝好的庫(kù)函數(shù)是十分方便的,也是十分高效的,這篇文章主要給大家介紹了關(guān)于C語言庫(kù)的封裝和使用的相關(guān)資料,需要的朋友可以參考下
    2021-08-08
  • C語言實(shí)現(xiàn)數(shù)據(jù)結(jié)構(gòu)迷宮實(shí)驗(yàn)

    C語言實(shí)現(xiàn)數(shù)據(jù)結(jié)構(gòu)迷宮實(shí)驗(yàn)

    這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)數(shù)據(jù)結(jié)構(gòu)迷宮實(shí)驗(yàn),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-03-03
  • C++流程控制中用于跳轉(zhuǎn)的return和goto語句學(xué)習(xí)教程

    C++流程控制中用于跳轉(zhuǎn)的return和goto語句學(xué)習(xí)教程

    這篇文章主要介紹了C++流程控制中用于跳轉(zhuǎn)的return和goto語句學(xué)習(xí)教程,是C++入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下
    2016-01-01
  • C語言獲取文件大小的兩種方式

    C語言獲取文件大小的兩種方式

    因?yàn)橐粢曨l開發(fā)的需要,經(jīng)常會(huì)寫一些文件輸入輸出的測(cè)試程序,常常用到獲取文件大小的函數(shù),本篇文章就記錄一下常用的兩種獲取文件大小的方式,希望對(duì)大家有所幫助
    2023-11-11

最新評(píng)論