您的位置:首页 > 理论基础 > 计算机网络

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

2017-06-19 22:34 411 查看

摘要

在学习游戏服务器搭建的过程中,需要使用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协议
4000


(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);

ee34
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 多线程)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: