Commit 1e34dbb5 authored by moret's avatar moret
Browse files

Improve NetMaster 'slow mode'

git-svn-id: http://subversion.jackaudio.org/jack/jack2/trunk/jackmp@2755 0c269be4-1314-0410-8aa9-9f06e86f4224
parent 24b28fd2
......@@ -54,7 +54,7 @@ namespace Jack
JackNetDriver::JackNetDriver ( const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table,
const char* ip, int port, int mtu, int midi_input_ports, int midi_output_ports,
const char* net_name, uint transport_sync, char network_master_mode, char network_slave_mode )
const char* net_name, uint transport_sync, char network_master_mode )
: JackAudioDriver ( name, alias, engine, table ), fSocket ( ip, port )
{
jack_log ( "JackNetDriver::JackNetDriver ip %s, port %d", ip, port );
......@@ -68,7 +68,6 @@ namespace Jack
fSocket.GetName ( fParams.fSlaveNetName );
fParams.fTransportSync = transport_sync;
fParams.fNetworkMasterMode = network_master_mode;
fParams.fNetworkSlaveMode = network_slave_mode;
#ifdef JACK_MONITOR
fMonitor = NULL;
fMeasure = NULL;
......@@ -190,6 +189,7 @@ namespace Jack
session_params_t params;
int us_timeout = 2000000;
int rx_bytes = 0;
unsigned char loop = 0;
//socket
if ( fSocket.NewSocket() == SOCKET_ERROR )
......@@ -206,6 +206,10 @@ namespace Jack
if ( fSocket.SetTimeOut ( us_timeout ) == SOCKET_ERROR )
jack_error ( "Can't set timeout : %s", StrError ( NET_ERROR_CODE ) );
//disable local loop
if ( fSocket.SetOption ( IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof ( loop ) ) == SOCKET_ERROR )
jack_error ( "Can't disable multicast loop : %s", StrError ( NET_ERROR_CODE ) );
//send 'AVAILABLE' until 'SLAVE_SETUP' received
jack_info ( "Waiting for a master..." );
do
......@@ -213,19 +217,15 @@ namespace Jack
//send 'available'
if ( fSocket.SendTo ( &fParams, sizeof ( session_params_t ), 0, fMulticastIP ) == SOCKET_ERROR )
jack_error ( "Error in data send : %s", StrError ( NET_ERROR_CODE ) );
//filter incoming packets : don't exit while receiving wrong packets
do
//filter incoming packets : don't exit while no error is detected
rx_bytes = fSocket.CatchHost ( &params, sizeof ( session_params_t ), 0 );
if ( ( rx_bytes == SOCKET_ERROR ) && ( fSocket.GetError() != NET_NO_DATA ) )
{
rx_bytes = fSocket.CatchHost ( &params, sizeof ( session_params_t ), 0 );
if ( ( rx_bytes == SOCKET_ERROR ) && ( fSocket.GetError() != NET_NO_DATA ) )
{
jack_error ( "Can't receive : %s", StrError ( NET_ERROR_CODE ) );
return NET_RECV_ERROR;
}
jack_error ( "Can't receive : %s", StrError ( NET_ERROR_CODE ) );
return NET_RECV_ERROR;
}
while ( ( rx_bytes > 0 ) && strcmp ( params.fPacketType, fParams.fPacketType ) );
}
while ( ( GetPacketType ( &params ) != SLAVE_SETUP ) );
while ( strcmp ( params.fPacketType, fParams.fPacketType ) && ( GetPacketType ( &params ) != SLAVE_SETUP ) );
//connect the socket
if ( fSocket.Connect() == SOCKET_ERROR )
......@@ -526,10 +526,6 @@ namespace Jack
int JackNetDriver::Read()
{
//slow mode and first cycle : skip read
if ( ( fParams.fNetworkSlaveMode == 's' ) && ( fRxHeader.fCycle == 0 ) )
return 0;
int rx_bytes;
uint recvd_midi_pckt = 0;
packet_header_t* rx_head = reinterpret_cast<packet_header_t*> ( fRxBuffer );
......@@ -779,7 +775,7 @@ namespace Jack
desc->params[i].value.ui = 's';
strcpy ( desc->params[i].short_desc, "Slow network add 1 cycle latency" );
strcpy ( desc->params[i].long_desc, "'s' for slow, 'f' for fast, default is slow.\
Fast network will make the master waiting for the current cycle return data." );
Fast network will make the master waiting for the current cycle return data." );
i++;
strcpy ( desc->params[i].name, "network_slave_mode" );
......@@ -788,7 +784,7 @@ namespace Jack
desc->params[i].value.ui = 's';
strcpy ( desc->params[i].short_desc, "Slow network add 1 cycle latency" );
strcpy ( desc->params[i].long_desc, "'s' for slow, 'f' for fast, default is slow.\
Fast network will make the slave waiting for the first cycle data." );
Fast network will make the slave waiting for the first cycle data." );
return desc;
}
......@@ -814,7 +810,6 @@ namespace Jack
int midi_output_ports = 0;
bool monitor = false;
char network_master_mode = 's';
char network_slave_mode = 's';
const JSList* node;
const jack_driver_param_t* param;
......@@ -850,40 +845,26 @@ namespace Jack
case 't' :
transport_sync = param->value.ui;
break;
case 'm' :
switch ( param->value.ui )
{
case 's' :
network_master_mode = 's';
break;
case 'f' :
network_master_mode = 'f';
break;
default :
network_master_mode = 's';
break;
}
break;
case 's' :
switch ( param->value.ui )
{
case 's' :
network_slave_mode = 's';
break;
case 'f' :
network_slave_mode = 'f';
break;
default :
network_slave_mode = 's';
break;
}
break;
case 'N' :
switch ( param->value.ui )
{
case 's' :
network_master_mode = 's';
break;
case 'f' :
network_master_mode = 'f';
break;
default :
network_master_mode = 's';
break;
}
break;
}
}
Jack::JackDriverClientInterface* driver = new Jack::JackWaitThreadedDriver (
new Jack::JackNetDriver ( "system", "net_pcm", engine, table, multicast_ip, udp_port, mtu,
midi_input_ports, midi_output_ports, name, transport_sync, network_master_mode, network_slave_mode ) );
midi_input_ports, midi_output_ports, name, transport_sync, network_master_mode ) );
if ( driver->Open ( period_size, sample_rate, 1, 1, audio_capture_ports, audio_playback_ports,
monitor, "from_master_", "to_master_", 0, 0 ) == 0 )
return driver;
......
......@@ -30,9 +30,9 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
namespace Jack
{
/**
\Brief This class describes the Net Backend
*/
/**
\Brief This class describes the Net Backend
*/
class JackNetDriver : public JackAudioDriver
{
......@@ -100,7 +100,7 @@ namespace Jack
public:
JackNetDriver ( const char* name, const char* alias, JackLockedEngine* engine, JackSynchro* table,
const char* ip, int port, int mtu, int midi_input_ports, int midi_output_ports,
const char* master_name, uint transport_sync, char network_master_mode, char network_slave_mode );
const char* master_name, uint transport_sync, char network_master_mode );
~JackNetDriver();
int Open ( jack_nframes_t frames_per_cycle, jack_nframes_t rate, bool capturing, bool playing,
......
......@@ -166,6 +166,7 @@ namespace Jack
int usec_timeout = 1000000;
uint attempt = 0;
int rx_bytes = 0;
int rx_bufsize = 0;
//socket
if ( fSocket.NewSocket() == SOCKET_ERROR )
......@@ -212,6 +213,14 @@ namespace Jack
return false;
}
//set the new rx buffer size
rx_bufsize = GetNetBufferSize ( &fParams );
if ( fSocket.SetOption ( SOL_SOCKET, SO_RCVBUF, &rx_bufsize, sizeof ( rx_bufsize ) ) == SOCKET_ERROR )
{
jack_error ( "Can't set rx buffer size : %s", StrError ( NET_ERROR_CODE ) );
return false;
}
//jack client and process
jack_status_t status;
jack_options_t options = JackNullOption;
......@@ -354,12 +363,9 @@ namespace Jack
if ( ( rx_bytes = fSocket.Recv ( fRxBuffer, size, flags ) ) == SOCKET_ERROR )
{
net_error_t error = fSocket.GetError();
//no data isn't really a network error, so just return 0 avalaible read bytes
if ( error == NET_NO_DATA )
{
//no data isn't really a network error, so just return 0 avalaible read bytes
jack_error ( "No data from %s...", fParams.fName );
return 0;
}
else if ( fRunning && ( error == NET_CONN_ERROR ) )
{
//fatal connection issue, exit
......@@ -387,8 +393,8 @@ namespace Jack
int tx_bytes = 0;
int rx_bytes = 0;
int copy_size = 0;
size_t midi_recvd_pckt = 0;
uint midi_recvd_pckt = 0;
uint jumpcnt = 0;
fTxHeader.fCycle++;
fTxHeader.fSubCycle = 0;
fTxHeader.fIsLastPckt = 'n';
......@@ -421,12 +427,13 @@ namespace Jack
//send ------------------------------------------------------------------------------------------------------------------
//sync
fTxHeader.fDataType = 's';
//memset ( fTxData, 0, fPayloadSize );
//SetSyncPacket();
if ( !fParams.fSendMidiChannels && !fParams.fSendAudioChannels )
fTxHeader.fIsLastPckt = 'y';
fTxHeader.fPacketSize = sizeof ( packet_header_t );
memcpy ( fTxBuffer, &fTxHeader, sizeof ( packet_header_t ) );
//memset ( fTxData, 0, fPayloadSize );
//SetSyncPacket();
tx_bytes = Send ( fTxBuffer, sizeof ( packet_header_t ), 0 );
tx_bytes = Send ( fTxBuffer, fTxHeader.fPacketSize, 0 );
if ( tx_bytes == SOCKET_ERROR )
return tx_bytes;
......@@ -445,9 +452,10 @@ namespace Jack
fTxHeader.fSubCycle = subproc;
if ( ( subproc == ( fTxHeader.fNMidiPckt - 1 ) ) && !fParams.fSendAudioChannels )
fTxHeader.fIsLastPckt = 'y';
fTxHeader.fPacketSize = fNetMidiCaptureBuffer->RenderToNetwork ( subproc, fTxHeader.fMidiDataSize );
fTxHeader.fPacketSize += sizeof ( packet_header_t );
memcpy ( fTxBuffer, &fTxHeader, sizeof ( packet_header_t ) );
copy_size = fNetMidiCaptureBuffer->RenderToNetwork ( subproc, fTxHeader.fMidiDataSize );
tx_bytes = Send ( fTxBuffer, sizeof ( packet_header_t ) + copy_size, 0 );
tx_bytes = Send ( fTxBuffer, fTxHeader.fPacketSize, 0 );
if ( tx_bytes == SOCKET_ERROR )
return tx_bytes;
}
......@@ -462,9 +470,10 @@ namespace Jack
fTxHeader.fSubCycle = subproc;
if ( subproc == ( fNSubProcess - 1 ) )
fTxHeader.fIsLastPckt = 'y';
memcpy ( fTxBuffer, &fTxHeader, sizeof ( packet_header_t ) );
fTxHeader.fPacketSize = fAudioTxLen;
fNetAudioCaptureBuffer->RenderFromJackPorts ( subproc );
tx_bytes = Send ( fTxBuffer, fAudioTxLen, 0 );
memcpy ( fTxBuffer, &fTxHeader, sizeof ( packet_header_t ) );
tx_bytes = Send ( fTxBuffer, fTxHeader.fPacketSize, 0 );
if ( tx_bytes == SOCKET_ERROR )
return tx_bytes;
}
......@@ -476,10 +485,10 @@ namespace Jack
//receive --------------------------------------------------------------------------------------------------------------------
//sync
rx_bytes == Recv ( sizeof ( packet_header_t ), 0 );
rx_bytes = Recv ( sizeof ( packet_header_t ), 0 );
//wait here for sync, switch network mode :
// -fast : this recv will wait for a long time (90% of cycle duration)
// -slow : just wait for a short time, then return, the data will be available at the next cycle
// -fast : this recv will wait for a long time (90% of cycle duration)
// -slow : just wait for a short time, then return, the data will be available at the next cycle
if ( ( rx_bytes == 0 ) || ( rx_bytes == SOCKET_ERROR ) )
return rx_bytes;
......@@ -492,33 +501,38 @@ namespace Jack
do
{
rx_bytes = Recv ( fParams.fMtu, MSG_PEEK );
if ( ( rx_bytes == 0 ) || ( rx_bytes == SOCKET_ERROR ) )
if ( rx_bytes == SOCKET_ERROR )
return rx_bytes;
if ( ++jumpcnt == fNSubProcess )
{
jack_error ( "No data from %s...", fParams.fName );
jumpcnt = 0;
}
if ( rx_bytes && ( rx_head->fDataStream == 'r' ) && ( rx_head->fID == fParams.fID ) )
{
switch ( rx_head->fDataType )
{
case 'm': //midi
rx_bytes = Recv ( rx_bytes, 0 );
Recv ( rx_head->fPacketSize, 0 );
fRxHeader.fCycle = rx_head->fCycle;
fRxHeader.fIsLastPckt = rx_head->fIsLastPckt;
fNetMidiPlaybackBuffer->RenderFromNetwork ( rx_head->fSubCycle, rx_bytes - sizeof ( packet_header_t ) );
if ( ++midi_recvd_pckt == rx_head->fNMidiPckt )
fNetMidiPlaybackBuffer->RenderToJackPorts();
jumpcnt = 0;
break;
case 'a': //audio
rx_bytes = Recv ( fAudioRxLen, 0 );
Recv ( rx_head->fPacketSize, 0 );
if ( !IsNextPacket ( &fRxHeader, rx_head, fNSubProcess ) )
jack_error ( "Packet(s) missing from '%s'...", fParams.fName );
fRxHeader.fCycle = rx_head->fCycle;
fRxHeader.fSubCycle = rx_head->fSubCycle;
fRxHeader.fIsLastPckt = rx_head->fIsLastPckt;
fNetAudioPlaybackBuffer->RenderToJackPorts ( rx_head->fSubCycle );
jumpcnt = 0;
break;
case 's': //sync
rx_bytes = Recv ( rx_bytes, 0 );
fRxHeader.fCycle = rx_head->fCycle;
fRxHeader.fSubCycle = rx_head->fSubCycle;
Recv ( rx_head->fPacketSize, 0 );
return 0;
}
}
......
......@@ -271,6 +271,7 @@ namespace Jack
header->fMidiDataSize = htonl ( header->fMidiDataSize );
header->fBitdepth = htonl ( header->fBitdepth );
header->fNMidiPckt = htonl ( header->fNMidiPckt );
header->fPacketSize = htonl ( header->fPacketSize );
header->fCycle = ntohl ( header->fCycle );
header->fSubCycle = htonl ( header->fSubCycle );
}
......@@ -281,6 +282,7 @@ namespace Jack
header->fMidiDataSize = ntohl ( header->fMidiDataSize );
header->fBitdepth = ntohl ( header->fBitdepth );
header->fNMidiPckt = ntohl ( header->fNMidiPckt );
header->fPacketSize = ntohl ( header->fPacketSize );
header->fCycle = ntohl ( header->fCycle );
header->fSubCycle = ntohl ( header->fSubCycle );
}
......@@ -338,12 +340,23 @@ namespace Jack
{
if ( !params->fSendAudioChannels && !params->fReturnAudioChannels )
return ( params->fFramesPerPacket = params->fPeriodSize );
size_t period = ( int ) powf ( 2.f, ( int ) ( log ( ( params->fMtu - sizeof ( packet_header_t ) )
jack_nframes_t period = ( int ) powf ( 2.f, ( int ) ( log ( ( params->fMtu - sizeof ( packet_header_t ) )
/ ( max ( params->fReturnAudioChannels, params->fSendAudioChannels ) * sizeof ( sample_t ) ) ) / log ( 2 ) ) );
( period > params->fPeriodSize ) ? params->fFramesPerPacket = params->fPeriodSize : params->fFramesPerPacket = period;
return params->fFramesPerPacket;
}
EXPORT int GetNetBufferSize ( session_params_t* params )
{
//audio
float audio_size = params->fMtu * ( params->fPeriodSize / params->fFramesPerPacket );
//midi
float midi_size = params->fMtu * ( max ( params->fSendMidiChannels, params->fReturnMidiChannels ) *
params->fPeriodSize * sizeof ( sample_t ) / ( params->fMtu - sizeof ( packet_header_t ) ) );
//return : sizes of sync + audio + midi
return ( params->fMtu + ( int ) audio_size + ( int ) midi_size );
}
EXPORT int GetNMidiPckt ( session_params_t* params, size_t data_size )
{
//even if there is no midi data, jack need an empty buffer to know there is no event to read
......@@ -357,18 +370,18 @@ namespace Jack
return npckt;
}
EXPORT int SetRxTimeout ( JackNetSocket* socket, session_params_t* params )
{
float time;
//fast mode, wait for the entire cycle duration
if ( params->fNetworkMasterMode == 'f' )
time = 900000.f * ( static_cast<float> ( params->fPeriodSize ) / static_cast<float> ( params->fSampleRate ) );
//slow mode, just try recv during a subcycle audio packet
else
time = 1250000.f * ( static_cast<float> ( params->fFramesPerPacket ) / static_cast<float> ( params->fSampleRate ) );
EXPORT int SetRxTimeout ( JackNetSocket* socket, session_params_t* params )
{
float time;
//fast mode, wait for the entire cycle duration
if ( params->fNetworkMasterMode == 'f' )
time = 900000.f * ( static_cast<float> ( params->fPeriodSize ) / static_cast<float> ( params->fSampleRate ) );
//slow mode, just try recv during two subcycle audio packets
else
time = 2000000.f * ( static_cast<float> ( params->fFramesPerPacket ) / static_cast<float> ( params->fSampleRate ) );
int usec = ( int ) time;
return socket->SetTimeOut ( usec );
}
return socket->SetTimeOut ( usec );
}
// Packet *******************************************************************************************************
......
......@@ -39,19 +39,19 @@ namespace Jack
//session params ******************************************************************************
/**
\brief This structure containes master/slave connection parameters, it's used to setup the whole system
We have :
- some info like version, type and packet id
- names
- network parameters (hostnames and mtu)
- nunber of audio and midi channels
- sample rate and buffersize
- number of audio frames in one network packet (depends on the channel number)
- is the NetDriver in Sync or ASync mode ?
- is the NetDriver linked with the master's transport
*/
/**
\brief This structure containes master/slave connection parameters, it's used to setup the whole system
We have :
- some info like version, type and packet id
- names
- network parameters (hostnames and mtu)
- nunber of audio and midi channels
- sample rate and buffersize
- number of audio frames in one network packet (depends on the channel number)
- is the NetDriver in Sync or ASync mode ?
- is the NetDriver linked with the master's transport
*/
struct _session_params
{
......@@ -73,15 +73,14 @@ We have :
uint32_t fFramesPerPacket; //complete frames per packet
uint32_t fBitdepth; //samples bitdepth (unused)
uint32_t fSlaveSyncMode; //is the slave in sync mode ?
char fNetworkMasterMode; //fast or slow mode
char fNetworkSlaveMode; //fast or slow mode
char fNetworkMasterMode; //fast or slow mode
};
//net status **********************************************************************************
/**
\Brief This enum groups network error by type
*/
/**
\Brief This enum groups network error by type
*/
enum _net_status
{
......@@ -98,9 +97,9 @@ We have :
//sync packet type ****************************************************************************
/**
\Brief This enum indicates the type of a sync packet (used in the initialization phase)
*/
/**
\Brief This enum indicates the type of a sync packet (used in the initialization phase)
*/
enum _sync_packet_type
{
......@@ -117,24 +116,24 @@ We have :
//packet header *******************************************************************************
/**
\Brief This structure is a complete header
A header indicates :
- it is a header
- the type of data the packet contains (sync, midi or audio)
- the path of the packet (send -master->slave- or return -slave->master-)
- the unique ID of the slave
- the sample's bitdepth (unused for now)
- the size of the midi data contains in the packet (indicates how much midi data will be sent)
- the number of midi packet(s) : more than one is very unusual, it depends on the midi load
- the ID of the current cycle (used to check missing packets)
- the ID of the packet subcycle (for audio data)
- a flag indicating this packet is the last of the cycle (for sync robustness, it's better to process this way)
- a flag indicating if, in async mode, the previous graph was not finished or not
- padding to fill 64 bytes
*/
/**
\Brief This structure is a complete header
A header indicates :
- it is a header
- the type of data the packet contains (sync, midi or audio)
- the path of the packet (send -master->slave- or return -slave->master-)
- the unique ID of the slave
- the sample's bitdepth (unused for now)
- the size of the midi data contains in the packet (indicates how much midi data will be sent)
- the number of midi packet(s) : more than one is very unusual, it depends on the midi load
- the ID of the current cycle (used to check missing packets)
- the ID of the packet subcycle (for audio data)
- a flag indicating this packet is the last of the cycle (for sync robustness, it's better to process this way)
- a flag indicating if, in async mode, the previous graph was not finished or not
- padding to fill 64 bytes
*/
struct _packet_header
{
......@@ -143,8 +142,9 @@ A header indicates :
char fDataStream; //s for send, r for return
uint32_t fID; //unique ID of the slave
uint32_t fBitdepth; //bitdepth of the data samples
uint32_t fMidiDataSize; //size of midi data (if packet is 'midi typed') in bytes
uint32_t fMidiDataSize; //size of midi data in bytes
uint32_t fNMidiPckt; //number of midi packets of the cycle
uint32_t fPacketSize; //packet size in bytes
uint32_t fCycle; //process cycle counter
uint32_t fSubCycle; //midi/audio subcycle counter
char fIsLastPckt; //is it the last packet of a given cycle ('y' or 'n')
......@@ -154,9 +154,9 @@ A header indicates :
//transport data ******************************************************************************
/**
\Brief This structure contains transport info
*/
/**
\Brief This structure contains transport info
*/
struct _net_transport_data
{
......@@ -166,20 +166,20 @@ A header indicates :
//midi data ***********************************************************************************
/**
\Brief Midi buffer and operations class
/**
\Brief Midi buffer and operations class
This class is a toolset to manipulate Midi buffers.
A JackMidiBuffer has a fixed size, which is the same than an audio buffer size.
An intermediate fixed size buffer allows to uninterleave midi data (from jack ports).
But for a big majority of the process cycles, this buffer is filled less than 1%,
Sending over a network 99% of useless data seems completely unappropriate.
The idea is to count effective midi data, and then send the smallest packet we can.
To do it, we use an intermediate buffer.
We have two methods to convert data from jack ports to intermediate buffer,
And two others to convert this intermediate buffer to a network buffer (header + payload data)
This class is a toolset to manipulate Midi buffers.
A JackMidiBuffer has a fixed size, which is the same than an audio buffer size.
An intermediate fixed size buffer allows to uninterleave midi data (from jack ports).
But for a big majority of the process cycles, this buffer is filled less than 1%,
Sending over a network 99% of useless data seems completely unappropriate.
The idea is to count effective midi data, and then send the smallest packet we can.
To do it, we use an intermediate buffer.
We have two methods to convert data from jack ports to intermediate buffer,
And two others to convert this intermediate buffer to a network buffer (header + payload data)
*/
*/
class EXPORT NetMidiBuffer
{
......@@ -212,15 +212,15 @@ And two others to convert this intermediate buffer to a network buffer (header +
// audio data *********************************************************************************
/**
\Brief Audio buffer and operations class
/**
\Brief Audio buffer and operations class
This class is a toolset to manipulate audio buffers.
The manipulation of audio buffers is similar to midi buffer, except those buffers have fixed size.
The interleaving/uninterleaving operations are simplier here because audio buffers have fixed size,
So there is no need of an intermediate buffer as in NetMidiBuffer.
This class is a toolset to manipulate audio buffers.
The manipulation of audio buffers is similar to midi buffer, except those buffers have fixed size.
The interleaving/uninterleaving operations are simplier here because audio buffers have fixed size,
So there is no need of an intermediate buffer as in NetMidiBuffer.
*/
*/
class EXPORT NetAudioBuffer
{
......@@ -264,6 +264,8 @@ So there is no need of an intermediate buffer as in NetMidiBuffer.
EXPORT int SetPacketType ( session_params_t* params, sync_packet_type_t packet_type );
//step of network initialization
EXPORT jack_nframes_t SetFramesPerPacket ( session_params_t* params );
//step of network initialization
EXPORT int GetNetBufferSize ( session_params_t* params );
//get the midi packet number for a given cycle
EXPORT int GetNMidiPckt ( session_params_t* params, size_t data_size );
//set the recv timeout on a socket
......
......@@ -273,6 +273,8 @@ namespace Jack
return NET_NO_DATA;
case ECONNABORTED:
return NET_CONN_ERROR;
case EINVAL:
return NET_CONN_ERROR;
case ECONNREFUSED:
return NET_CONN_ERROR;
case ECONNRESET:
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment