vn-verdnaturachat/ios/Pods/Flipper-Folly/folly/io/async/AsyncUDPSocket.h

448 lines
13 KiB
C
Raw Normal View History

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <memory>
#include <folly/ScopeGuard.h>
#include <folly/SocketAddress.h>
#include <folly/io/IOBuf.h>
#include <folly/io/SocketOptionMap.h>
#include <folly/io/async/AsyncSocketBase.h>
#include <folly/io/async/AsyncSocketException.h>
#include <folly/io/async/EventBase.h>
#include <folly/io/async/EventHandler.h>
#include <folly/net/NetOps.h>
#include <folly/net/NetworkSocket.h>
namespace folly {
/**
* UDP socket
*/
class AsyncUDPSocket : public EventHandler {
public:
enum class FDOwnership { OWNS, SHARED };
class ReadCallback {
public:
struct OnDataAvailableParams {
int gro_ = -1;
};
/**
* Invoked when the socket becomes readable and we want buffer
* to write to.
*
* NOTE: From socket we will end up reading at most `len` bytes
* and if there were more bytes in datagram, we will end up
* dropping them.
*/
virtual void getReadBuffer(void** buf, size_t* len) noexcept = 0;
/**
* Invoked when a new datagram is available on the socket. `len`
* is the number of bytes read and `truncated` is true if we had
* to drop few bytes because of running out of buffer space.
* OnDataAvailableParams::gro is the GRO segment size
*/
virtual void onDataAvailable(
const folly::SocketAddress& client,
size_t len,
bool truncated,
OnDataAvailableParams params) noexcept = 0;
/**
* Notifies when data is available. This is only invoked when
* shouldNotifyOnly() returns true.
*/
virtual void onNotifyDataAvailable(AsyncUDPSocket&) noexcept {}
/**
* Returns whether or not the read callback should only notify
* but not call getReadBuffer.
* If shouldNotifyOnly() returns true, AsyncUDPSocket will invoke
* onNotifyDataAvailable() instead of getReadBuffer().
* If shouldNotifyOnly() returns false, AsyncUDPSocket will invoke
* getReadBuffer() and onDataAvailable().
*/
virtual bool shouldOnlyNotify() {
return false;
}
/**
* Invoked when there is an error reading from the socket.
*
* NOTE: Since UDP is connectionless, you can still read from the socket.
* But you have to re-register readCallback yourself after
* onReadError.
*/
virtual void onReadError(const AsyncSocketException& ex) noexcept = 0;
/**
* Invoked when socket is closed and a read callback is registered.
*/
virtual void onReadClosed() noexcept = 0;
virtual ~ReadCallback() = default;
};
class ErrMessageCallback {
public:
virtual ~ErrMessageCallback() = default;
/**
* errMessage() will be invoked when kernel puts a message to
* the error queue associated with the socket.
*
* @param cmsg Reference to cmsghdr structure describing
* a message read from error queue associated
* with the socket.
*/
virtual void errMessage(const cmsghdr& cmsg) noexcept = 0;
/**
* errMessageError() will be invoked if an error occurs reading a message
* from the socket error stream.
*
* @param ex An exception describing the error that occurred.
*/
virtual void errMessageError(const AsyncSocketException& ex) noexcept = 0;
};
/**
* Create a new UDP socket that will run in the
* given eventbase
*/
explicit AsyncUDPSocket(EventBase* evb);
~AsyncUDPSocket() override;
/**
* Returns the address server is listening on
*/
virtual const folly::SocketAddress& address() const {
CHECK_NE(NetworkSocket(), fd_) << "Server not yet bound to an address";
return localAddress_;
}
/**
* Bind the socket to the following address. If port is not
* set in the `address` an ephemeral port is chosen and you can
* use `address()` method above to get it after this method successfully
* returns.
*/
virtual void bind(const folly::SocketAddress& address);
/**
* Use an already bound file descriptor. You can either transfer ownership
* of this FD by using ownership = FDOwnership::OWNS or share it using
* FDOwnership::SHARED. In case FD is shared, it will not be `close`d in
* destructor.
*/
virtual void setFD(NetworkSocket fd, FDOwnership ownership);
/**
* Send the data in buffer to destination. Returns the return code from
* ::sendmsg.
*/
virtual ssize_t write(
const folly::SocketAddress& address,
const std::unique_ptr<folly::IOBuf>& buf);
/**
* Send the data in buffers to destination. Returns the return code from
* ::sendmmsg.
* bufs is an array of std::unique_ptr<folly::IOBuf>
* of size num
*/
virtual int writem(
const folly::SocketAddress& address,
const std::unique_ptr<folly::IOBuf>* bufs,
size_t count);
/**
* Send the data in buffer to destination. Returns the return code from
* ::sendmsg.
* gso is the generic segmentation offload value
* writeGSO will return -1 if
* buf->computeChainDataLength() <= gso
* Before calling writeGSO with a positive value
* verify GSO is supported on this platform by calling getGSO
*/
virtual ssize_t writeGSO(
const folly::SocketAddress& address,
const std::unique_ptr<folly::IOBuf>& buf,
int gso);
/**
* Send the data in buffers to destination. Returns the return code from
* ::sendmmsg.
* bufs is an array of std::unique_ptr<folly::IOBuf>
* of size num
* gso is an array with the generic segmentation offload values or nullptr
* Before calling writeGSO with a positive value
* verify GSO is supported on this platform by calling getGSO
*/
virtual int writemGSO(
const folly::SocketAddress& address,
const std::unique_ptr<folly::IOBuf>* bufs,
size_t count,
const int* gso);
/**
* Send data in iovec to destination. Returns the return code from sendmsg.
*/
virtual ssize_t writev(
const folly::SocketAddress& address,
const struct iovec* vec,
size_t iovec_len,
int gso);
virtual ssize_t writev(
const folly::SocketAddress& address,
const struct iovec* vec,
size_t iovec_len);
virtual ssize_t recvmsg(struct msghdr* msg, int flags);
virtual int recvmmsg(
struct mmsghdr* msgvec,
unsigned int vlen,
unsigned int flags,
struct timespec* timeout);
/**
* Start reading datagrams
*/
virtual void resumeRead(ReadCallback* cob);
/**
* Pause reading datagrams
*/
virtual void pauseRead();
/**
* Stop listening on the socket.
*/
virtual void close();
/**
* Get internal FD used by this socket
*/
virtual NetworkSocket getNetworkSocket() const {
CHECK_NE(NetworkSocket(), fd_) << "Need to bind before getting FD out";
return fd_;
}
/**
* Set reuse port mode to call bind() on the same address multiple times
*/
virtual void setReusePort(bool reusePort) {
reusePort_ = reusePort;
}
/**
* Set SO_REUSEADDR flag on the socket. Default is OFF.
*/
virtual void setReuseAddr(bool reuseAddr) {
reuseAddr_ = reuseAddr;
}
/**
* Set SO_RCVBUF option on the socket, if not zero. Default is zero.
*/
virtual void setRcvBuf(int rcvBuf) {
rcvBuf_ = rcvBuf;
}
/**
* Set SO_SNDBUG option on the socket, if not zero. Default is zero.
*/
virtual void setSndBuf(int sndBuf) {
sndBuf_ = sndBuf;
}
/**
* Set SO_BUSY_POLL option on the socket, if not zero. Default is zero.
* Caution! The feature is not available on Apple's systems.
*/
virtual void setBusyPoll(int busyPollUs) {
busyPollUs_ = busyPollUs;
}
EventBase* getEventBase() const {
return eventBase_;
}
/**
* Enable or disable fragmentation on the socket.
*
* On Linux, this sets IP(V6)_MTU_DISCOVER to IP(V6)_PMTUDISC_DO when enabled,
* and to IP(V6)_PMTUDISC_WANT when disabled. IP(V6)_PMTUDISC_WANT will use
* per-route setting to set DF bit. It may be more desirable to use
* IP(V6)_PMTUDISC_PROBE as opposed to IP(V6)_PMTUDISC_DO for apps that has
* its own PMTU Discovery mechanism.
* Note this doesn't work on Apple.
*/
virtual void dontFragment(bool df);
/**
* Set Dont-Fragment (DF) but ignore Path MTU.
*
* On Linux, this sets IP(V6)_MTU_DISCOVER to IP(V6)_PMTUDISC_PROBE.
* This essentially sets DF but ignores Path MTU for this socket.
* This may be desirable for apps that has its own PMTU Discovery mechanism.
* See http://man7.org/linux/man-pages/man7/ip.7.html for more info.
*/
virtual void setDFAndTurnOffPMTU();
/**
* Callback for receiving errors on the UDP sockets
*/
virtual void setErrMessageCallback(ErrMessageCallback* errMessageCallback);
/**
* Connects the UDP socket to a remote destination address provided in
* address. This can speed up UDP writes on linux because it will cache flow
* state on connects.
* Using connect has many quirks, and you should be aware of them before using
* this API:
* 1. This must only be called after binding the socket.
* 2. Normally UDP can use the 2 tuple (src ip, src port) to steer packets
* sent by the peer to the socket, however after connecting the socket, only
* packets destined to the destination address specified in connect() will be
* forwarded and others will be dropped. If the server can send a packet
* from a different destination port / IP then you probably do not want to use
* this API.
* 3. It can be called repeatedly on either the client or server however it's
* normally only useful on the client and not server.
*
* Returns the result of calling the connect syscall.
*/
virtual int connect(const folly::SocketAddress& address);
virtual bool isBound() const {
return fd_ != NetworkSocket();
}
virtual bool isReading() const {
return readCallback_ != nullptr;
}
virtual void detachEventBase();
virtual void attachEventBase(folly::EventBase* evb);
// generic segmentation offload get/set
// negative return value means GSO is not available
int getGSO();
bool setGSO(int val);
// generic receive offload get/set
// negative return value means GRO is not available
int getGRO();
bool setGRO(bool bVal);
void setTrafficClass(int tclass);
void applyOptions(
const SocketOptionMap& options,
SocketOptionKey::ApplyPos pos);
protected:
virtual ssize_t
sendmsg(NetworkSocket socket, const struct msghdr* message, int flags) {
return netops::sendmsg(socket, message, flags);
}
virtual int sendmmsg(
NetworkSocket socket,
struct mmsghdr* msgvec,
unsigned int vlen,
int flags) {
return netops::sendmmsg(socket, msgvec, vlen, flags);
}
void fillMsgVec(
sockaddr_storage* addr,
socklen_t addr_len,
const std::unique_ptr<folly::IOBuf>* bufs,
size_t count,
struct mmsghdr* msgvec,
struct iovec* iov,
size_t iov_count,
const int* gso,
char* gsoControl);
virtual int writeImpl(
const folly::SocketAddress& address,
const std::unique_ptr<folly::IOBuf>* bufs,
size_t count,
struct mmsghdr* msgvec,
const int* gso,
char* gsoControl);
size_t handleErrMessages() noexcept;
void failErrMessageRead(const AsyncSocketException& ex);
// Non-null only when we are reading
ReadCallback* readCallback_;
private:
AsyncUDPSocket(const AsyncUDPSocket&) = delete;
AsyncUDPSocket& operator=(const AsyncUDPSocket&) = delete;
// EventHandler
void handlerReady(uint16_t events) noexcept override;
void handleRead() noexcept;
bool updateRegistration() noexcept;
EventBase* eventBase_;
folly::SocketAddress localAddress_;
NetworkSocket fd_;
FDOwnership ownership_;
// Temp space to receive client address
folly::SocketAddress clientAddress_;
// If the socket is connected.
folly::SocketAddress connectedAddress_;
bool connected_{false};
bool reuseAddr_{false};
bool reusePort_{false};
int rcvBuf_{0};
int sndBuf_{0};
int busyPollUs_{0};
// generic segmentation offload value, if available
// See https://lwn.net/Articles/188489/ for more details
folly::Optional<int> gso_;
// generic receive offload value, if available
// See https://lwn.net/Articles/770978/ for more details
folly::Optional<int> gro_;
ErrMessageCallback* errMessageCallback_{nullptr};
};
} // namespace folly