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,但是需要connectif(::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 多线程)
相关文章推荐
- WINDOWS (服务器) 和 DOS(客户端) 网络互连 基于TCP/IP的编程实现
- python socket 创建tcp服务器和客户端改进版(实现半双工聊天)
- WINDOWS (服务器) 和 DOS(客户端) 网络互连 基于TCP/IP的编程实现
- WINDOWS (服务器) 和 DOS(客户端) 网络互连 基于TCP/IP的编程实现
- Python中的TCP编程,实现客户端与服务器的聊天(socket)
- WINDOWS (服务器) 和 DOS(客户端) 网络互连 基于TCP/IP的编程实现
- Java 实现TCP网络聊天[服务器-客户端]UI界面
- 初探基于TCP的服务器/客户端结构的聊天系统(三)之表情聊天的实现
- WINDOWS (服务器) 和 DOS(客户端) 网络互连 基于TCP/IP的编程实现
- WINDOWS (服务器) 和 DOS(客户端) 网络互连 基于TCP/IP的编程实现
- WINDOWS (服务器) 和 DOS(客户端) 网络互连 基于TCP/IP的编程实现
- WINDOWS (服务器) 和 DOS(客户端) 网络互连 基于TCP/IP的编程实现
- C# 网络编程之Tcp实现客户端和服务器聊天
- 初探基于TCP的服务器/客户端结构的聊天系统(四)视频聊天的实现
- Golang实现简单tcp服务器03 -- 文本广播式聊天服务器/客户端
- windows下TCP服务器和客户端的实现
- C#下如何实现服务器+客户端的聊天程序
- Select I/O模型来实现一个并发处理多个客户端的TCP服务器 <转>
- java 通过 socket 实现 服务器和客户端的通信 TCP