1.简单理解什么是Socket?
通俗理解Socket翻译成中文是套接字,同时也有插座的意思。可以按照插座的意思来理解它,插座就是连接电源和机器的中间件,同理,socket就是连接两个进程或应用的中间件,可以用作两个进程之间的通信。将这两个进程分别称为服务端和客户端。
socket顾名思义就是套接字的意思,用于描述地址和端口,是一个通信链的句柄。两个应用程序通过socket向网络发出请求或者回应,从而完成通信过程。socket编程有三种,流式套接字(SOCK_STREAM),数据报套接字(SOCK_DGRAM),原始套接字(SOCK_RAW),前两者较常用。基于TCP的socket编程是流式套接字,也是我们主要讲述描述的。
2.客户端client/服务端server 即C/S模式
client/server即C/S模式: TCP/IP通信中,主要是进行C/S交互。
**服务端Server:**建立socket,申明自身的port和IP,并绑定到socket,使用listen监听,然后不断用accept去查看是否有连接。如果有,捕获socket,并通过recv获取消息的内容,通信完成后调用closeSocket关闭这个对应accept到的socket。如果不需要等待任何客户端连接,那么用closeSocket直接关闭自身的socket。服务端采用了winsock搭建,配合C++的thread类,实现多客户端连接。在编程过程中,用到的函数可以通过微软文档进行查询。
微软文档链接地址
**客户端Client:**建立socket,通过端口号和地址确定目标服务器,使用Connect连接到服务器,send发送消息,等待处理,通信完成后调用closeSocket关闭socket。由于工作需求,客户端采用QTcpSocket来进行搭建。
3.代码实现
服务端,采用winsock建立:
#include <iostream>
#include <string.h>
#include <thread>
#include <vector>
#include <winsock.h>
#pragma comment(lib, "ws2_32.lib")
using namespace std;
void clientSocketDeal(SOCKET sock) {
char buffFromClient[1024];
while (true)
{
memset(buffFromClient, 0, sizeof(buffFromClient));
//recv为阻塞函数
int iLenOfRecvData = recv(sock, buffFromClient, sizeof(buffFromClient), 0);
if (iLenOfRecvData > 0) //如果接收的数据不为空
{
cout << "iLenOfRecvData: " << iLenOfRecvData << "buffFromClient: " << buffFromClient << endl;
}
else
{
cout << "客户端断开,退出当前连接..." << endl;
break;
}
cout << "发送消息====》" << endl;
char sendToClientBuff[] = "{\"Age\": 30,\"Name\" : \"Ace\",\"Sex\" : \"man\"}";
send(sock, sendToClientBuff, sizeof(sendToClientBuff), 0);
}
closesocket(sock);//关闭与该客户端的套接字
}
int main()
{
WORD wVersion = MAKEWORD(2, 2);
WSADATA wsadata;
if (WSAStartup(wVersion, &wsadata) != 0)
{
return 0;
}
SOCKET serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (serverSocket == INVALID_SOCKET)
{
cout << "socket error:" << WSAGetLastError() << endl;
WSACleanup();
return 0;
}
sockaddr_in serverAddr;
serverAddr.sin_family = AF_INET;
serverAddr.sin_addr.S_un.S_addr = INADDR_ANY;
serverAddr.sin_port = htons(8863);
if (bind(serverSocket, (SOCKADDR*)&serverAddr, sizeof(serverAddr)) == SOCKET_ERROR)
{
cout << "Bind Error!" << endl;
closesocket(serverSocket);
WSACleanup();
return 0;
}
if (listen(serverSocket, 20) == SOCKET_ERROR)
{
cout << "Listen Error !" << endl;
closesocket(serverSocket);
WSACleanup();
return 0;
}
vector<thread> clientSockVec;
cout << "等待客户端连接..." << endl;
while (true)
{
SOCKET clientSocket = INVALID_SOCKET;
sockaddr_in clientAddr;
int iAddrLength = sizeof(clientAddr);
//accept为阻塞函数
clientSocket = accept(serverSocket, (SOCKADDR*)&clientAddr, &iAddrLength);
if (clientSocket == INVALID_SOCKET)
{
cout << "Accept Error !" << WSAGetLastError() << endl;
closesocket(serverSocket);
WSACleanup();
break;
}
cout << "客户端地址:" << inet_ntoa(clientAddr.sin_addr) << ntohs(clientAddr.sin_port) << endl;
//数据交互
clientSockVec.push_back(thread(clientSocketDeal, clientSocket));
}
//待完善部分,这里不会被执行!!!
for (auto &var : clientSockVec){
var.join();
}
closesocket(serverSocket);
WSACleanup();
return 0;
}
客户端代码,客户端基于Qt实现:
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QJsonObject>
#include <QJsonDocument>
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent,Qt::CustomizeWindowHint),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_socket = new QTcpSocket(this);
m_socket->connectToHost("127.0.0.1", 8863);
connect(m_socket, &QTcpSocket::connected, this, [=]()
{
ui->label->setText("连接服务器成功 ");
QJsonObject obj;
obj.insert("Name", "Ace2222222222");
obj.insert("Sex", "man");
obj.insert("Age", 20);
QJsonDocument doc(obj);
sendData(QString(doc.toJson(QJsonDocument::Indented)));
});
connect(m_socket, &QTcpSocket::readyRead,this, [=]()
{
qDebug() << "----------------------------------------------------------------";
// 接收服务器发送的数据
QByteArray recvMsg = m_socket->readAll();
qDebug() << recvMsg;
ui->textEdit->setText(recvMsg);
});
connect(m_socket, &QTcpSocket::disconnected, this, [=]()
{
ui->label->setText("服务器已经断开了连接 ");
m_socket->close();
});
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::sendData(QString data)
{
// // 数据做base64转码
// QByteArray base64 = data.toUtf8().toBase64();
// QString le;
// // 预留8字节保存长度
// le.sprintf("%08d", base64.size());
// // 拼接数据 报文长度+报文
// base64 = le.toUtf8() + base64;
// 发送数据
m_socket->write(data.toStdString().c_str());
}