Windows下TCP聊天服务器与客户端雏形的实现

摘要

在学习游戏服务器搭建的过程中,需要使用Windows搭建TCP服务器与客户端,这里记录下遇到的错误与 所写的代码。

在blog最后bia了一个很挫的DEMO,仅供参考,后续解决后会继续发上来

服务器端

首先需要包含所需的头文件winsock2.h和ws2tcpip.h;

然后需要连接Winsock API 链接库文件ws2_32.lib,有以下两种方法

// (1) PRO文件中定义
LIBS += -lpthread libwsock32 libws2_32

// (2) Pragma

#pragma comment(lib,"ws2_32.lib")
  • (1) 初始化阶段调用WSAStartup函数,此函数在程序中初始化Windows Socket DLL,只有此函数调用成功后,程序才可以调用别的API:
WSADATA wsaData;
int nRet;
if((nRet = WSAStartup(MAKEWORD(2,2),&wsaData)) != 0){
    exit(0);
}
  • (2) 创建Socket
SOCKET s;
s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); // 使用流Socket,基于TCP协议
  • (3) 绑定端口
SOCKADDR_IN ServerAddr;
ServerAddr.sin_family = AF_INET;
ServerAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
ServerAddr.sin_port = htons(9000);

ret = ::bind(s, (LPSOCKADDR)&ServerAddr, sizeof(ServerAddr));
if(ret == SOCKET_ERROR)
{
    error = WSAGetLastError();
    std::cout<<"Binding Failed!"<<error<<std::endl;
    closesocket(s);
    s = NULL;
    WSACleanup();
    break;
}

如果使用者不在意地址或者端口的值,那么可以设定地址为INADDR_ANY,即Port为0,然后可以调用getsockname()来获知其被系统自动设定的值

SOCKADDR_IN ServerAddr;
ServerAddr.sin_family = AF_INET;
ServerAddr.sin_addr_s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(0);
::bind(s, (LPSOCKADDR)&ServerAddr, sizeof(ServerAddr));
  • (4) 监听
ret = listen(s,SOMAXCONN);
if(ret == SOCKET_ERROR){
    error = WSAGetLastError();
    std::cout<<"Listening Failed!"<<error<<std::endl;
    closesocket(s);
    s = NULL;
    WSACleanup();
    break;
}
  • (5) 接受请求
SOCKET temp;
temp = ::accept(s, (SOCKADDR*)&ServerAddr, (socklen_t*)&len);

客户端

与服务端的区别在于没有了bind,但是需要connect

if(::connect(s, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr)) == SOCKET_ERROR){
    std::cout<<"Connecting Failed!"<<WSAGetLastError()<<std::endl;
    ::closesocket(s);
    WSACleanup();
    return;
}else{
    std::cout<<"Connecting Succfully!"<<std::endl;
}

一个简单的QT聊天软件

服务器端

(1)头文件

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QAction>
#include <QLabel>
#include <QMenu>
#include <QSlider>
#include <QLineEdit>
#include <QPushButton>
#include <QScrollArea>
#include <QGridLayout>
#include <QMenuBar>
#include <QTimer>
#include <QFileDialog>
#include <QMessageBox>
#include <QApplication>

#include <winsock2.h>
#include <ws2tcpip.h>

#include <iostream>
#include <string>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void start_connect();

private:
    QWidget *centralWidget;
    QWidget *displayWidget;
    QWidget *controlWidget;

    QScrollArea *controlArea;
    QPushButton *button_connect;
    QLineEdit *edit_port;
    QLabel *lable_connetion_status;

};
#endif // MAINWINDOW_H

(2) cpp文件

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent)
{
    // Initiation of CentrlWidget
    centralWidget = new QWidget;
    setGeometry(100,100,1200,768);
    setWindowTitle("Fabric Simulator");
    setCentralWidget(centralWidget);

    // Initiation of displayWidget
    displayWidget = new QWidget;
    displayWidget->setSizeIncrement(500,768);

    QGridLayout *displayLayout = new QGridLayout;

    displayWidget->setLayout(displayLayout);

    // Initiation of ConsoleWidget
    controlWidget = new QWidget;
    button_connect = new QPushButton("Start Connection");
    connect(button_connect, SIGNAL(clicked()), this, SLOT(start_connect()));
    edit_port = new QLineEdit("9000");
    edit_port->setPlaceholderText("port");
    lable_connetion_status = new QLabel("Con Status:");


    QGridLayout *controlLayout = new QGridLayout;
    controlLayout->addWidget(edit_port,0,0,1,1);
    controlLayout->addWidget(button_connect,1,0,1,1);
    controlLayout->addWidget(lable_connetion_status,2,0,1,1);



    controlWidget->setLayout(controlLayout);


    controlArea = new QScrollArea;
    controlArea->setWidget(controlWidget);
    controlArea->setWidgetResizable(true);
    controlArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    controlArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    controlArea->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
    controlArea->setMinimumSize(50, 50);

    //将控件加入到主Widget里去
    QGridLayout *centralLayout = new QGridLayout;
    //centralLayout->addWidget(openGLWidgetArea, 0, 0, 1, 3);
    centralLayout->addWidget(displayWidget,0, 0, 1, 3);
    centralLayout->addWidget(controlArea, 0, 1, 1, 3);

    centralWidget->setLayout(centralLayout);
}


//SLOT Implementation
void MainWindow::start_connect()
{
    // !!注意:在Windows下执行socket相关操作前,必须初始化WSADATA!!!!
    WSADATA wsaData;
    int nRet;
    if((nRet = WSAStartup(MAKEWORD(2,2),&wsaData)) != 0){
        exit(0);
    }

    // (1) 初始化监听Socket
    SOCKET s;

    if((s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET){ // 使用流Socket,基于TCP协议
        std::cout<<"Socket Initiation Failed!"<<WSAGetLastError()<<std::endl;
        return;
    }else{
        std::cout<<"Socket Initiation Successfully!"<<std::endl;
    }

    SOCKADDR_IN ServerAddr;
    ServerAddr.sin_family = AF_INET;
    int port = this->edit_port->text().toInt();
    ServerAddr.sin_port = htons(port);
    ServerAddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    // (2)绑定Socket
    if(::bind(s, (LPSOCKADDR)&ServerAddr, sizeof(ServerAddr)) == SOCKET_ERROR){
       std::cout<<"Binding Failed!"<<WSAGetLastError()<<std::endl;
       ::closesocket(s);
       s = NULL;
       WSACleanup();
       return;
    }else{
       std::cout<<"Binding Succfully!"<<std::endl;
    }

    // (3)开始监听
    if(SOCKET_ERROR == ::listen(s, SOMAXCONN)){
        std::cout<<"Start Listening Failed!"<<WSAGetLastError()<<std::endl;
        ::closesocket(s);
        s = NULL;
        WSACleanup();
        return;
    }else{
        std::cout<<"Start Listening Succfully On Port: "<<port<<std::endl;
        this->lable_connetion_status->setText(QString("Con Status: Listening Succfully On Port:"+this->edit_port->text()));
    }

    // (4)检测新连接
    int len = sizeof(ServerAddr);
    while(1){ //循环等待新连接
        SOCKET temp = accept(s, (SOCKADDR*)&ServerAddr, (socklen_t*)&len);
        if(temp == INVALID_SOCKET){
            //链接失败
            std::cout<<"New Connection Established Failed!"<<WSAGetLastError()<<std::endl;
            ::closesocket(s);
            s = NULL;
            WSACleanup();
            break;
        }else{
            std::cout<<"New Connection Established Successfully!"<<std::endl;
            while(1){
                char buf[256];
                memset(buf, 0, sizeof(buf));
                int ret = recv(temp, buf, 256, 0);
                if(ret==0 || ret == SOCKET_ERROR){
                    std::cout<<"Received Failed!"<<WSAGetLastError()<<std::endl;
                    ::closesocket(temp);
                    break;
                }
                std::string str = std::string(buf);
                std::cout<<str<<std::endl;
            }
        }
    }

    ::closesocket(s);
    s = NULL;
    WSACleanup();
    std::cout<<"Socket Finished!"<<std::endl;
}

MainWindow::~MainWindow(){}

这里写图片描述
这里写图片描述

客户端

(1)头文件

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QAction>
#include <QLabel>
#include <QMenu>
#include <QSlider>
#include <QLineEdit>
#include <QPushButton>
#include <QScrollArea>
#include <QGridLayout>
#include <QMenuBar>
#include <QTimer>
#include <QFileDialog>
#include <QMessageBox>
#include <QApplication>

#include <winsock2.h>
#include <ws2tcpip.h>

#include <iostream>
#include <string>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void start_connect();
    void close_connect();
    void send_message();

private:
    SOCKET s;

    QWidget *centralWidget;
    QWidget *displayWidget;
    QWidget *controlWidget;

    QScrollArea *controlArea;
    QPushButton *button_connect;
    QPushButton *button_close;
    QPushButton *button_send;
    QLineEdit *edit_address;
    QLineEdit *edit_port;
    QLineEdit *edit_message;
    QLabel *lable_connetion_status;
};

#endif // MAINWINDOW_H

(2)cpp文件

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent)
{
    // Initiation of CentrlWidget
    centralWidget = new QWidget;
    setGeometry(100,100,1200,768);
    setWindowTitle("Fabric Simulator");
    setCentralWidget(centralWidget);

    // Initiation of displayWidget
    displayWidget = new QWidget;
    displayWidget->setSizeIncrement(500,768);

    QGridLayout *displayLayout = new QGridLayout;

    displayWidget->setLayout(displayLayout);

    // Initiation of ConsoleWidget
    controlWidget = new QWidget;
    button_connect = new QPushButton("Start Connection");
    connect(button_connect, SIGNAL(clicked()), this, SLOT(start_connect()));
    button_close = new QPushButton("Close Connection");
    connect(button_close, SIGNAL(clicked()), this, SLOT(close_connect()));
    button_send = new QPushButton("send");
    connect(button_send, SIGNAL(clicked()), this, SLOT(send_message()));
    edit_port = new QLineEdit("9000");
    edit_port->setPlaceholderText("port");
    edit_message = new QLineEdit("");
    edit_message->setPlaceholderText("message");
    lable_connetion_status = new QLabel("Con Status:");


    QGridLayout *controlLayout = new QGridLayout;
    controlLayout->addWidget(edit_port,0,0,1,2);
    controlLayout->addWidget(button_connect,1,0,1,2);
    controlLayout->addWidget(button_close,2,0,1,2);
    controlLayout->addWidget(edit_message,3,0,1,1);
    controlLayout->addWidget(button_send,3,1,1,1);
    controlLayout->addWidget(lable_connetion_status,4,0,1,2);

    controlWidget->setLayout(controlLayout);


    controlArea = new QScrollArea;
    controlArea->setWidget(controlWidget);
    controlArea->setWidgetResizable(true);
    controlArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    controlArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    controlArea->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
    controlArea->setMinimumSize(50, 50);

    //将控件加入到主Widget里去
    QGridLayout *centralLayout = new QGridLayout;
    //centralLayout->addWidget(openGLWidgetArea, 0, 0, 1, 3);
    centralLayout->addWidget(controlArea, 0, 0, 1, 3);

    centralWidget->setLayout(centralLayout);
}


//SLOT Implementation
void MainWindow::start_connect()
{
    // !!注意:在Windows下执行socket相关操作前,必须初始化WSADATA!!!!
    WSADATA wsaData;
    int nRet;
    if((nRet = WSAStartup(MAKEWORD(2,2),&wsaData)) != 0){
        std::cout<<"WSA Initiation Failed!"<<WSAGetLastError()<<std::endl;
        return;
    }

    // (1) 初始化Socket
    // SOCKET s;

    if((s = ::socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == INVALID_SOCKET){ // 使用流Socket,基于TCP协议
        std::cout<<"Socket Initiation Failed!"<<WSAGetLastError()<<std::endl;
        return;
    }else{
        std::cout<<"Socket Initiation Successfully!"<<std::endl;
    }

    // 设置目标服务器的地址
    SOCKADDR_IN ServerAddr;
    ServerAddr.sin_family = AF_INET;
    int port = this->edit_port->text().toInt();
    ServerAddr.sin_port = htons(port);
    ServerAddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    // (2)连接服务器
    if(::connect(s, (SOCKADDR*)&ServerAddr, sizeof(ServerAddr)) == SOCKET_ERROR){
        std::cout<<"Connecting Failed!"<<WSAGetLastError()<<std::endl;
        ::closesocket(s);
        WSACleanup();
        return;
    }else{
        std::cout<<"Connecting Succfully!"<<std::endl;
    }
}

void MainWindow::close_connect(){
    if(s!=NULL){
        ::closesocket(s);
        s = NULL;
        WSACleanup();
    }
    std::cout<<"Connection Closed!"<<std::endl;
}

void MainWindow::send_message(){
    // (3)发送消息
    int ret;

    std::string str = edit_message->text().toStdString();
    const char* ch = str.c_str();

    if((ret=::send(s, ch , strlen(ch) , 0))==SOCKET_ERROR){
        std::cout<<"Message Sending Failed!"<<WSAGetLastError()<<std::endl;
        ::closesocket(s);
        s = NULL;
        WSACleanup();
    }else{
        std::cout<<"Message Sending Successfully!"<<std::endl;
    }
}

MainWindow::~MainWindow(){}

这里写图片描述

这里写图片描述

如果发送成功:

这里写图片描述

服务器端就会显示客户端发来的信息:

这里写图片描述

总结

这个Demo实在很粗糙,而且不具有实用性,但是对于熟悉TCP通信基础来说已经足够了,有以下问题需要解决:

(1) 阻塞问题

(2) 多个用户连接(maybe 多线程)

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值