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

来源:互联网 发布:程序员初级职称 编辑:程序博客网 时间:2024/05/18 03:11

摘要

在学习游戏服务器搭建的过程中,需要使用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_OBJECTpublic:    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 Implementationvoid 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_OBJECTpublic:    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 Implementationvoid 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 0
原创粉丝点击