在 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交互式服务端的更多相关文章

  1. java socket实现服务端,客户端简单网络通信。Chat

    之前写的实现简单网络通信的代码,有一些严重bug.后面详细写. 根据上次的代码,主要增加了用户注册,登录页面,以及实现了实时显示当前在登录状态的人数.并解决一些上次未发现的bug.(主要功能代码参见之 ...

  2. Socket客户端/服务端简单实例

    1.client端 package demo.socket; import java.io.BufferedReader;import java.io.IOException;import java. ...

  3. java.net.SocketException:Software caused connection abort: recv failed 异常分析 +socket客户端&服务端代码

    java.net.SocketException:Software caused connection abort: recv failed 异常分析 分类: 很多的技术 2012-01-04 12: ...

  4. JAVA Socket获取服务端信息

    1.Socket.getInetAddress(),获取服务端地址. 2.Socket.getPort(),获取服务端端口.

  5. 简单的同步Socket程序服务端

    首先,Socket是.Net提供的 System.Net.Sockets命名空间的Scoket类为网络通信提供了一套丰富的方法和属性 服务器按照Socket的基本流程 先创建Socket 在用Bind ...

  6. c#Socket Tcp服务端编程

    创建一个socket服务类,绑定监听端口, 然后创建一个线程listen连接的客户端, 把监听到的客户端加入dictionary里面,以便于管理, 同时创建receive线程,循环接收数据加入list ...

  7. python网络编程:socket、服务端、客户端

    本文内容: socket介绍 TCP: 服务端 客户端 UDP: 服务端 客户端 首发时间:2018-02-08 01:14 修改: 2018-03-20 :重置了布局,增加了UDP 什么是socke ...

  8. 利用Socket 客户端---->服务端 传送文件到指定路径,并返回一个友好的回馈

    首先盲写的一个传输文件的方法,但测试发现了一个非常不容易发现的问题,这里先说明一下. 错误的代码如下: package com.TCP.java; import java.io.File; impor ...

  9. C# socket异步 服务端

    转自:  http://blog.csdn.net/qq_20282263/article/details/54310737 private Dictionary<string, Session ...

  10. python 学习笔记_2 模拟socket编程 服务端、客户端通信(参考核心编程2代码实现)

    服务器端代码实现: #!/usr/bin/env python#coding=gbk'''接收客户端字符串,在字段串前面打上当前时间,然后返回server端采用 python2 linux下调试运行客 ...

随机推荐

  1. 通过mongo-driver使用说明 GO 包管理机制

    本篇记录通过GO语言操作mongodb,实现的流程包括: 初始化项目工程 容器方式安装mongo 调试运行和编译运行 go使用mongo的代码如下,go操作mongo的SDK是mongo-driver ...

  2. 聚合查询 分组查询 F与Q查询 添加新字段

    目录 聚合查询 aggregate 聚合函数 起别名 分组查询 annotate 注释函数 起别名 分组查询报错 分组查询练习 总结 添加新字段 F与Q查询 F查询 字符串拼接 concat方法 Q查 ...

  3. 【Git使用】代码拉取及用户名初始化

    代码拉取及用户名初始化

  4. Linux 下运行.NET 6 7 8 程序遇到的两个问题

    一. /lib64/libstdc++.so.6: version `GLIBCXX_3.4.21' not found 的解决办法 1. 下载 libstdc++.so.6.0.21 文件 注意区分 ...

  5. Codeforces Round #671 (Div. 2) (A - B、D1题)

    比赛链接:https://codeforces.com/contest/1419 https://codeforces.com/contest/1419/problems A. Digit Game ...

  6. 【Serverless实战】传统单节点网站的Serverles

    什么是函数?刚刚考完数学没多久的我,脑里立马想到的是自变量.因变量.函数值,也就是y=f(x).当然,在计算机里,函数function往往指的是一段被定义好的代码程序,我们可以通过传参调用这个定义好的 ...

  7. vue tabBar导航栏设计实现1-初步设计

    系列导航 一.vue tabBar导航栏设计实现1-初步设计 二.vue tabBar导航栏设计实现2-抽取tab-bar 三.vue tabBar导航栏设计实现3-进一步抽取tab-item 四.v ...

  8. vue学习笔记 九、父子组件实例-基本结构

    系列导航 vue学习笔记 一.环境搭建 vue学习笔记 二.环境搭建+项目创建 vue学习笔记 三.文件和目录结构 vue学习笔记 四.定义组件(组件基本结构) vue学习笔记 五.创建子组件实例 v ...

  9. 关于spring-boot-starter-parent 3.1.2和3.1.5版本的区别导致的错误

    1.问题 在学习黑马程序员SpringBoot3+Vue3全套视频教程时,手动配置springboot项目时,由于之前spring-boot-starter-parent安装的版本是3.1.5,视频要 ...

  10. [转帖]Grafana连接oracle数据库插件

    Granfana作为前端监控显示程序提供了迅速图形化查看数据库数据的方式.虽然官网提供了部分免费数据库插件,但毕竟太少,最近需要在Oracle数据库上做项目,发现官方的oracle插件是收费的,几经周 ...