کلاس QAbstractSocket در Qt
- codenevisam
- کلاس ها در کیوت, کلاس های شبکه در کیوت

مقدمه
در دنیای برنامهنویسی شبکه، ارتباط بین برنامهها از طریق سوکتها برقرار میشود. Qt، به عنوان یکی از محبوبترین فریمورکهای چندسکویی برای توسعه نرمافزار، کلاسهای قدرتمندی را برای کار با سوکتها در اختیار توسعهدهندگان قرار میدهد. در این مقاله، به بررسی جامع کلاس QAbstractSocket در Qt پرداخته و اهمیت آن در ایجاد ارتباطات شبکهای را تشریح خواهیم کرد.
QAbstractSocket چیست؟
QAbstractSocket کلاس پایه برای سوکتها در Qt است و امکانات پایهای برای ایجاد، مدیریت و برقرار کردن ارتباطات شبکهای را فراهم میکند. این کلاس به عنوان یک کلاس انتزاعی عمل میکند و کلاسهای مشتق شده از آن مانند QTcpSocket و QUdpSocket برای پیادهسازی پروتکلهای TCP و UDP به ترتیب استفاده میشوند.
ویژگیهای کلیدی QAbstractSocket:
- چندسکویی: با استفاده از این کلاس، میتوان برنامههایی ایجاد کرد که روی سیستمعاملهای مختلف مانند ویندوز، لینوکس و macOS اجرا شوند.
- ساده و کاربرپسند: API این کلاس به گونهای طراحی شده است که استفاده از آن آسان باشد و توسعهدهندگان بتوانند به سرعت ارتباطات شبکهای را در برنامههای خود پیادهسازی کنند.
- انعطافپذیر:
QAbstractSocketامکان سفارشیسازی گستردهای را برای تنظیم پارامترهای سوکت فراهم میکند. - سیگنالها و اسلاتها: این کلاس از مکانیزم سیگنالها و اسلاتهای Qt استفاده میکند که امکان مدیریت رویدادهای شبکهای مانند اتصال، قطع اتصال و دریافت داده را به صورت آسان فراهم میآورد.
کاربردهای QAbstractSocket
- برنامههای چت: ایجاد برنامههای چت مبتنی بر متن یا صدا.
- سرورهای ساده: ساخت سرورهای ساده برای ارائه خدمات مختلف.
- کلاینتهای شبکهای: توسعه کلاینتهایی برای ارتباط با سرورهای از راه دور.
- انتقال فایل: پیادهسازی پروتکلهای انتقال فایل مانند FTP.
- بازیهای شبکهای: ایجاد بازیهایی که از طریق شبکه به چندین کاربر اجازه میدهند به صورت همزمان بازی کنند.
مقایسه QTcpSocket و QUdpSocket
- QTcpSocket: برای ارتباطات مبتنی بر اتصال (connection-oriented) استفاده میشود. دادهها به صورت قابل اطمینان و ترتیبدار منتقل میشوند.
- QUdpSocket: برای ارتباطات مبتنی بر بیاتصال (connectionless) استفاده میشود. دادهها به صورت بستههای جداگانه و ممکن است به ترتیب ارسال نشوند.
مثال ساده
نکات مهم برای استفاده از QAbstractSocket
- مدیریت خطا: همیشه خطاهای احتمالی را مدیریت کنید تا برنامه شما در صورت بروز مشکل به درستی کار کند.
- کارایی: برای بهبود کارایی برنامه، از تکنیکهایی مانند بافر کردن دادهها و استفاده از عملیات غیر مسدودشونده استفاده کنید.
- امنیت: در هنگام پیادهسازی ارتباطات شبکهای، به امنیت توجه ویژه داشته باشید تا از نفوذهای احتمالی جلوگیری کنید.
نتیجهگیری
QAbstractSocket یک ابزار قدرتمند برای توسعه برنامههای شبکهای در Qt است. با درک مفاهیم پایه و کاربردهای این کلاس، میتوانید برنامههای شبکهای پیچیده و کارآمدی را ایجاد کنید.
توجه: برای کسب اطلاعات بیشتر، به مستندات رسمی Qt مراجعه کنید.
مطالب مرتبط:
- آموزش جامع Qt
- برنامهنویسی شبکه با C++
- پروتکل TCP
- پروتکل UDP
لینکهای مفید:
معرفی کامل کلاس QAbstractSocket
| Header: | #include <QAbstractSocket> |
| CMake: | find_package(Qt6 REQUIRED COMPONENTS Network) target_link_libraries(mytarget PRIVATE Qt6::Network) |
| qmake: | QT += network |
| Inherits: | QIODevice |
| Inherited By: | QTcpSocket and QUdpSocket |
انواع عمومی :
| enum | BindFlag { ShareAddress, DontShareAddress, ReuseAddressHint, DefaultForPlatform } |
| flags | BindMode |
| enum | NetworkLayerProtocol { IPv4Protocol, IPv6Protocol, AnyIPProtocol, UnknownNetworkLayerProtocol } |
| enum | PauseMode { PauseNever, PauseOnSslErrors } |
| flags | PauseModes |
| enum | SocketError { ConnectionRefusedError, RemoteHostClosedError, HostNotFoundError, SocketAccessError, SocketResourceError, …, UnknownSocketError } |
| enum | SocketOption { LowDelayOption, KeepAliveOption, MulticastTtlOption, MulticastLoopbackOption, TypeOfServiceOption, …, PathMtuSocketOption } |
| enum | SocketState { UnconnectedState, HostLookupState, ConnectingState, ConnectedState, BoundState, …, ListeningState } |
| enum | SocketType { TcpSocket, UdpSocket, SctpSocket, UnknownSocketType } |
متدهای عمومی :
| QAbstractSocket(QAbstractSocket::SocketType socketType, QObject *parent) | |
| virtual | ~QAbstractSocket() |
| void | abort() |
| virtual bool | bind(const QHostAddress &address, quint16 port = 0, QAbstractSocket::BindMode mode = DefaultForPlatform) |
(since 6.2) bool | bind(QHostAddress::SpecialAddress addr, quint16 port = 0, QAbstractSocket::BindMode mode = DefaultForPlatform) |
| bool | bind(quint16 port = 0, QAbstractSocket::BindMode mode = DefaultForPlatform) |
| virtual void | connectToHost(const QString &hostName, quint16 port, QIODeviceBase::OpenMode openMode = ReadWrite, QAbstractSocket::NetworkLayerProtocol protocol = AnyIPProtocol) |
| void | connectToHost(const QHostAddress &address, quint16 port, QIODeviceBase::OpenMode openMode = ReadWrite) |
| virtual void | disconnectFromHost() |
| QAbstractSocket::SocketError | error() const |
| bool | flush() |
| bool | isValid() const |
| QHostAddress | localAddress() const |
| quint16 | localPort() const |
| QAbstractSocket::PauseModes | pauseMode() const |
| QHostAddress | peerAddress() const |
| QString | peerName() const |
| quint16 | peerPort() const |
| QString | protocolTag() const |
| QNetworkProxy | proxy() const |
| qint64 | readBufferSize() const |
| virtual void | resume() |
| void | setPauseMode(QAbstractSocket::PauseModes pauseMode) |
| void | setProtocolTag(const QString &tag) |
| void | setProxy(const QNetworkProxy &networkProxy) |
| virtual void | setReadBufferSize(qint64 size) |
| virtual bool | setSocketDescriptor(qintptr socketDescriptor, QAbstractSocket::SocketState socketState = ConnectedState, QIODeviceBase::OpenMode openMode = ReadWrite) |
| virtual void | setSocketOption(QAbstractSocket::SocketOption option, const QVariant &value) |
| virtual qintptr | socketDescriptor() const |
| virtual QVariant | socketOption(QAbstractSocket::SocketOption option) |
| QAbstractSocket::SocketType | socketType() const |
| QAbstractSocket::SocketState | state() const |
| virtual bool | waitForConnected(int msecs = 30000) |
| virtual bool | waitForDisconnected(int msecs = 30000) |
توابع عمومی مجدداً پیادهسازیشده :
| virtual qint64 | bytesAvailable() const override |
| virtual qint64 | bytesToWrite() const override |
| virtual void | close() override |
| virtual bool | isSequential() const override |
| virtual bool | waitForBytesWritten(int msecs = 30000) override |
| virtual bool | waitForReadyRead(int msecs = 30000) override |
سیگنال ها :
| void | connected() |
| void | disconnected() |
| void | errorOccurred(QAbstractSocket::SocketError socketError) |
| void | hostFound() |
| void | proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator *authenticator) |
| void | stateChanged(QAbstractSocket::SocketState socketState) |
متدهای محافظت شده :
| void | setLocalAddress(const QHostAddress &address) |
| void | setLocalPort(quint16 port) |
| void | setPeerAddress(const QHostAddress &address) |
| void | setPeerName(const QString &name) |
| void | setPeerPort(quint16 port) |
| void | setSocketError(QAbstractSocket::SocketError socketError) |
| void | setSocketState(QAbstractSocket::SocketState state) |
توابع محافظتشده مجدداً پیادهسازیشده
| virtual qint64 | readData(char *data, qint64 maxSize) override |
| virtual qint64 | readLineData(char *data, qint64 maxlen) override |
| virtual qint64 | skipData(qint64 maxSize) override |
| virtual qint64 | writeData(const char *data, qint64 size) override |
توضیحات کامل
کلاس `QAbstractSocket`، کلاس پایه برای `QTcpSocket ` و `QUdpSocket ` است و تمامی عملکردهای مشترک این دو کلاس را شامل میشود. اگر نیاز به یک سوکت دارید، دو گزینه پیش روی شماست:
1. ایجاد یک شیء از کلاس ` QTcpSocket` یا ` QUdpSocket`.
2. ایجاد یک توصیفگر سوکت بومی (native socket descriptor)، سپس ساختن یک شیء از `QAbstractSocket` و استفاده از تابع `setSocketDescriptor()` برای مرتبط کردن (wrap) سوکت بومی.
TCP (پروتکل کنترل انتقال) یک پروتکل انتقال مطمئن، جریانی و اتصالگرا است. UDP (پروتکل دیتاگرام کاربر) یک پروتکل انتقال غیرمطمئن، مبتنی بر دیتاگرام و بدون اتصال است. در عمل، این به این معناست که TCP برای انتقال پیوسته داده مناسبتر است، در حالی که UDP سبکتر بوده و میتواند در مواردی که قابلیت اطمینان اهمیت ندارد، مورد استفاده قرار گیرد.
API کلاس `QAbstractSocket` بیشتر تفاوتهای بین این دو پروتکل را یکپارچه میکند. به عنوان مثال، اگرچه UDP بدون اتصال است، اما تابع ` connectToHost()` برای سوکتهای UDP یک اتصال مجازی برقرار میکند، که این امکان را فراهم میآورد تا `QAbstractSocket` را به شکلی مشابه با پروتکل زیرین استفاده کنید. در داخل، `QAbstractSocket` آدرس و پورتی را که به ` connectToHost()` پاس داده شده، به خاطر میسپارد و توابعی مانند ` read()` و ` write()` از این مقادیر استفاده میکنند.
در هر لحظه، `QAbstractSocket` دارای یک وضعیت (state) است که با تابع ` state()` بازگردانده میشود. وضعیت اولیه، `UnconnectedState` است. پس از فراخوانی `connectToHost()`، سوکت ابتدا وارد حالت `HostLookupState` میشود. اگر میزبان پیدا شود، `QAbstractSocket` وارد حالت `ConnectingState` شده و سیگنال ` hostFound()` را منتشر میکند. وقتی اتصال برقرار شد، به ` ConnectedState` میرود و سیگنال ` connected()` منتشر میشود. اگر در هر مرحله خطایی رخ دهد، سیگنال `errorOccurred()` منتشر میشود. هر زمان که وضعیت تغییر کند، سیگنال `stateChanged()` منتشر میشود. برای راحتی، تابع `isValid()` در صورتی که سوکت آماده خواندن و نوشتن باشد، مقدار `true` برمیگرداند، اما توجه داشته باشید که وضعیت سوکت باید قبل از خواندن و نوشتن ` ConnectedState` باشد.
برای خواندن یا نوشتن دادهها از توابع `read()` یا `write()` استفاده کنید، یا از توابع راحتتری مانند `readLine() ()` و `readAll()` بهره ببرید. `QAbstractSocket` همچنین توابع ` getChar`، ` putChar()()` و ` ungetChar()` را از `QIODevice` به ارث میبرد که روی بایتهای تکی کار میکنند. سیگنال `bytesWritten()` زمانی که دادهها به سوکت نوشته شدند، منتشر میشود. توجه داشته باشید که Qt محدودیتی برای اندازه بافر نوشتن تعیین نمیکند. شما میتوانید با گوش دادن به این سیگنال، اندازه آن را مانیتور کنید.
سیگنال `readyRead()` هر بار که یک دسته داده جدید رسیده باشد، منتشر میشود. سپس ` bytesAvailable()` تعداد بایتهایی که برای خواندن در دسترس هستند را برمیگرداند. معمولاً شما سیگنال `readyRead()` را به یک اسلات وصل میکنید و همه دادههای موجود را در آنجا میخوانید. اگر همه دادهها را یکجا نخوانید، دادههای باقیمانده همچنان در دسترس خواهند بود و هر داده جدیدی که وارد شود به بافر خواندن داخلی `QAbstractSocket` اضافه خواهد شد. برای محدود کردن اندازه بافر خواندن، تابع ` setReadBufferSize()` را فراخوانی کنید.
برای بستن سوکت، تابع ` disconnectFromHost()` را فراخوانی کنید. `QAbstractSocket::ClosingState` وارد حالت `ClosingState` میشود. پس از اینکه همه دادههای معلق به سوکت نوشته شدند، ` QAbstractSocket::UnconnectedState` واقعاً سوکت را میبندد، وارد حالت `UnconnectedState` میشود و سیگنال `disconnected()` را منتشر میکند. اگر میخواهید اتصال را فوراً قطع کنید و همه دادههای معلق را نادیده بگیرید، از تابع ` abort()` استفاده کنید. اگر میزبان دوردست اتصال را ببندد، `QAbstractSocket` سیگنال `errorOccurred(QAbstractSocket::RemoteHostClosedError` را منتشر خواهد کرد، در حالی که وضعیت سوکت همچنان `ConnectedState` خواهد بود و سپس سیگنال `disconnected()` منتشر میشود.
پورت و آدرس میزبان متصلشده با فراخوانی `peerPort()` و `peerAddress()` بازیابی میشود. `peerName()` نام میزبان را که به `connectToHost()` پاس داده شده، برمیگرداند. ` localPort()` و `localAddress()` پورت و آدرس سوکت محلی را برمیگردانند.
`QAbstractSocket` مجموعهای از توابع را فراهم میکند که فراخوانی رشتهای را تا زمانی که سیگنالهای مشخصی منتشر شوند، متوقف میکنند. این توابع میتوانند برای پیادهسازی سوکتهای بلوککننده استفاده شوند:
1. `waitForConnected()` تا زمانی که اتصال برقرار نشده است، بلوک میکند.
2. `waitForReadyRead()` تا زمانی که داده جدیدی برای خواندن در دسترس نباشد، بلوک میکند.
3. `waitForBytesWritten()` تا زمانی که یک بار داده به سوکت نوشته نشده باشد، بلوک میکند.
4. `waitForDisconnected()` تا زمانی که اتصال بسته نشده است، بلوک میکند.
اگر waitForReadyRead() مقدار false را برگرداند، یا اتصال بسته شده یا یک خطا رخ داده است.
برنامهنویسی با یک سوکت بلوککننده بهطور اساسی با برنامهنویسی با یک سوکت غیر بلوککننده متفاوت است. یک سوکت بلوککننده نیازی به حلقه رویداد (event loop) ندارد و معمولاً به کد سادهتری منجر میشود. با این حال، در یک برنامه رابط گرافیکی (GUI)، سوکتهای بلوککننده باید فقط در رشتههای غیر گرافیکی (non-GUI threads) استفاده شوند تا از فریز شدن رابط کاربری جلوگیری شود. برای آشنایی بیشتر با هر دو روش، به مثالهای fortuneclient و blockingfortuneclient مراجعه کنید.
توجه: ما استفاده از توابع بلوککننده را به همراه سیگنالها توصیه نمیکنیم. بهتر است یکی از این دو روش بهطور مجزا استفاده شود.
کلاس `QAbstractSocket` میتواند با اپراتورهای جریان (`operator<<()` و `operator>>()`) در کلاسهای ` QTextStream` و `QDataStream‘` استفاده شود. با این حال، یک نکته مهم وجود دارد که باید به آن توجه کنید: پیش از تلاش برای خواندن داده با استفاده از `operator>>()`، باید اطمینان حاصل کنید که داده کافی در دسترس است.
مستندات نوع عضو (Member Type Documentation)
این بخش از مستندات کلاسهای Qt به توضیح انواع اعضا (Member Types) اختصاص دارد که در داخل کلاس تعریف شدهاند. این انواع میتوانند شامل enumها، typedefها، یا دیگر تعاریف نوع دادهای باشند که مخصوص همان کلاس هستند.
در این مستندات، انواع عضو به طور مفصل معرفی میشوند و نحوه استفاده از آنها در کد توضیح داده میشود. همچنین، ممکن است مثالهایی از نحوه استفاده از این انواع عضو ارائه شود. این اطلاعات به برنامهنویسان کمک میکند تا بتوانند به درستی از انواع دادهای خاص هر کلاس استفاده کنند.
enum QAbstractSocket::BindFlag
flags QAbstractSocket::BindMode
enum QAbstractSocket::BindFlag
flags QAbstractSocket::BindMode
این enum (نوع شمارشی) توصیفکننده پرچمهای مختلفی است که میتوانید برای تغییر رفتار یک عملیات یا تابع، به آنها پاس دهید.
| Constant | Value | Description |
|---|---|---|
QAbstractSocket::ShareAddress | 0x1 | اجازه میدهد که سایر سرویسها به همان آدرس و پورت متصل شوند. این ویژگی زمانی مفید است که چندین فرآیند به صورت مشترک بار یک سرویس را با گوش دادن به همان آدرس و پورت تقسیم کنند (برای مثال، یک وب سرور با چندین شنونده از پیش ایجاد شده میتواند به طور قابل توجهی زمان پاسخگویی را بهبود بخشد). با این حال، به دلیل اینکه هر سرویسی مجاز است دوباره به این آدرس و پورت متصل شود، این گزینه نیازمند در نظر گرفتن برخی ملاحظات امنیتی است. توجه داشته باشید که با ترکیب این گزینه با `ReuseAddressHint`، به سرویس خود اجازه میدهید تا به یک آدرس مشترک موجود دوباره متصل شود. در سیستمهای یونیکس، این معادل گزینه سوکت `SO_REUSEADDR` است. در ویندوز، این رفتار به صورت پیشفرض فعال است، بنابراین این گزینه نادیده گرفته میشود. |
QAbstractSocket::DontShareAddress | 0x2 | آدرس و پورت را بهطور انحصاری متصل میکند، به طوری که هیچ سرویس دیگری مجاز به اتصال مجدد نخواهد بود. با گذراندن این گزینه به `QAbstractSocket::bind`، اطمینان حاصل میکنید که در صورت موفقیت، سرویس شما تنها سرویسی است که به آن آدرس و پورت گوش میدهد. هیچ سرویسی مجاز به اتصال مجدد نخواهد بود، حتی اگر از `ReuseAddressHint` استفاده کند. این گزینه امنیت بیشتری نسبت به `ShareAddress` فراهم میکند، اما در برخی سیستمعاملها نیاز است که سرور را با دسترسیهای مدیر (administrator) اجرا کنید. در یونیکس و macOS، بهطور پیشفرض اشتراکگذاری در زمان متصل کردن یک آدرس و پورت غیرفعال است، بنابراین این گزینه نادیده گرفته میشود. در ویندوز، این گزینه از `SO_EXCLUSIVEADDRUSE` در سوکت استفاده میکند. |
QAbstractSocket::ReuseAddressHint | 0x4 | این گزینه به QAbstractSocket نشان میدهد که تلاش کند تا سرویس را دوباره متصل کند، حتی اگر آدرس و پورت قبلاً توسط یک سوکت دیگر متصل شده باشند. در ویندوز و یونیکس، این معادل گزینه سوکت SO_REUSEADDR است. |
QAbstractSocket::DefaultForPlatform | 0x0 | گزینه پیشفرض برای پلتفرم فعلی. در یونیکس و macOS، این گزینه معادل `(DontShareAddress + ReuseAddressHint)` است و در ویندوز، معادل `ShareAddress` میباشد. |
این `enum` (نوع شمارشی) مقادیر پروتکل لایه شبکه را که در Qt استفاده میشوند، توصیف میکند.
| Constant | Value | Description |
|---|---|---|
QAbstractSocket::IPv4Protocol | 0 | IPv4 |
QAbstractSocket::IPv6Protocol | 1 | IPv6 |
QAbstractSocket::AnyIPProtocol | 2 | IPv4 یا IPv6 یا |
QAbstractSocket::UnknownNetworkLayerProtocol | -1 | ” IPv4 و IPv6 غیر از” |
2 نظر
تشکر ، بابت مستندات کیوت به زبان فارسی.
خواهش میکنم