├── .gitignore ├── README.md ├── Server ├── TcpServerer.pro ├── eventdispatcher_libev │ ├── .gitignore │ ├── .gitmodules │ ├── .travis.yml │ ├── eventdispatcher_libev.cpp │ ├── eventdispatcher_libev.h │ ├── eventdispatcher_libev.pri │ ├── eventdispatcher_libev_p.cpp │ ├── eventdispatcher_libev_p.h │ ├── readme.txt │ ├── socknot_p.cpp │ └── timers_p.cpp ├── main.cpp ├── tcpserver.cpp ├── tcpserver.h ├── tcpsocket.cpp ├── tcpsocket.h ├── threadhandle.cpp └── threadhandle.h └── test-client ├── TestClient ├── TestClient.pro ├── main.cpp ├── mainwindow.cpp ├── mainwindow.h ├── mainwindow.ui ├── tcpsocket.cpp ├── tcpsocket.h ├── testsockets.cpp └── testsockets.h └── myTcpClient ├── main.cpp ├── mainwindow.cpp ├── mainwindow.h ├── mainwindow.ui └── myTcpClient.pro /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | 6 | # Compiled Dynamic libraries 7 | *.so 8 | *.dylib 9 | 10 | # Compiled Static libraries 11 | *.lai 12 | *.la 13 | *.a 14 | /myTcpClient/*.user 15 | 16 | /myQThreadTcpServer/*.user 17 | /myQTcpServer/*.user 18 | *.user 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | QtTcp---Server 2 | ============== 3 | 4 | ### QtTcp多线程Server 5 | > 继承自QTcpServer,多线程处理Qtcpsocket,有两种方式分配线程,固定线程和固定线程处理的连接数。 6 | > 7 | > 代码基于Qt5和C++11,建议win下用mingw版Qt。 8 | > 9 | > 代码中的信号槽用的用Qt5的新语法,以便使用lambda表达式, 10 | > 11 | > 所以有很多lambda表达式的应用。 12 | > 13 | > 14 | > 15 | > [我博客关于此实现的简单说明](http://www.dushibaiyu.com/2013/12/qtcpserver多线程实现.html)
16 | 17 | 注:此为我学习所写,实际应用很多地方没考虑到、、分配的地方应该还有可以优化地方,如果您发现问题请告诉我,有改进最好也告诉我下、、 18 | QLibeventTcpServer为用libevent的时间选择器、、速度好像的确有所增加,但是多线程的话就没有优势了、、 19 | ================= 20 | 每个线程有自己的事件循环。 21 | 现在线程分三级,主线程负责监听端口,新建链接,并移动到其他线程中。 22 | socket线程,负责接收和发送数据,一些小处理也可以放在其中。(线程数目可控) 23 | QtConcurrent : 负责处理发送过来的数据。(动态的线程数目。) 24 | 25 | 26 | =========================== 27 | Next: 28 | 重新设计下线程模型,现有的movetothread不是很好。 29 | 30 | ============== 31 | QLibeventTcpServer移到了old分支 32 | 还有原来的测试代码移动到了old分支 -------------------------------------------------------------------------------- /Server/TcpServerer.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2013-09-16T14:24:31 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core network 8 | 9 | TARGET = QThreadTcpServer 10 | TEMPLATE = app 11 | 12 | QT -= gui 13 | CONFIG += console 14 | CONFIG -= app_bundle 15 | CONFIG += C++11 16 | 17 | SOURCES += $$PWD/main.cpp\ 18 | $$PWD/tcpserver.cpp \ 19 | $$PWD/tcpsocket.cpp \ 20 | $$PWD/threadhandle.cpp 21 | 22 | HEADERS += \ 23 | $$PWD/tcpserver.h \ 24 | $$PWD/tcpsocket.h \ 25 | $$PWD/threadhandle.h 26 | 27 | unix:include($$PWD/eventdispatcher_libev/eventdispatcher_libev.pri) 28 | -------------------------------------------------------------------------------- /Server/eventdispatcher_libev/.gitignore: -------------------------------------------------------------------------------- 1 | *.a 2 | *.o 3 | *.moc 4 | *.prl 5 | *.pro.user 6 | moc_*.cpp 7 | Makefile 8 | Makefile.* 9 | /tests/tst_* 10 | -------------------------------------------------------------------------------- /Server/eventdispatcher_libev/.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "tests/qt_eventdispatcher_tests"] 2 | path = tests/qt_eventdispatcher_tests 3 | url = git://github.com/sjinks/qt_eventdispatcher_tests.git 4 | -------------------------------------------------------------------------------- /Server/eventdispatcher_libev/.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | 3 | compiler: 4 | - gcc 5 | - clang 6 | 7 | branches: 8 | only: 9 | - master 10 | 11 | before_install: 12 | - git submodule update --init --recursive 13 | 14 | install: 15 | - sudo apt-get update -qq 16 | - sudo apt-get install -qq libqt4-dev libqt4-private-dev libev4 libev-dev 17 | 18 | script: 19 | - ./build.sh 20 | -------------------------------------------------------------------------------- /Server/eventdispatcher_libev/eventdispatcher_libev.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "eventdispatcher_libev.h" 5 | #include "eventdispatcher_libev_p.h" 6 | 7 | 8 | EventDispatcherLibEv::EventDispatcherLibEv(QObject* parent) 9 | : QAbstractEventDispatcher(parent), d_ptr(new EventDispatcherLibEvPrivate(this)) 10 | { 11 | } 12 | 13 | EventDispatcherLibEv::~EventDispatcherLibEv(void) 14 | { 15 | } 16 | 17 | bool EventDispatcherLibEv::processEvents(QEventLoop::ProcessEventsFlags flags) 18 | { 19 | Q_D(EventDispatcherLibEv); 20 | return d->processEvents(flags); 21 | } 22 | 23 | bool EventDispatcherLibEv::hasPendingEvents(void) 24 | { 25 | extern uint qGlobalPostedEventsCount(); 26 | return qGlobalPostedEventsCount() > 0; 27 | } 28 | 29 | void EventDispatcherLibEv::registerSocketNotifier(QSocketNotifier* notifier) 30 | { 31 | #ifndef QT_NO_DEBUG 32 | if (notifier->socket() < 0) { 33 | qWarning("QSocketNotifier: Internal error: sockfd < 0"); 34 | return; 35 | } 36 | 37 | if (notifier->thread() != thread() || thread() != QThread::currentThread()) { 38 | qWarning("QSocketNotifier: socket notifiers cannot be enabled from another thread"); 39 | return; 40 | } 41 | #endif 42 | 43 | if (notifier->type() == QSocketNotifier::Exception) { 44 | return; 45 | } 46 | 47 | Q_D(EventDispatcherLibEv); 48 | d->registerSocketNotifier(notifier); 49 | } 50 | 51 | void EventDispatcherLibEv::unregisterSocketNotifier(QSocketNotifier* notifier) 52 | { 53 | #ifndef QT_NO_DEBUG 54 | if (notifier->socket() < 0) { 55 | qWarning("QSocketNotifier: Internal error: sockfd < 0"); 56 | return; 57 | } 58 | 59 | if (notifier->thread() != thread() || thread() != QThread::currentThread()) { 60 | qWarning("QSocketNotifier: socket notifiers cannot be disabled from another thread"); 61 | return; 62 | } 63 | #endif 64 | 65 | // Short circuit, we do not support QSocketNotifier::Exception 66 | if (notifier->type() == QSocketNotifier::Exception) { 67 | return; 68 | } 69 | 70 | Q_D(EventDispatcherLibEv); 71 | d->unregisterSocketNotifier(notifier); 72 | } 73 | 74 | void EventDispatcherLibEv::registerTimer(int timerId,int interval, 75 | Qt::TimerType timerType,QObject* object) 76 | { 77 | #ifndef QT_NO_DEBUG 78 | if (timerId < 1 || interval < 0 || !object) { 79 | qWarning("%s: invalid arguments", Q_FUNC_INFO); 80 | return; 81 | } 82 | 83 | if (object->thread() != this->thread() && this->thread() != QThread::currentThread()) { 84 | qWarning("%s: timers cannot be started from another thread", Q_FUNC_INFO); 85 | return; 86 | } 87 | #endif 88 | 89 | Qt::TimerType type; 90 | type = timerType; 91 | 92 | Q_D(EventDispatcherLibEv); 93 | if (interval) { 94 | d->registerTimer(timerId, interval, type, object); 95 | } 96 | else { 97 | d->registerZeroTimer(timerId, object); 98 | } 99 | } 100 | 101 | bool EventDispatcherLibEv::unregisterTimer(int timerId) 102 | { 103 | #ifndef QT_NO_DEBUG 104 | if (timerId < 1) { 105 | qWarning("%s: invalid arguments", Q_FUNC_INFO); 106 | return false; 107 | } 108 | 109 | if (this->thread() != QThread::currentThread()) { 110 | qWarning("%s: timers cannot be stopped from another thread", Q_FUNC_INFO); 111 | return false; 112 | } 113 | #endif 114 | 115 | Q_D(EventDispatcherLibEv); 116 | return d->unregisterTimer(timerId); 117 | } 118 | 119 | bool EventDispatcherLibEv::unregisterTimers(QObject* object) 120 | { 121 | #ifndef QT_NO_DEBUG 122 | if (!object) { 123 | qWarning("%s: invalid arguments", Q_FUNC_INFO); 124 | return false; 125 | } 126 | 127 | if (object->thread() != this->thread() && this->thread() != QThread::currentThread()) { 128 | qWarning("%s: timers cannot be stopped from another thread", Q_FUNC_INFO); 129 | return false; 130 | } 131 | #endif 132 | 133 | Q_D(EventDispatcherLibEv); 134 | return d->unregisterTimers(object); 135 | } 136 | 137 | QList EventDispatcherLibEv::registeredTimers(QObject* object) const 138 | { 139 | if (!object) { 140 | qWarning("%s: invalid argument", Q_FUNC_INFO); 141 | return QList(); 142 | } 143 | 144 | Q_D(const EventDispatcherLibEv); 145 | return d->registeredTimers(object); 146 | } 147 | 148 | int EventDispatcherLibEv::remainingTime(int timerId) 149 | { 150 | Q_D(const EventDispatcherLibEv); 151 | return d->remainingTime(timerId); 152 | } 153 | 154 | void EventDispatcherLibEv::wakeUp(void) 155 | { 156 | Q_D(EventDispatcherLibEv); 157 | 158 | if (d->m_wakeups.testAndSetAcquire(0, 1)) 159 | { 160 | ev_async_send(d->m_base, &d->m_wakeup); 161 | } 162 | } 163 | 164 | void EventDispatcherLibEv::interrupt(void) 165 | { 166 | Q_D(EventDispatcherLibEv); 167 | d->m_interrupt = true; 168 | this->wakeUp(); 169 | } 170 | 171 | void EventDispatcherLibEv::flush(void) 172 | { 173 | } 174 | 175 | EventDispatcherLibEv::EventDispatcherLibEv(EventDispatcherLibEvPrivate& dd, QObject* parent) 176 | : QAbstractEventDispatcher(parent), d_ptr(&dd) 177 | { 178 | } 179 | -------------------------------------------------------------------------------- /Server/eventdispatcher_libev/eventdispatcher_libev.h: -------------------------------------------------------------------------------- 1 | #ifndef EVENTDISPATCHER_LIBEV_H 2 | #define EVENTDISPATCHER_LIBEV_H 3 | 4 | #include 5 | 6 | class EventDispatcherLibEvPrivate; 7 | 8 | class EventDispatcherLibEv : public QAbstractEventDispatcher 9 | { 10 | Q_OBJECT 11 | public: 12 | explicit EventDispatcherLibEv(QObject* parent = 0); 13 | virtual ~EventDispatcherLibEv(void); 14 | 15 | virtual bool processEvents(QEventLoop::ProcessEventsFlags flags); 16 | virtual bool hasPendingEvents(void); 17 | 18 | virtual void registerSocketNotifier(QSocketNotifier* notifier); 19 | virtual void unregisterSocketNotifier(QSocketNotifier* notifier); 20 | 21 | virtual void registerTimer(int timerId,int interval, 22 | Qt::TimerType timerType,QObject* object); 23 | 24 | virtual bool unregisterTimer(int timerId); 25 | virtual bool unregisterTimers(QObject* object); 26 | virtual QList registeredTimers(QObject* object) const; 27 | virtual int remainingTime(int timerId); 28 | 29 | virtual void wakeUp(void); 30 | virtual void interrupt(void); 31 | virtual void flush(void); 32 | 33 | protected: 34 | EventDispatcherLibEv(EventDispatcherLibEvPrivate& dd, QObject* parent = 0); 35 | 36 | private: 37 | Q_DISABLE_COPY(EventDispatcherLibEv) 38 | Q_DECLARE_PRIVATE(EventDispatcherLibEv) 39 | QScopedPointer d_ptr; 40 | }; 41 | 42 | #endif // EVENTDISPATCHER_LIBEV_H 43 | -------------------------------------------------------------------------------- /Server/eventdispatcher_libev/eventdispatcher_libev.pri: -------------------------------------------------------------------------------- 1 | HEADERS += \ 2 | $$PWD/eventdispatcher_libev.h \ 3 | $$PWD/eventdispatcher_libev_p.h 4 | SOURCES += \ 5 | $$PWD/eventdispatcher_libev.cpp \ 6 | $$PWD/eventdispatcher_libev_p.cpp \ 7 | $$PWD/timers_p.cpp \ 8 | $$PWD/socknot_p.cpp 9 | 10 | LIBS += -lev 11 | 12 | -------------------------------------------------------------------------------- /Server/eventdispatcher_libev/eventdispatcher_libev_p.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "eventdispatcher_libev.h" 3 | #include "eventdispatcher_libev_p.h" 4 | 5 | EventDispatcherLibEvPrivate::EventDispatcherLibEvPrivate(EventDispatcherLibEv* const q) 6 | : q_ptr(q), m_interrupt(false), m_base(0), m_wakeup(),m_wakeups(), 7 | m_notifiers(), m_timers(), m_event_list(), m_zero_timers(), m_awaken(false) 8 | { 9 | this->m_base = ev_loop_new(EVFLAG_AUTO); 10 | Q_CHECK_PTR(this->m_base != 0); 11 | 12 | ev_set_userdata(this->m_base, this); 13 | 14 | struct ev_async* tmp = &this->m_wakeup; 15 | ev_async_init(tmp, EventDispatcherLibEvPrivate::wake_up_handler); 16 | ev_async_start(this->m_base, &this->m_wakeup); 17 | } 18 | 19 | EventDispatcherLibEvPrivate::~EventDispatcherLibEvPrivate(void) 20 | { 21 | if (this->m_base) { 22 | ev_async_stop(this->m_base, &this->m_wakeup); 23 | 24 | this->killTimers(); 25 | this->killSocketNotifiers(); 26 | 27 | ev_loop_destroy(this->m_base); 28 | this->m_base = 0; 29 | } 30 | } 31 | 32 | bool EventDispatcherLibEvPrivate::processEvents(QEventLoop::ProcessEventsFlags flags) 33 | { 34 | Q_Q(EventDispatcherLibEv); 35 | 36 | const bool exclude_notifiers = (flags & QEventLoop::ExcludeSocketNotifiers); 37 | const bool exclude_timers = (flags & QEventLoop::X11ExcludeTimers); 38 | 39 | exclude_notifiers && this->disableSocketNotifiers(true); 40 | exclude_timers && this->disableTimers(true); 41 | 42 | this->m_interrupt = false; 43 | this->m_awaken = false; 44 | 45 | bool result = q->hasPendingEvents(); 46 | 47 | Q_EMIT q->awake(); 48 | QCoreApplication::sendPostedEvents(); 49 | 50 | bool can_wait = !this->m_interrupt && (flags & QEventLoop::WaitForMoreEvents) && !result; 51 | int f = EVLOOP_NONBLOCK; 52 | 53 | if (!this->m_interrupt) { 54 | if (!exclude_timers && this->m_zero_timers.size() > 0) { 55 | result |= this->processZeroTimers(); 56 | if (result) { 57 | can_wait = false; 58 | } 59 | } 60 | 61 | if (can_wait) { 62 | Q_EMIT q->aboutToBlock(); 63 | f = EVLOOP_ONESHOT; 64 | } 65 | 66 | // Work around a bug when libev returns from ev_loop(loop, EVLOOP_ONESHOT) without processing any events 67 | do { 68 | ev_loop(this->m_base, f); 69 | } while (can_wait && !this->m_awaken && !this->m_event_list.size()); 70 | 71 | EventList list; 72 | this->m_event_list.swap(list); 73 | 74 | result |= (list.size() > 0) | this->m_awaken; 75 | 76 | for (int i=0; itype() == QEvent::Timer) { 90 | QTimerEvent* te = static_cast(e.second); 91 | TimerHash::Iterator tit = this->m_timers.find(te->timerId()); 92 | if (tit != this->m_timers.end()) { 93 | TimerInfo* info = tit.value(); 94 | 95 | struct ev_timer* tmp = &info->ev; 96 | if (!ev_is_pending(tmp) && !ev_is_active(tmp)) { // false in tst_QTimer::restartedTimerFiresTooSoon() 97 | ev_tstamp delta = calculateNextTimeout(info, now); 98 | ev_timer_set(tmp, delta, 0); 99 | ev_timer_start(this->m_base, tmp); 100 | } 101 | } 102 | } 103 | 104 | delete e.second; 105 | } 106 | } 107 | 108 | exclude_notifiers && this->disableSocketNotifiers(false); 109 | exclude_timers && this->disableTimers(false); 110 | 111 | return result; 112 | } 113 | 114 | bool EventDispatcherLibEvPrivate::processZeroTimers(void) 115 | { 116 | bool result = false; 117 | QList ids = this->m_zero_timers.keys(); 118 | 119 | for (int i=0; im_zero_timers.find(tid); 122 | if (it != this->m_zero_timers.end()) { 123 | ZeroTimer& data = it.value(); 124 | if (data.active) { 125 | data.active = false; 126 | 127 | QTimerEvent event(tid); 128 | QCoreApplication::sendEvent(data.object, &event); 129 | result = true; 130 | 131 | it = this->m_zero_timers.find(tid); 132 | if (it != this->m_zero_timers.end()) { 133 | ZeroTimer& data = it.value(); 134 | if (!data.active) { 135 | data.active = true; 136 | } 137 | } 138 | } 139 | } 140 | } 141 | 142 | return result; 143 | } 144 | 145 | void EventDispatcherLibEvPrivate::wake_up_handler(struct ev_loop* loop, struct ev_async* w, int revents) 146 | { 147 | Q_UNUSED(w) 148 | Q_UNUSED(revents) 149 | 150 | EventDispatcherLibEvPrivate* disp = reinterpret_cast(ev_userdata(loop)); 151 | disp->m_awaken = true; 152 | 153 | if (!disp->m_wakeups.testAndSetRelease(1, 0)) { 154 | qCritical("%s: internal error, wakeUps.testAndSetRelease(1, 0) failed!", Q_FUNC_INFO); 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /Server/eventdispatcher_libev/eventdispatcher_libev_p.h: -------------------------------------------------------------------------------- 1 | #ifndef EVENTDISPATCHER_LIBEV_P_H 2 | #define EVENTDISPATCHER_LIBEV_P_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | struct TimerInfo { 13 | QObject* object; 14 | struct ev_timer ev; 15 | struct timeval when; 16 | int timerId; 17 | int interval; 18 | Qt::TimerType type; 19 | }; 20 | 21 | struct ZeroTimer { 22 | QObject* object; 23 | bool active; 24 | }; 25 | 26 | Q_DECLARE_TYPEINFO(TimerInfo, Q_PRIMITIVE_TYPE); 27 | Q_DECLARE_TYPEINFO(ZeroTimer, Q_PRIMITIVE_TYPE); 28 | 29 | ev_tstamp calculateNextTimeout(TimerInfo* info, const struct timeval& now); 30 | 31 | class EventDispatcherLibEv; 32 | 33 | class EventDispatcherLibEvPrivate 34 | { 35 | public: 36 | EventDispatcherLibEvPrivate(EventDispatcherLibEv * const q); 37 | ~EventDispatcherLibEvPrivate(void); 38 | bool processEvents(QEventLoop::ProcessEventsFlags flags); 39 | bool processZeroTimers(void); 40 | void registerSocketNotifier(QSocketNotifier* notifier); 41 | void unregisterSocketNotifier(QSocketNotifier* notifier); 42 | void registerTimer(int timerId, int interval, Qt::TimerType type, QObject* object); 43 | void registerZeroTimer(int timerId, QObject* object); 44 | bool unregisterTimer(int timerId); 45 | bool unregisterTimers(QObject* object); 46 | QList registeredTimers(QObject* object) const; 47 | int remainingTime(int timerId) const; 48 | 49 | typedef QMultiHash SocketNotifierHash; 50 | typedef QHash TimerHash; 51 | typedef QPair, QEvent*> PendingEvent; 52 | typedef QList EventList; 53 | typedef QHash ZeroTimerHash; 54 | 55 | private: 56 | Q_DISABLE_COPY(EventDispatcherLibEvPrivate) 57 | Q_DECLARE_PUBLIC(EventDispatcherLibEv) 58 | EventDispatcherLibEv* const q_ptr; 59 | 60 | bool m_interrupt; 61 | struct ev_loop* m_base; 62 | struct ev_async m_wakeup; 63 | QAtomicInt m_wakeups; 64 | SocketNotifierHash m_notifiers; 65 | TimerHash m_timers; 66 | EventList m_event_list; 67 | ZeroTimerHash m_zero_timers; 68 | bool m_awaken; 69 | 70 | static void socket_notifier_callback(struct ev_loop* loop, struct ev_io* w, int revents); 71 | static void timer_callback(struct ev_loop* loop, struct ev_timer* w, int revents); 72 | static void wake_up_handler(struct ev_loop* loop, struct ev_async* w, int revents); 73 | 74 | bool disableSocketNotifiers(bool disable); 75 | void killSocketNotifiers(void); 76 | bool disableTimers(bool disable); 77 | void killTimers(void); 78 | }; 79 | 80 | #endif // EVENTDISPATCHER_LIBEV_P_H 81 | -------------------------------------------------------------------------------- /Server/eventdispatcher_libev/readme.txt: -------------------------------------------------------------------------------- 1 | 此来自大牛的封装,我根据需求进行了部分改动和精简,源代码库地址:https://github.com/sjinks/qt_eventdispatcher_libev 2 | -------------------------------------------------------------------------------- /Server/eventdispatcher_libev/socknot_p.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "eventdispatcher_libev_p.h" 5 | 6 | void EventDispatcherLibEvPrivate::registerSocketNotifier(QSocketNotifier* notifier) 7 | { 8 | int sockfd = notifier->socket(); 9 | int what; 10 | switch (notifier->type()) { 11 | case QSocketNotifier::Read: what = EV_READ; break; 12 | case QSocketNotifier::Write: what = EV_WRITE; break; 13 | case QSocketNotifier::Exception: /// FIXME 14 | return; 15 | 16 | default: 17 | Q_ASSERT(false); 18 | return; 19 | } 20 | 21 | struct ev_io* data = new struct ev_io; 22 | ev_io_init(data, EventDispatcherLibEvPrivate::socket_notifier_callback, sockfd, what); 23 | data->data = notifier; 24 | ev_io_start(this->m_base, data); 25 | 26 | this->m_notifiers.insertMulti(sockfd, data); 27 | } 28 | 29 | void EventDispatcherLibEvPrivate::unregisterSocketNotifier(QSocketNotifier* notifier) 30 | { 31 | int sockfd = notifier->socket(); 32 | SocketNotifierHash::Iterator it = this->m_notifiers.find(sockfd); 33 | while (it != this->m_notifiers.end() && it.key() == sockfd) { 34 | struct ev_io* data = it.value(); 35 | if (data->data == notifier) { 36 | ev_io_stop(this->m_base, data); 37 | delete data; 38 | it = this->m_notifiers.erase(it); 39 | } 40 | else { 41 | ++it; 42 | } 43 | } 44 | } 45 | 46 | void EventDispatcherLibEvPrivate::socket_notifier_callback(struct ev_loop* loop, struct ev_io* w, int revents) 47 | { 48 | EventDispatcherLibEvPrivate* disp = reinterpret_cast(ev_userdata(loop)); 49 | 50 | SocketNotifierHash::Iterator it = disp->m_notifiers.find(w->fd); 51 | while (it != disp->m_notifiers.end() && it.key() == w->fd) { 52 | struct ev_io* data = it.value(); 53 | QSocketNotifier* notifier = reinterpret_cast(data->data); 54 | QSocketNotifier::Type type = notifier->type(); 55 | 56 | if ((QSocketNotifier::Read == type && (revents & EV_READ)) || (QSocketNotifier::Write == type && (revents & EV_WRITE))) { 57 | PendingEvent event(notifier, new QEvent(QEvent::SockAct)); 58 | disp->m_event_list.append(event); 59 | } 60 | 61 | ++it; 62 | } 63 | } 64 | 65 | bool EventDispatcherLibEvPrivate::disableSocketNotifiers(bool disable) 66 | { 67 | SocketNotifierHash::Iterator it = this->m_notifiers.begin(); 68 | while (it != this->m_notifiers.end()) { 69 | struct ev_io* data = it.value(); 70 | if (disable) { 71 | ev_io_stop(this->m_base, data); 72 | } 73 | else { 74 | ev_io_start(this->m_base, data); 75 | } 76 | 77 | ++it; 78 | } 79 | 80 | return true; 81 | } 82 | 83 | void EventDispatcherLibEvPrivate::killSocketNotifiers(void) 84 | { 85 | if (!this->m_notifiers.isEmpty()) { 86 | SocketNotifierHash::Iterator it = this->m_notifiers.begin(); 87 | while (it != this->m_notifiers.end()) { 88 | struct ev_io* data = it.value(); 89 | ev_io_stop(this->m_base, data); 90 | delete data; 91 | ++it; 92 | } 93 | 94 | this->m_notifiers.clear(); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /Server/eventdispatcher_libev/timers_p.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "eventdispatcher_libev_p.h" 5 | 6 | #ifdef WIN32 7 | # include "win32_utils.h" 8 | #endif 9 | 10 | namespace { 11 | 12 | static void calculateCoarseTimerTimeout(TimerInfo* info, const struct timeval& now, struct timeval& when) 13 | { 14 | Q_ASSERT(info->interval > 20); 15 | // The coarse timer works like this: 16 | // - interval under 40 ms: round to even 17 | // - between 40 and 99 ms: round to multiple of 4 18 | // - otherwise: try to wake up at a multiple of 25 ms, with a maximum error of 5% 19 | // 20 | // We try to wake up at the following second-fraction, in order of preference: 21 | // 0 ms 22 | // 500 ms 23 | // 250 ms or 750 ms 24 | // 200, 400, 600, 800 ms 25 | // other multiples of 100 26 | // other multiples of 50 27 | // other multiples of 25 28 | // 29 | // The objective is to make most timers wake up at the same time, thereby reducing CPU wakeups. 30 | 31 | int interval = info->interval; 32 | int msec = static_cast(info->when.tv_usec / 1000); 33 | int max_rounding = interval / 20; // 5% 34 | when = info->when; 35 | 36 | if (interval < 100 && (interval % 25) != 0) { 37 | if (interval < 50) { 38 | uint round_up = ((msec % 50) >= 25) ? 1 : 0; 39 | msec = ((msec >> 1) | round_up) << 1; 40 | } 41 | else { 42 | uint round_up = ((msec % 100) >= 50) ? 1 : 0; 43 | msec = ((msec >> 2) | round_up) << 2; 44 | } 45 | } 46 | else { 47 | int min = qMax(0, msec - max_rounding); 48 | int max = qMin(1000, msec + max_rounding); 49 | 50 | bool done = false; 51 | 52 | // take any round-to-the-second timeout 53 | if (min == 0) { 54 | msec = 0; 55 | done = true; 56 | } 57 | else if (max == 1000) { 58 | msec = 1000; 59 | done = true; 60 | } 61 | 62 | if (!done) { 63 | int boundary; 64 | 65 | // if the interval is a multiple of 500 ms and > 5000 ms, always round towards a round-to-the-second 66 | // if the interval is a multiple of 500 ms, round towards the nearest multiple of 500 ms 67 | if ((interval % 500) == 0) { 68 | if (interval >= 5000) { 69 | msec = msec >= 500 ? max : min; 70 | done = true; 71 | } 72 | else { 73 | boundary = 500; 74 | } 75 | } 76 | else if ((interval % 50) == 0) { 77 | // same for multiples of 250, 200, 100, 50 78 | uint tmp = interval / 50; 79 | if ((tmp % 4) == 0) { 80 | boundary = 200; 81 | } 82 | else if ((tmp % 2) == 0) { 83 | boundary = 100; 84 | } 85 | else if ((tmp % 5) == 0) { 86 | boundary = 250; 87 | } 88 | else { 89 | boundary = 50; 90 | } 91 | } 92 | else { 93 | boundary = 25; 94 | } 95 | 96 | if (!done) { 97 | int base = (msec / boundary) * boundary; 98 | int middle = base + boundary / 2; 99 | msec = (msec < middle) ? qMax(base, min) : qMin(base + boundary, max); 100 | } 101 | } 102 | } 103 | 104 | if (msec == 1000) { 105 | ++when.tv_sec; 106 | when.tv_usec = 0; 107 | } 108 | else { 109 | when.tv_usec = msec * 1000; 110 | } 111 | 112 | if (timercmp(&when, &now, <)) { 113 | when.tv_sec += interval / 1000; 114 | when.tv_usec += (interval % 1000) * 1000; 115 | if (when.tv_usec > 999999) { 116 | ++when.tv_sec; 117 | when.tv_usec -= 1000000; 118 | } 119 | } 120 | 121 | Q_ASSERT(timercmp(&now, &when, <=)); 122 | } 123 | } 124 | 125 | ev_tstamp calculateNextTimeout(TimerInfo* info, const struct timeval& now) 126 | { 127 | struct timeval tv_interval; 128 | struct timeval when; 129 | tv_interval.tv_sec = info->interval / 1000; 130 | tv_interval.tv_usec = (info->interval % 1000) * 1000; 131 | 132 | if (info->interval) { 133 | qlonglong tnow = (qlonglong(now.tv_sec) * 1000) + (now.tv_usec / 1000); 134 | qlonglong twhen = (qlonglong(info->when.tv_sec) * 1000) + (info->when.tv_usec / 1000); 135 | 136 | if ((info->interval < 1000 && twhen - tnow > 1500) || (info->interval >= 1000 && twhen - tnow > 1.2*info->interval)) { 137 | info->when = now; 138 | } 139 | } 140 | 141 | if (Qt::VeryCoarseTimer == info->type) { 142 | if (info->when.tv_usec >= 500000) { 143 | ++info->when.tv_sec; 144 | } 145 | 146 | info->when.tv_usec = 0; 147 | info->when.tv_sec += info->interval / 1000; 148 | if (info->when.tv_sec <= now.tv_sec) { 149 | info->when.tv_sec = now.tv_sec + info->interval / 1000; 150 | } 151 | 152 | when = info->when; 153 | } 154 | else if (Qt::PreciseTimer == info->type) { 155 | if (info->interval) { 156 | timeradd(&info->when, &tv_interval, &info->when); 157 | if (timercmp(&info->when, &now, <)) { 158 | timeradd(&now, &tv_interval, &info->when); 159 | } 160 | 161 | when = info->when; 162 | } 163 | else { 164 | when = now; 165 | } 166 | } 167 | else { 168 | timeradd(&info->when, &tv_interval, &info->when); 169 | if (timercmp(&info->when, &now, <)) { 170 | timeradd(&now, &tv_interval, &info->when); 171 | } 172 | 173 | calculateCoarseTimerTimeout(info, now, when); 174 | } 175 | 176 | return static_cast(when.tv_sec - now.tv_sec) + static_cast(when.tv_usec - now.tv_usec) * 1.0E-6; 177 | } 178 | 179 | 180 | void EventDispatcherLibEvPrivate::registerTimer(int timerId, int interval, Qt::TimerType type, QObject* object) 181 | { 182 | Q_ASSERT(interval > 0); 183 | 184 | struct timeval now; 185 | gettimeofday(&now, 0); 186 | 187 | TimerInfo* info = new TimerInfo; 188 | info->timerId = timerId; 189 | info->interval = interval; 190 | info->type = type; 191 | info->object = object; 192 | info->when = now; // calculateNextTimeout() will take care of info->when 193 | 194 | if (Qt::CoarseTimer == type) { 195 | if (interval >= 20000) { 196 | info->type = Qt::VeryCoarseTimer; 197 | } 198 | else if (interval <= 20) { 199 | info->type = Qt::PreciseTimer; 200 | } 201 | } 202 | 203 | ev_tstamp delta = calculateNextTimeout(info, now); 204 | 205 | struct ev_timer* tmp = &info->ev; 206 | ev_timer_init(tmp, EventDispatcherLibEvPrivate::timer_callback, delta, 0); 207 | info->ev.data = info; 208 | ev_timer_start(this->m_base, tmp); 209 | this->m_timers.insert(timerId, info); 210 | } 211 | 212 | void EventDispatcherLibEvPrivate::registerZeroTimer(int timerId, QObject* object) 213 | { 214 | ZeroTimer data; 215 | data.object = object; 216 | data.active = true; 217 | this->m_zero_timers.insert(timerId, data); 218 | } 219 | 220 | bool EventDispatcherLibEvPrivate::unregisterTimer(int timerId) 221 | { 222 | TimerHash::Iterator it = this->m_timers.find(timerId); 223 | if (it != this->m_timers.end()) { 224 | TimerInfo* info = it.value(); 225 | ev_timer_stop(this->m_base, &info->ev); 226 | delete info; 227 | this->m_timers.erase(it); 228 | return true; 229 | } 230 | 231 | return this->m_zero_timers.remove(timerId) > 0; 232 | } 233 | 234 | bool EventDispatcherLibEvPrivate::unregisterTimers(QObject* object) 235 | { 236 | bool result = false; 237 | TimerHash::Iterator it = this->m_timers.begin(); 238 | while (it != this->m_timers.end()) { 239 | TimerInfo* info = it.value(); 240 | 241 | if (object == info->object) { 242 | result = true; 243 | ev_timer_stop(this->m_base, &info->ev); 244 | delete info; 245 | it = this->m_timers.erase(it); 246 | } 247 | else { 248 | ++it; 249 | } 250 | } 251 | 252 | ZeroTimerHash::Iterator zit = this->m_zero_timers.begin(); 253 | while (zit != this->m_zero_timers.end()) { 254 | ZeroTimer& data = zit.value(); 255 | if (object == data.object) { 256 | result = true; 257 | zit = this->m_zero_timers.erase(zit); 258 | } 259 | else { 260 | ++zit; 261 | } 262 | } 263 | 264 | return result; 265 | } 266 | 267 | QList EventDispatcherLibEvPrivate::registeredTimers(QObject* object) const 268 | { 269 | QList res; 270 | 271 | TimerHash::ConstIterator it = this->m_timers.constBegin(); 272 | while (it != this->m_timers.constEnd()) { 273 | TimerInfo* info = it.value(); 274 | if (object == info->object) { 275 | QAbstractEventDispatcher::TimerInfo ti(it.key(), info->interval, info->type); 276 | res.append(ti); 277 | } 278 | 279 | ++it; 280 | } 281 | 282 | ZeroTimerHash::ConstIterator zit = this->m_zero_timers.constBegin(); 283 | while (zit != this->m_zero_timers.constEnd()) { 284 | const ZeroTimer& data = zit.value(); 285 | if (object == data.object) { 286 | QAbstractEventDispatcher::TimerInfo ti(it.key(), 0, Qt::PreciseTimer); 287 | res.append(ti); 288 | } 289 | 290 | ++zit; 291 | } 292 | 293 | return res; 294 | } 295 | 296 | int EventDispatcherLibEvPrivate::remainingTime(int timerId) const 297 | { 298 | TimerHash::ConstIterator it = this->m_timers.find(timerId); 299 | if (it != this->m_timers.end()) { 300 | const TimerInfo* info = it.value(); 301 | 302 | const struct ev_timer* tmp = &info->ev; 303 | if (ev_is_pending(tmp) || ev_is_active(tmp)) { 304 | ev_tstamp now = ev_now(this->m_base); 305 | ev_tstamp when = static_cast(info->when.tv_sec) + static_cast(info->when.tv_usec) * 1.0E-6; 306 | 307 | if (now > when) { 308 | return 0; 309 | } 310 | 311 | return static_cast((when - now) * 1000); 312 | } 313 | } 314 | 315 | // For zero timers we return -1 as well 316 | 317 | return -1; 318 | } 319 | 320 | void EventDispatcherLibEvPrivate::timer_callback(struct ev_loop* loop, struct ev_timer* w, int revents) 321 | { 322 | Q_UNUSED(revents) 323 | 324 | EventDispatcherLibEvPrivate* self = reinterpret_cast(ev_userdata(loop)); 325 | TimerInfo* info = reinterpret_cast(w->data); 326 | 327 | // Timer can be reactivated only after its callback finishes; processEvents() will take care of this 328 | PendingEvent event(info->object, new QTimerEvent(info->timerId)); 329 | self->m_event_list.append(event); 330 | } 331 | 332 | bool EventDispatcherLibEvPrivate::disableTimers(bool disable) 333 | { 334 | struct timeval now; 335 | if (!disable) { 336 | gettimeofday(&now, 0); 337 | } 338 | 339 | TimerHash::Iterator it = this->m_timers.begin(); 340 | while (it != this->m_timers.end()) { 341 | TimerInfo* info = it.value(); 342 | if (disable) { 343 | ev_timer_stop(this->m_base, &info->ev); 344 | } 345 | else { 346 | ev_tstamp delta = calculateNextTimeout(info, now); 347 | struct ev_timer* tmp = &info->ev; 348 | ev_timer_set(tmp, delta, 0); 349 | ev_timer_start(this->m_base, tmp); 350 | } 351 | 352 | ++it; 353 | } 354 | 355 | return true; 356 | } 357 | 358 | void EventDispatcherLibEvPrivate::killTimers(void) 359 | { 360 | if (!this->m_timers.isEmpty()) { 361 | TimerHash::Iterator it = this->m_timers.begin(); 362 | while (it != this->m_timers.end()) { 363 | TimerInfo* info = it.value(); 364 | ev_timer_stop(this->m_base, &info->ev); 365 | delete info; 366 | ++it; 367 | } 368 | 369 | this->m_timers.clear(); 370 | } 371 | } 372 | -------------------------------------------------------------------------------- /Server/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "tcpserver.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #ifndef Q_OS_WIN 9 | #include "eventdispatcher_libev/eventdispatcher_libev.h" 10 | #endif 11 | 12 | void customMessageHandler(QtMsgType type, const QMessageLogContext &context,const QString & msg) 13 | { 14 | QString txt; 15 | switch (type) { 16 | //调试信息提示 17 | case QtDebugMsg: 18 | // txt = QString("%1: Debug: at:%2,%3 on %4; %5").arg(QTime::currentTime().toString("hh:mm:ss.zzz")) 19 | // .arg(context.file).arg(context.line).arg(context.function).arg(msg); 20 | txt = msg; 21 | break; 22 | 23 | //一般的warning提示 24 | case QtWarningMsg: 25 | txt = QString("%1:Warning: at:%2,%3 on %4; %5").arg(QTime::currentTime().toString("hh:mm:ss.zzz")) 26 | .arg(context.file).arg(context.line).arg(context.function).arg(msg); 27 | break; 28 | //严重错误提示 29 | case QtCriticalMsg: 30 | txt = QString("%1:Critical: at:%2,%3 on %4; %5").arg(QTime::currentTime().toString("hh:mm:ss.zzz")) 31 | .arg(context.file).arg(context.line).arg(context.function).arg(msg); 32 | break; 33 | //致命错误提示 34 | case QtFatalMsg: 35 | txt = QString("%1:Fatal: at:%2,%3 on %4; %5").arg(QTime::currentTime().toString("hh:mm:ss.zzz")) 36 | .arg(context.file).arg(context.line).arg(context.function).arg(msg); 37 | abort(); 38 | } 39 | QFile outFile(QString("%1/%2.txt").arg(QDir::currentPath()).arg(QDate::currentDate().toString("yyyy-MM-dd"))); 40 | outFile.open(QIODevice::WriteOnly | QIODevice::Append); 41 | QTextStream ts(&outFile); 42 | ts << txt << endl; 43 | } 44 | 45 | 46 | int main(int argc, char *argv[]) 47 | { 48 | // qInstallMessageHandler(customMessageHandler); 49 | #ifndef Q_OS_WIN 50 | QCoreApplication::setEventDispatcher(new EventDispatcherLibEv()); 51 | #endif 52 | QCoreApplication a(argc, argv); 53 | std::cout << "???" << std::endl; 54 | TcpServer ser; 55 | ser.listen(QHostAddress::Any,6666); 56 | 57 | return a.exec(); 58 | } 59 | -------------------------------------------------------------------------------- /Server/tcpserver.cpp: -------------------------------------------------------------------------------- 1 | #include "tcpserver.h" 2 | #include "threadhandle.h" 3 | 4 | TcpServer::TcpServer(QObject *parent,int numConnections) : 5 | QTcpServer(parent) 6 | { 7 | tcpClient = new QHash; 8 | setMaxPendingConnections(numConnections); 9 | } 10 | 11 | TcpServer::~TcpServer() 12 | { 13 | emit this->sentDisConnect(-1); 14 | delete tcpClient; 15 | } 16 | 17 | void TcpServer::setMaxPendingConnections(int numConnections) 18 | { 19 | this->QTcpServer::setMaxPendingConnections(numConnections);//调用Qtcpsocket函数,设置最大连接数,主要是使maxPendingConnections()依然有效 20 | this->maxConnections = numConnections; 21 | } 22 | 23 | void TcpServer::incomingConnection(qintptr socketDescriptor) //多线程必须在此函数里捕获新连接 24 | { 25 | if (tcpClient->size() > maxPendingConnections())//继承重写此函数后,QTcpServer默认的判断最大连接数失效,自己实现 26 | { 27 | QTcpSocket tcp; 28 | tcp.setSocketDescriptor(socketDescriptor); 29 | tcp.disconnectFromHost(); 30 | return; 31 | } 32 | auto th = ThreadHandle::getClass().getThread(); 33 | auto tcpTemp = new TcpSocket(socketDescriptor); 34 | QString ip = tcpTemp->peerAddress().toString(); 35 | qint16 port = tcpTemp->peerPort(); 36 | 37 | connect(tcpTemp,&TcpSocket::sockDisConnect,this,&TcpServer::sockDisConnectSlot);//NOTE:断开连接的处理,从列表移除,并释放断开的Tcpsocket,此槽必须实现,线程管理计数也是考的他 38 | connect(this,&TcpServer::sentDisConnect,tcpTemp,&TcpSocket::disConTcp);//断开信号 39 | 40 | tcpTemp->moveToThread(th);//把tcp类移动到新的线程,从线程管理类中获取 41 | tcpClient->insert(socketDescriptor,tcpTemp);//插入到连接信息中 42 | emit connectClient(socketDescriptor,ip,port); 43 | } 44 | 45 | void TcpServer::sockDisConnectSlot(int handle,const QString & ip, quint16 prot,QThread * th) 46 | { 47 | tcpClient->remove(handle);//连接管理中移除断开连接的socket 48 | ThreadHandle::getClass().removeThread(th); //告诉线程管理类那个线程里的连接断开了 49 | emit sockDisConnect(handle,ip,prot); 50 | } 51 | 52 | 53 | void TcpServer::clear() 54 | { 55 | emit this->sentDisConnect(-1); 56 | ThreadHandle::getClass().clear(); 57 | tcpClient->clear(); 58 | } 59 | -------------------------------------------------------------------------------- /Server/tcpserver.h: -------------------------------------------------------------------------------- 1 | #ifndef TCPSERVER_H 2 | #define TCPSERVER_H 3 | 4 | #include 5 | #include 6 | #include "tcpsocket.h" 7 | 8 | 9 | //继承QTCPSERVER以实现多线程TCPscoket的服务器。 10 | //如果socket的信息处理直接处理的话,很多新建的信号和槽是用不到的 11 | class TcpServer : public QTcpServer 12 | { 13 | Q_OBJECT 14 | public: 15 | explicit TcpServer(QObject *parent = 0,int numConnections = 10000); 16 | ~TcpServer(); 17 | 18 | void setMaxPendingConnections(int numConnections);//重写设置最大连接数函数 19 | signals: 20 | void connectClient(const int , const QString & ,const quint16 );//发送新用户连接信息 21 | void readData(const int,const QString &, quint16, const QByteArray &);//发送获得用户发过来的数据 22 | void sockDisConnect(int ,QString ,quint16);//断开连接的用户信息 23 | void sentData(const QByteArray &,const int);//向scoket发送消息 24 | void sentDisConnect(int i); //断开特定连接,并释放资源,-1为断开所有。 25 | 26 | public slots: 27 | void clear(); //断开所有连接,线程计数器请0 28 | protected slots: 29 | void sockDisConnectSlot(int handle,const QString & ip, quint16 prot, QThread *th);//断开连接的用户信息 30 | 31 | protected: 32 | void incomingConnection(qintptr socketDescriptor);//覆盖已获取多线程 33 | private: 34 | QHash * tcpClient;//管理连接的map 35 | int maxConnections; 36 | 37 | }; 38 | 39 | #endif // TCPSERVER_H 40 | -------------------------------------------------------------------------------- /Server/tcpsocket.cpp: -------------------------------------------------------------------------------- 1 | #include "tcpsocket.h" 2 | #include 3 | #include 4 | #include 5 | 6 | TcpSocket::TcpSocket(qintptr socketDescriptor, QObject *parent) : //构造函数在主线程执行,lambda在子线程 7 | QTcpSocket(parent),socketID(socketDescriptor) 8 | { 9 | this->setSocketDescriptor(socketDescriptor); 10 | connect(this,&TcpSocket::readyRead,this,&TcpSocket::readData); 11 | dis = connect(this,&TcpSocket::disconnected, 12 | [&](){ 13 | qDebug() << "disconnect" ; 14 | emit sockDisConnect(socketID,this->peerAddress().toString(),this->peerPort(),QThread::currentThread());//发送断开连接的用户信息 15 | this->deleteLater(); 16 | }); 17 | connect(&watcher,&QFutureWatcher::finished,this,&TcpSocket::startNext); 18 | connect(&watcher,&QFutureWatcher::canceled,this,&TcpSocket::startNext); 19 | qDebug() << "new connect" ; 20 | } 21 | 22 | TcpSocket::~TcpSocket() 23 | { 24 | } 25 | 26 | 27 | void TcpSocket::sentData(const QByteArray &data, const int id) 28 | { 29 | if(id == socketID) 30 | { 31 | write(data); 32 | } 33 | } 34 | 35 | void TcpSocket::disConTcp(int i) 36 | { 37 | if (i == socketID) 38 | { 39 | this->disconnectFromHost(); 40 | } 41 | else if (i == -1) //-1为全部断开 42 | { 43 | disconnect(dis); //先断开连接的信号槽,防止二次析构 44 | this->disconnectFromHost(); 45 | this->deleteLater(); 46 | } 47 | } 48 | 49 | void TcpSocket::readData() 50 | { 51 | // datas.append(this->readAll()); 52 | auto data = handleData(this->readAll(),this->peerAddress().toString(),this->peerPort()); 53 | qDebug() << data; 54 | this->write(data); 55 | // if (!watcher.isRunning())//放到异步线程中处理。 56 | // { 57 | // watcher.setFuture(QtConcurrent::run(this,&TcpSocket::handleData,datas.dequeue(),this->peerAddress().toString(),this->peerPort())); 58 | // } 59 | } 60 | 61 | QByteArray TcpSocket::handleData(QByteArray data, const QString &ip, qint16 port) 62 | { 63 | QTime time; 64 | time.start(); 65 | 66 | QElapsedTimer tm; 67 | tm.start(); 68 | while(tm.elapsed() < 100) 69 | {} 70 | data = ip.toUtf8() + " " + QByteArray::number(port) + " " + data + " " + QTime::currentTime().toString().toUtf8(); 71 | return data; 72 | } 73 | 74 | void TcpSocket::startNext() 75 | { 76 | this->write(watcher.future().result()); 77 | if (!datas.isEmpty()) 78 | { 79 | watcher.setFuture(QtConcurrent::run(this,&TcpSocket::handleData,datas.dequeue(),this->peerAddress().toString(),this->peerPort())); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Server/tcpsocket.h: -------------------------------------------------------------------------------- 1 | #ifndef TCPSOCKET_H 2 | #define TCPSOCKET_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | 12 | class TcpSocket : public QTcpSocket 13 | { 14 | Q_OBJECT 15 | public: 16 | explicit TcpSocket(qintptr socketDescriptor, QObject *parent = 0); 17 | ~TcpSocket(); 18 | QByteArray handleData(QByteArray data,const QString & ip, qint16 port);//用来处理数据的函数 19 | 20 | signals: 21 | //void readData(const int,const QString &,const quint16,const QByteArray &); 22 | void sockDisConnect(const int ,const QString &,const quint16, QThread *);//NOTE:断开连接的用户信息,此信号必须发出!线程管理类根据信号计数的 23 | public slots: 24 | void sentData(const QByteArray & ,const int);//发送信号的槽 25 | void disConTcp(int i); 26 | 27 | protected slots: 28 | void readData();//接收数据 29 | void startNext();//处理下一个 30 | protected: 31 | QFutureWatcher watcher; 32 | QQueue datas; 33 | private: 34 | qintptr socketID; 35 | QMetaObject::Connection dis; 36 | }; 37 | 38 | #endif // TCPSOCKET_H 39 | -------------------------------------------------------------------------------- /Server/threadhandle.cpp: -------------------------------------------------------------------------------- 1 | #include "threadhandle.h" 2 | #include "eventdispatcher_libev/eventdispatcher_libev.h" 3 | 4 | ThreadHandle::ThreadHandle() 5 | { 6 | initfist = false; 7 | } 8 | 9 | ThreadHandle::~ThreadHandle() //停止所有线程,并释放资源 10 | { 11 | QThread * tmp; 12 | for (auto it = threadSize.begin(); it != threadSize.end(); ++it) 13 | { 14 | tmp = it.key(); 15 | tmp->exit(); 16 | tmp->wait(3000); 17 | delete tmp; 18 | } 19 | } 20 | 21 | ThreadHandle & ThreadHandle::getClass() 22 | { 23 | static ThreadHandle th; 24 | return th; 25 | } 26 | 27 | QThread *ThreadHandle::getThread() 28 | { 29 | if (!initfist) 30 | { 31 | initThreadType(THREADSIZE,10); 32 | } 33 | if (type == THREADSIZE) 34 | return findThreadSize(); 35 | else 36 | return findHandleSize(); 37 | } 38 | 39 | void ThreadHandle::removeThread(QThread * thread) 40 | { 41 | auto t = threadSize.find(thread); 42 | if (t != threadSize.end()) 43 | { 44 | t.value() --; 45 | if (type == HANDLESIZE && t.value() == 0 && threadSize.size() > 1) 46 | { 47 | threadSize.remove(thread); 48 | thread->exit(); 49 | thread->wait(3000); 50 | delete thread; 51 | } 52 | } 53 | } 54 | 55 | void ThreadHandle::initThreadType(ThreadType type, unsigned int max) 56 | { 57 | if (!initfist) 58 | { 59 | this->type = type; 60 | this->size = max; 61 | if (this->size == 0) 62 | { 63 | if(type == THREADSIZE) 64 | this->size = 10; 65 | else 66 | this->size = 1000; 67 | } 68 | 69 | if (type == THREADSIZE) 70 | initThreadSize(); 71 | else 72 | { 73 | QThread * tmp = new QThread; 74 | #ifndef Q_OS_WIN 75 | tmp->setEventDispatcher(new EventDispatcherLibEv()); 76 | #endif 77 | threadSize.insert(tmp,0); 78 | tmp->start(); 79 | } 80 | } 81 | initfist = true; 82 | } 83 | 84 | void ThreadHandle::initThreadSize() //建立好线程并启动, 85 | { 86 | QThread * tmp; 87 | for (unsigned int i = 0; i < size;++i) 88 | { 89 | tmp = new QThread; 90 | #ifndef Q_OS_WIN 91 | tmp->setEventDispatcher(new EventDispatcherLibEv()); 92 | #endif 93 | threadSize.insert(tmp,0); 94 | tmp->start(); 95 | } 96 | } 97 | 98 | QThread * ThreadHandle::findHandleSize() //查找到线程里的连接数小于最大值就返回查找到的,找不到就新建一个线程 99 | { 100 | for (auto it = threadSize.begin();it != threadSize.end() ;++it) 101 | { 102 | if (it.value() < size) 103 | { 104 | it.value() ++; 105 | return it.key(); 106 | } 107 | } 108 | QThread * tmp = new QThread; 109 | #ifndef Q_OS_WIN 110 | tmp->setEventDispatcher(new EventDispatcherLibEv()); 111 | #endif 112 | threadSize.insert(tmp,1); 113 | tmp->start(); 114 | return tmp; 115 | } 116 | 117 | QThread *ThreadHandle::findThreadSize() //遍历查找所有线程中连接数最小的那个,返回 118 | { 119 | auto it = threadSize.begin(); 120 | auto ite = threadSize.begin(); 121 | for (++it ; it != threadSize.end(); ++it) 122 | { 123 | if (it.value() < ite.value()) 124 | { 125 | ite = it; 126 | } 127 | } 128 | ite.value() ++; 129 | return ite.key(); 130 | } 131 | 132 | void ThreadHandle::clear()//仅仅清空计数,线程不释放 133 | { 134 | for (auto it = threadSize.begin();it != threadSize.end() ;++it) 135 | { 136 | it.value() = 0; 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /Server/threadhandle.h: -------------------------------------------------------------------------------- 1 | #ifndef THREADHANDLE_H 2 | #define THREADHANDLE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | //线程管理类,类似于一个线程池,单例类 9 | //两种初始化方式,一种是每个线程处理的连接数,一个是一共多少个线程 10 | class ThreadHandle 11 | { 12 | public: 13 | ~ThreadHandle(); 14 | 15 | 16 | enum ThreadType 17 | { 18 | THREADSIZE, //固定线程数 19 | HANDLESIZE //固定每个线程处理多少连接 20 | }; 21 | 22 | static ThreadHandle & getClass(); //返回对象引用,是单例类 23 | 24 | QThread * getThread(); //取出应该移入的线程 25 | void initThreadType(ThreadType type = HANDLESIZE,unsigned int max = 1000);//初始化线程管理的方式 26 | void removeThread(QThread *);//连接断开,线程计数减一 27 | void clear();//清空计数 28 | 29 | protected: 30 | void initThreadSize();//新建固定线程和启动 31 | QThread * findThreadSize();//固定线程数的查找 32 | QThread *findHandleSize();//固定连接数查找 33 | private: 34 | ThreadHandle(); 35 | ThreadType type;//线程类型 36 | unsigned int size;//最大值 37 | QHash threadSize;//保存每个线程的数目 38 | bool initfist;//是否是第一次初始化,只允许初始化一次。 39 | 40 | }; 41 | 42 | #endif // THREADHANDLE_H 43 | -------------------------------------------------------------------------------- /test-client/TestClient/TestClient.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2013-09-18T08:44:16 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core gui network 8 | 9 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 10 | 11 | TARGET = TestClient 12 | TEMPLATE = app 13 | 14 | CONFIG += C++11 15 | 16 | SOURCES += main.cpp\ 17 | mainwindow.cpp \ 18 | testsockets.cpp \ 19 | tcpsocket.cpp 20 | 21 | HEADERS += mainwindow.h \ 22 | testsockets.h \ 23 | tcpsocket.h 24 | 25 | FORMS += mainwindow.ui 26 | -------------------------------------------------------------------------------- /test-client/TestClient/main.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | QApplication a(argc, argv); 7 | MainWindow w; 8 | w.show(); 9 | 10 | return a.exec(); 11 | } 12 | -------------------------------------------------------------------------------- /test-client/TestClient/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include "ui_mainwindow.h" 3 | 4 | MainWindow::MainWindow(QWidget *parent) : 5 | QMainWindow(parent), 6 | ui(new Ui::MainWindow) 7 | { 8 | ui->setupUi(this); 9 | this->ui->timeBut->setEnabled(false); 10 | this->ui->txtIp->setText("127.0.0.1"); 11 | this->ui->txtPort->setText("6666"); 12 | } 13 | 14 | MainWindow::~MainWindow() 15 | { 16 | delete ui; 17 | } 18 | 19 | void MainWindow::on_pushConnect_clicked() 20 | { 21 | qDebug() << "点击连接:" ; 22 | if ("连接" == this->ui->pushConnect->text()) 23 | { 24 | QString ipAdd(this->ui->txtIp->text()), portd(this->ui->txtPort->text()); 25 | if (ipAdd.isEmpty() || portd.isEmpty()) 26 | { 27 | this->ui->textEdit->append("请输入IP和端口!"); 28 | return; 29 | } 30 | if(tcps == nullptr) 31 | { 32 | tcps = new TestSockets; 33 | connect(this,&MainWindow::con,tcps,&TestSockets::connectsocket); 34 | connect(this,&MainWindow::discon,tcps,&TestSockets::disconnectsoc); 35 | connect(tcps,&TestSockets::sentstring,[&](const QString & str){this->ui->textEdit->append(str);}); 36 | connect(&tm,&QTimer::timeout,tcps,&TestSockets::sent); 37 | } 38 | emit con(this->ui->txtIp->text(),this->ui->txtPort->text().toInt()); 39 | ui->pushConnect->setText("断开"); 40 | ui->textEdit->append("连接服务器成功"); 41 | this->ui->txtIp->setEnabled(false); 42 | this->ui->txtPort->setEnabled(false); 43 | this->ui->timeBut->setEnabled(true); 44 | 45 | } 46 | else 47 | { 48 | ui->pushConnect->setText("连接"); 49 | ui->textEdit->append("断开服务器"); 50 | this->ui->txtIp->setEnabled(true); 51 | this->ui->txtPort->setEnabled(true); 52 | emit this->discon(); 53 | tm.stop(); 54 | this->ui->timeBut->setEnabled(false); 55 | this->ui->lineEdit->setEnabled(true); 56 | this->ui->timeBut->setText("启动定时"); 57 | 58 | } 59 | } 60 | 61 | void MainWindow::on_timeBut_clicked() 62 | { 63 | if (this->ui->lineEdit->text().isEmpty()) 64 | { 65 | this->ui->textEdit->append("请输入时间:"); 66 | return; 67 | } 68 | if ("启动定时" == this->ui->timeBut->text()) 69 | { 70 | int h = this->ui->lineEdit->text().toInt(); 71 | h = h*1000; 72 | tm.start(h); 73 | this->ui->lineEdit->setEnabled(false); 74 | this->ui->timeBut->setText("停止定时"); 75 | } 76 | else 77 | { 78 | tm.stop(); 79 | this->ui->timeBut->setText("启动定时"); 80 | this->ui->lineEdit->setEnabled(true); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /test-client/TestClient/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include 6 | #include "testsockets.h" 7 | 8 | namespace Ui { 9 | class MainWindow; 10 | } 11 | 12 | class MainWindow : public QMainWindow 13 | { 14 | Q_OBJECT 15 | 16 | public: 17 | explicit MainWindow(QWidget *parent = 0); 18 | ~MainWindow(); 19 | 20 | signals: 21 | void con(const QString & str, int port); 22 | void discon(); 23 | 24 | public slots: 25 | void on_pushConnect_clicked(); 26 | void on_timeBut_clicked(); 27 | 28 | // void setdata(const QString & str); 29 | private: 30 | Ui::MainWindow * ui; 31 | TestSockets * tcps = nullptr; 32 | QTimer tm; 33 | }; 34 | 35 | #endif // MAINWINDOW_H 36 | -------------------------------------------------------------------------------- /test-client/TestClient/mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 431 10 | 466 11 | 12 | 13 | 14 | MainWindow 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | IP地址: 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 0 35 | 0 36 | 37 | 38 | 39 | QLineEdit::Normal 40 | 41 | 42 | 43 | 44 | 45 | 46 | 端口: 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 0 55 | 0 56 | 57 | 58 | 59 | 60 | 60 61 | 0 62 | 63 | 64 | 65 | 66 | 60 67 | 16777215 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 连接 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | Qt::Horizontal 87 | 88 | 89 | 90 | 188 91 | 18 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 间隔时间(s): 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 50 108 | 20 109 | 110 | 111 | 112 | 113 | 50 114 | 0 115 | 116 | 117 | 118 | 119 | 50 120 | 16777215 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 启动定时 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /test-client/TestClient/tcpsocket.cpp: -------------------------------------------------------------------------------- 1 | #include "tcpsocket.h" 2 | #include 3 | #include 4 | 5 | TcpSocket::TcpSocket(QObject *parent) : 6 | QTcpSocket(parent) 7 | { 8 | connect(this,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(ReadError(QAbstractSocket::SocketError))); 9 | connect(this,&TcpSocket::readyRead,[&](){QString str(QString::number(this->peerPort())); str += " : "; 10 | str += this->readAll(); emit this->sentdata(str);}); 11 | } 12 | 13 | void TcpSocket::cth(const QString &host, int port) 14 | { 15 | this->connectToHost(host,port); 16 | this->waitForConnected(5000); 17 | } 18 | 19 | void TcpSocket::sent() 20 | { 21 | if (this->state() == QAbstractSocket::ConnectedState) 22 | { 23 | this->write(QUuid::createUuid().toByteArray()); 24 | tm.start(); 25 | } 26 | } 27 | 28 | void TcpSocket::ReadError(QAbstractSocket::SocketError) 29 | { 30 | QString str(QString::number(this->peerPort())); 31 | str += " : erro : "; 32 | str += this->errorString(); 33 | emit senterro(str); 34 | } 35 | -------------------------------------------------------------------------------- /test-client/TestClient/tcpsocket.h: -------------------------------------------------------------------------------- 1 | #ifndef TCPSOCKET_H 2 | #define TCPSOCKET_H 3 | 4 | #include 5 | #include 6 | 7 | 8 | class TcpSocket : public QTcpSocket 9 | { 10 | Q_OBJECT 11 | public: 12 | explicit TcpSocket(QObject *parent = 0); 13 | 14 | signals: 15 | void sentdata(const QString & str); 16 | void senterro(const QString & erro); 17 | public slots: 18 | void cth(const QString &host, int port); 19 | void sent(); 20 | 21 | void ReadError(QAbstractSocket::SocketError); 22 | 23 | protected: 24 | QTime tm; 25 | }; 26 | 27 | #endif // TCPSOCKET_H 28 | -------------------------------------------------------------------------------- /test-client/TestClient/testsockets.cpp: -------------------------------------------------------------------------------- 1 | #include "testsockets.h" 2 | 3 | TestSockets::TestSockets(QObject *parent) : 4 | QObject(parent) 5 | { 6 | QThread * th; 7 | th = new QThread(this); 8 | th->start(); 9 | for (int i = 1; i <= 500 ; ++i) 10 | { 11 | TcpSocket * tmp = new TcpSocket; 12 | connect(this,&TestSockets::connectsocket,tmp,&TcpSocket::cth); 13 | connect(this,&TestSockets::sent,tmp,&TcpSocket::sent); 14 | connect(tmp,&TcpSocket::sentdata,this,&TestSockets::sentstring); 15 | connect(tmp,&TcpSocket::senterro,this,&TestSockets::sentstring); 16 | connect(this,&TestSockets::disconnectsoc,tmp,&TcpSocket::disconnectFromHost); 17 | if (i %20 == 0) 18 | { 19 | th = new QThread(this); 20 | th->start(); 21 | } 22 | tmp->moveToThread(th); 23 | } 24 | } 25 | 26 | TestSockets::~TestSockets() 27 | { 28 | qDeleteAll(tcps); 29 | } 30 | -------------------------------------------------------------------------------- /test-client/TestClient/testsockets.h: -------------------------------------------------------------------------------- 1 | #ifndef TESTSOCKETS_H 2 | #define TESTSOCKETS_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "tcpsocket.h" 8 | 9 | class TestSockets : public QObject 10 | { 11 | Q_OBJECT 12 | public: 13 | explicit TestSockets(QObject *parent = 0); 14 | ~TestSockets(); 15 | 16 | signals: 17 | void sent(); 18 | void connectsocket(const QString & ip, int port); 19 | void sentstring(const QString & str); 20 | void disconnectsoc(); 21 | public slots: 22 | 23 | private: 24 | QList tcps; 25 | }; 26 | 27 | #endif // TESTSOCKETS_H 28 | -------------------------------------------------------------------------------- /test-client/myTcpClient/main.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | QApplication a(argc, argv); 7 | MainWindow w; 8 | w.show(); 9 | 10 | return a.exec(); 11 | } 12 | -------------------------------------------------------------------------------- /test-client/myTcpClient/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include "ui_mainwindow.h" 3 | 4 | MainWindow::MainWindow(QWidget *parent) : 5 | QMainWindow(parent), 6 | ui(new Ui::MainWindow) 7 | { 8 | ui->setupUi(this); 9 | tcpClient = new QTcpSocket(this); 10 | ui->pushSent->setEnabled(false); 11 | this->ui->timeBut->setEnabled(false); 12 | tcpClient->abort(); 13 | connect(tcpClient,&QTcpSocket::readyRead, 14 | [&](){this->ui->textEdit->append(tr("%1 Server Say:%2").arg(QTime::currentTime().toString("hh:mm:ss.zzz")).arg(QString(this->tcpClient->readAll())));}); 15 | connect(tcpClient,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(ReadError(QAbstractSocket::SocketError))); 16 | connect(&tm,&QTimer::timeout,[&](){ 17 | int i = qrand() % 6; 18 | this->ui->textEdit->append(tr("%1 Timer Sent: %2").arg(QTime::currentTime().toString("hh:mm:ss.zzz")).arg(list.at(i))); 19 | tcpClient->write(list.at(i).toUtf8()); 20 | }); 21 | connect(tcpClient,&QTcpSocket::disconnected,[](){qDebug()<< "123333" ;}); 22 | list << "我是谁?" << "渡世白玉" << "hello" << "哈哈哈哈哈" << "你是坏蛋!" << "测试一下下了" << "不知道写什么" ; 23 | QTime time; 24 | time= QTime::currentTime(); 25 | qsrand(time.msec()+time.second()*1000); 26 | this->ui->txtIp->setText("127.0.0.1"); 27 | this->ui->txtPort->setText("6666"); 28 | } 29 | 30 | MainWindow::~MainWindow() 31 | { 32 | delete ui; 33 | delete tcpClient; 34 | } 35 | 36 | void MainWindow::on_pushConnect_clicked() 37 | { 38 | qDebug() << "点击连接:" ; 39 | if ("连接" == this->ui->pushConnect->text()) 40 | { 41 | QString ipAdd(this->ui->txtIp->text()), portd(this->ui->txtPort->text()); 42 | if (ipAdd.isEmpty() || portd.isEmpty()) 43 | { 44 | this->ui->textEdit->append("请输入IP和端口!"); 45 | return; 46 | } 47 | tcpClient->connectToHost(ipAdd,portd.toInt()); 48 | if (tcpClient->waitForConnected(1000)) 49 | { 50 | ui->pushConnect->setText("断开"); 51 | ui->textEdit->append("连接服务器成功"); 52 | ui->pushSent->setEnabled(true); 53 | this->ui->txtIp->setEnabled(false); 54 | this->ui->txtPort->setEnabled(false); 55 | this->ui->timeBut->setEnabled(true); 56 | } 57 | } 58 | else 59 | { 60 | tcpClient->disconnectFromHost(); 61 | if (tcpClient->state() == QAbstractSocket::UnconnectedState || tcpClient->waitForDisconnected(1000) ) 62 | { 63 | ui->pushConnect->setText("连接"); 64 | ui->textEdit->append("断开服务器"); 65 | ui->pushSent->setEnabled(false); 66 | this->ui->txtIp->setEnabled(true); 67 | this->ui->txtPort->setEnabled(true); 68 | tm.stop(); 69 | this->ui->timeBut->setEnabled(false); 70 | this->ui->lineEdit->setEnabled(true); 71 | this->ui->timeBut->setText("启动定时"); 72 | } 73 | } 74 | } 75 | 76 | void MainWindow::on_pushSent_clicked() 77 | { 78 | qDebug() << "点击发送:" ; 79 | QString data = this->ui->txtData->toPlainText();//->text(); 80 | if (data.isEmpty()) 81 | { 82 | return ; 83 | } 84 | tcpClient->write(data.toUtf8()); 85 | ui->textEdit->append(tr("Say:%1").arg(data)); 86 | } 87 | 88 | void MainWindow::ReadError(QAbstractSocket::SocketError) 89 | { 90 | tcpClient->disconnectFromHost(); 91 | ui->pushConnect->setText("连接"); 92 | ui->textEdit->append(tr("连接出错:%1").arg(tcpClient->errorString())); 93 | ui->pushSent->setEnabled(false); 94 | this->ui->txtIp->setEnabled(true); 95 | this->ui->txtPort->setEnabled(true); 96 | tm.stop(); 97 | this->ui->timeBut->setEnabled(false); 98 | this->ui->lineEdit->setEnabled(true); 99 | this->ui->timeBut->setText("启动定时"); 100 | } 101 | 102 | void MainWindow::on_timeBut_clicked() 103 | { 104 | if (this->ui->lineEdit->text().isEmpty()) 105 | { 106 | this->ui->textEdit->append("请输入时间:"); 107 | return; 108 | } 109 | if ("启动定时" == this->ui->timeBut->text()) 110 | { 111 | int h = this->ui->lineEdit->text().toInt(); 112 | h = h*1000; 113 | tm.start(h); 114 | this->ui->lineEdit->setEnabled(false); 115 | this->ui->timeBut->setText("停止定时"); 116 | } 117 | else 118 | { 119 | tm.stop(); 120 | this->ui->timeBut->setText("启动定时"); 121 | this->ui->lineEdit->setEnabled(true); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /test-client/myTcpClient/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace Ui { 9 | class MainWindow; 10 | } 11 | 12 | class MainWindow : public QMainWindow 13 | { 14 | Q_OBJECT 15 | 16 | public: 17 | explicit MainWindow(QWidget *parent = 0); 18 | ~MainWindow(); 19 | 20 | public slots: 21 | void ReadError(QAbstractSocket::SocketError); 22 | 23 | void on_pushSent_clicked(); 24 | void on_pushConnect_clicked(); 25 | void on_timeBut_clicked(); 26 | 27 | private: 28 | Ui::MainWindow * ui; 29 | QTcpSocket * tcpClient; 30 | QTimer tm; 31 | QStringList list; 32 | }; 33 | 34 | #endif // MAINWINDOW_H 35 | -------------------------------------------------------------------------------- /test-client/myTcpClient/mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 504 10 | 505 11 | 12 | 13 | 14 | MainWindow 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 发送数据: 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 发送数据 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | IP地址: 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 0 56 | 0 57 | 58 | 59 | 60 | QLineEdit::Normal 61 | 62 | 63 | 64 | 65 | 66 | 67 | 端口: 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 0 76 | 0 77 | 78 | 79 | 80 | 81 | 60 82 | 0 83 | 84 | 85 | 86 | 87 | 60 88 | 16777215 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 连接 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | Qt::Horizontal 108 | 109 | 110 | 111 | 188 112 | 18 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 间隔时间(s): 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 50 129 | 20 130 | 131 | 132 | 133 | 134 | 50 135 | 0 136 | 137 | 138 | 139 | 140 | 50 141 | 16777215 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 启动定时 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /test-client/myTcpClient/myTcpClient.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2013-09-18T08:44:16 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core gui network 8 | 9 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 10 | 11 | TARGET = myTcpClient 12 | TEMPLATE = app 13 | 14 | CONFIG += C++11 15 | 16 | SOURCES += main.cpp\ 17 | mainwindow.cpp 18 | 19 | HEADERS += mainwindow.h 20 | 21 | FORMS += mainwindow.ui 22 | --------------------------------------------------------------------------------