1 设计思路简介
1.1 服务端通过 Server 类实现:
(1)通信初始化。包括套接字版本检查、套接字配置(套接字创建和地址配置);
(2)绑定套接字 + 端口监听;
(3)链接客户端。当客户端发来链接请求时,服务端与客户端连接;链接成功后,服务端起一个线程处理本次链接成功的客服端请求(接受客服端请求信息,给客服端返回响应数据)。
服务端仅仅提供一个通讯准备函数 ready(),不处理具体的数据,把业务逻辑留给线程函数。
1.2 客户端通过 Client 类实现:
(1)通信初始化。包括套接字版本检查,创建客户端套接字,配置将要链接的服务端的端口和ip地址等;
(2)发送链接请求。客户端主动给服务端发送链接请求,服务端起一个线程处理请求。
(3)设置发送数据。
(4)数据发送函数。
(5)数据接收函数。
客户端的数据都放到 Client 里方便读写操作。
2 Tcp通信实现(传输 struct 格式的数据)
/*
* 服务端 .h 文件
*/
#pragma once
#pragma warning(disable:4996)
#pragma comment(lib,"ws2_32.lib")
#include<winsock2.h>
#include<windows.h>
#include <iostream>
class Server
{
public:
Server();
~Server();
public:
bool ready(int hton = 10008, const char* ip = "127.0.0.1", int backlog = 10);
private:
bool intalize(int hton, const char* ip);
bool blind_listen(int backlog);
bool server_connect();
public:
WSADATA data;
SOCKET sock_server;
SOCKET sock_client;
SOCKADDR_IN add_server;
SOCKADDR_IN add_client;
};
/*
* 服务端 .cpp 文件
*/
#include "Server.h"
#include<thread>
#define _CRT_SECURE_NO_WARINGS
bool Server::ready(int hton, const char* ip, int backlog)
{
if (intalize(hton, ip) && blind_listen(backlog) && server_connect())
{
return true;
}
return false;
}
Server::Server()
{
return;
}
Server::~Server()
{
WSACleanup();
closesocket(sock_client);
closesocket(sock_server);
return;
}
bool Server::intalize(int hton, const char* ip)
{
//socket版本检查
if (WSAStartup(MAKEWORD(2, 2), &data) != 0)
{
std::cout << "套接字的版本错误" << std::endl;
return false;
}
//套接字接口的配置
sock_server = socket(AF_INET, SOCK_STREAM, 0);
if (sock_server == INVALID_SOCKET)
{
std::cout << "设置失败" << std::endl;
return false;
}
//配置地址的设置
add_server.sin_family = AF_INET;
add_server.sin_port = htons(hton); //填写一个端口
add_server.sin_addr.S_un.S_addr = inet_addr(ip); //填写一个Ip地址
return true;
}
bool Server::blind_listen(int backlog) {
int n = sizeof(SOCKADDR_IN);
if ((bind(sock_server, (LPSOCKADDR)&add_server, n) == SOCKET_ERROR) || (listen(sock_server, backlog) == SOCKET_ERROR))
{
std::cout << "套接字绑定失败或者监听失败" << std::endl;
return false;
}
return true;
}
void serverThread(int sock_c);
//链接客户端 给每一个链接起一个线程
bool Server::server_connect()
{
std::cout << "请稍等。。。" << "\t" << "正在准备链接客户端" << std::endl;
Sleep(3000);
while (true)
{
int len = sizeof(SOCKADDR);
//首先开始链接客户端
sock_client = accept(sock_server, (LPSOCKADDR)&add_client, &len);
if (sock_client == SOCKET_ERROR)
{
std::cout << "链接失败" << std::endl;
break;
}
//起一个线程
std::thread(&serverThread,(int)sock_client).detach();
}
return true;
}
/*
* 服务端应用文件
*/
#include "Server.h"
int main()
{
Server a;
if (a.ready() == false) {
return -1;
}
return 0;
}
typedef struct
{
int ID;
char action[8]; //接受并且进行打印的数组
float speed;
float x;
float y;
float z;
}DATA;
void serverThread(int sock_c)
{
DATA requestData;
DATA responseData;
int i = 10;
while (true) {
if (recv((SOCKET)sock_c, (char*)&requestData, sizeof(DATA), 0) == SOCKET_ERROR) {
std::cout << "线程 " << GetCurrentThreadId() << " 退出" << std::endl;
return;
}
std::cout << "线程ID " << GetCurrentThreadId() <<
"客户端的信息是:" << requestData.ID << " " <<
requestData.action << " " << requestData.speed << " " <<
requestData.x << " " << requestData.y << " " << requestData.z << "\n" << std::endl;
responseData.ID = i;
strcpy(responseData.action, "move");
responseData.speed = i;
responseData.x = i;
responseData.y = i;
responseData.z = i;
if (send((SOCKET)sock_c, (const char*)&responseData, sizeof(DATA), 0) == SOCKET_ERROR)
{
std::cout << "线程 " << GetCurrentThreadId() << " 退出" << std::endl;
return;
}
++i;
}
}
/*
* 客户端 .h文件
*/
#pragma once
#pragma warning(disable:4996)
#pragma comment(lib,"ws2_32.lib")
#include<winsock2.h>
#include<windows.h>
#include <iostream>
typedef struct
{
int ID;
char action[8]; //接受并且进行打印的数组
float speed;
float x;
float y;
float z;
}DATA;
class Client
{
public:
Client();
~Client();
public:
bool ready();
bool sendInfor();
bool receiveInfor();
void setSendData(DATA &sendData);
DATA& getReceiveData();
private:
bool intalize(int hton = 10008, const char *ip = "127.0.0.1");
bool client_connect();
private:
SOCKET sock_client;
SOCKADDR_IN addr_server;
SOCKADDR_IN client_ADDR;
WSADATA data;
int len;
private:
DATA sendData;
DATA receiveData;
};
/*
* 客服端 .cpp 文件
*/
#include "Client.h"
bool Client::ready() {
if (intalize() && client_connect())
{
return true;
}
return false;
}
bool Client::sendInfor() {
if (send(sock_client, (const char*)&sendData, sizeof(DATA), 0) == SOCKET_ERROR)
{
std::cout << "发送失败" << std::endl;
return false;
}
return true;
}
bool Client::receiveInfor()
{
if (recv(sock_client, (char*)&receiveData, sizeof(DATA), 0) == SOCKET_ERROR)
{
std::cout << "接收失败" << std::endl;
return false;
}
std::cout << "线程ID " << GetCurrentThreadId() <<
" 服务器的信息是:" << receiveData.ID << "\n" << receiveData.action <<
"\n" << receiveData.speed << "\n" << receiveData.x << "\n" << receiveData.y <<
"\n" << receiveData.z << "\n" << std::endl;
return true;
}
void Client::setSendData(DATA& sendData)
{
this->sendData = sendData;
}
DATA& Client::getReceiveData()
{
return this->receiveData;
}
Client::Client()
{
return;
}
Client::~Client()
{
WSACleanup();
closesocket(sock_client);
return;
}
bool Client::intalize(int hton, const char* ip)
{
//socket版本检查
if (WSAStartup(MAKEWORD(2, 2), &data) != 0)
{
std::cout << "套接字的版本错误" << std::endl;
return false;
}
//套接字接口的配置
memset(&addr_server, 0, sizeof(addr_server));
sock_client = socket(AF_INET, SOCK_STREAM, 0);
if (sock_client == INVALID_SOCKET)
{
std::cout << "设置失败" << std::endl;
return false;
}
addr_server.sin_family = AF_INET;
addr_server.sin_port = htons(hton);//选择一个电脑的任意端口
addr_server.sin_addr.S_un.S_addr = inet_addr(ip);//填写一个Ip地址
len = sizeof(SOCKADDR_IN);
return true;
}
bool Client::client_connect()
{
if (connect(sock_client, (LPSOCKADDR)&addr_server, len) == SOCKET_ERROR)
{
std::cout << "连接失败" << std::endl;
return false;
}
return true;
}
/*
* 客服端应用文件
*/
#include "Client.h"
int main()
{
Client a;
if (a.ready()) {
DATA ser;
ser.ID = 1;
strcpy(ser.action, "success");
ser.speed = 1;
ser.x = 1;
ser.y = 1;
ser.z = 1;
a.setSendData(ser);
for (int i = 0 ; i < 100; ++i)
{
a.sendInfor();
a.receiveInfor();
Sleep(100);
}
}
system("pause");
return 0;
}