Windows下Socket网络通信,支持多客户端连接

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());
}
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值