Files
ppsspp/Core/HLE/SocketManager.h
T
Henrik RydgårdandClaude Opus 5.5 3ee22fbd3d sceNetInet: TCP socket states and bind's port sharing
Following pspautotests net/inet/errors, states and reuse:
- bind decides itself whether a port is in use, as 4.4BSD's in_pcbbind()
  does with SO_REUSEADDR and SO_REUSEPORT. The host can't, since its
  sockets have both on for running several instances, so any number of
  sockets could bind the same port. A socket that's bound already is
  EINVAL.
- Shutting down the sending side of an unconnected TCP socket, or a
  failed connect, throws away its TCP state as in 4.4BSD's tcp_usrreq(),
  after which bind, listen, connect, accept and shutdown fail with
  EINVAL, and its port is free again. shutdown otherwise only fails for
  a bad how, and still reaches the host, so a thread waiting in accept
  wakes up.
- listen on an unbound TCP socket binds one, which Winsock didn't, and on
  a connected one succeeds.
- connect on a listening socket is EOPNOTSUPP. Winsock's WSAEINVAL for
  it looked like a connect in progress, and a blocking socket waited
  forever. A second connect on a loopback connection the host hasn't
  noticed finishing is EISCONN once it has.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 13:09:28 +01:00

85 lines
2.6 KiB
C++

#pragma once
#include "Common/CommonTypes.h"
#include "Common/Net/SocketCompat.h"
// Keep track of who's using a socket.
enum class SocketState {
Unused = 0,
UsedNetInet,
UsedProAdhoc,
};
const char *SocketStateToString(SocketState state);
// Internal socket state tracking
struct InetSocket {
SOCKET sock; // native socket
SocketState state;
// NOTE: These are the PSP types. Can be converted to the host types if needed.
int domain;
int type;
int protocol;
bool nonblocking;
// SO_RCVTIMEO and SO_SNDTIMEO, for the blocking calls we wait for in emulated time. 0 is forever.
u32 recvTimeoutUs;
u32 sendTimeoutUs;
// The other socket-level options, as the game sees them. Only a few are passed on to the host socket.
u32 soOptions; // PSP_NET_INET_SO_* flags, like BSD's so_options.
int lingerTime;
int sndBuf;
int rcvBuf;
int sndLowat;
int rcvLowat;
// The port bind() asked UPnP to forward, removed on close. 0 if none.
u16 upnpPort;
// What the game bound the socket to, for telling when a port is in use. boundPort is 0 if it hasn't
// bound one itself. boundAddr is in network byte order.
u32 boundAddr;
u16 boundPort;
// A TCP socket whose protocol control block 4.4BSD has thrown away: after shutting down the sending
// side while not connected, or a failed connect. Most calls on it then fail with EINVAL.
bool tcpClosed;
// Whether a listen() on it succeeded.
bool listening;
// IP_TTL, kept here since the host's default differs (Windows' is 128, the PSP's 64).
int ipTtl;
// Metadata for debug use only.
std::string addr;
int port;
};
// Only use this for sockets whose ID are exposed to the game.
// Don't really need to bother with the others, as the game doesn't know about them.
class SocketManager {
public:
enum {
VALID_INET_SOCKET_COUNT = 256,
MIN_VALID_INET_SOCKET = 1,
};
InetSocket *CreateSocket(int *index, int *returned_errno, SocketState state, int domain, int type, int protocol);
// for accept()
InetSocket *AdoptSocket(int *index, SOCKET hostSocket, const InetSocket *derive);
bool GetInetSocket(int sock, InetSocket **inetSocket);
SOCKET GetHostSocketFromInetSocket(int sock);
bool Close(InetSocket *inetSocket);
void CloseAll();
// For debugger
const InetSocket *Sockets() {
return inetSockets_;
}
private:
int FindFreeSlot();
// The last number handed out. Numbers go round-robin from there.
int lastSlot_ = 0;
// We use this array from MIN_VALID_INET_SOCKET and forward. It's probably not a good idea to return 0 as a socket.
InetSocket inetSockets_[VALID_INET_SOCKET_COUNT];
};
extern SocketManager g_socketManager;