try to ensure all data is written on socket shutdown

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/base/trunk@37402 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Richard Frith-MacDonald 2013-11-23 14:46:48 +00:00
parent a1dc63adf7
commit 33c451f902

View file

@ -95,13 +95,156 @@
#endif
// Maximum data in single I/O operation
#define NETBUF_SIZE 4096
#define NETBUF_SIZE (1024 * 16)
#define READ_SIZE NETBUF_SIZE*10
static GSFileHandle* fh_stdin = nil;
static GSFileHandle* fh_stdout = nil;
static GSFileHandle* fh_stderr = nil;
@interface GSTcpTune : NSObject
- (int) recvSize;
- (int) sendSize: (int)bytesToSend;
- (void) tune: (void*)handle;
@end
@implementation GSTcpTune
static int tuneLinger = -1;
static int tuneReceive = 0;
static BOOL tuneSendAll = NO;
static int tuneRBuf = 0;
static int tuneSBuf = 0;
+ (void) defaultsChanged: (NSNotification*)n
{
NSUserDefaults *defs = (NSUserDefaults*)[n object];
NSString *str;
if (nil == defs)
{
defs = [NSUserDefaults standardUserDefaults];
}
str = [defs stringForKey: @"GSTcpLinger"];
if (nil == str)
{
tuneLinger = -1;
}
else
{
tuneLinger = [str intValue];
}
tuneRBuf = (int)[defs integerForKey: @"GSTcpRcvBuf"];
tuneSBuf = (int)[defs integerForKey: @"GSTcpSndBuf"];
tuneReceive = (int)[defs integerForKey: @"GSTcpReceive"];
tuneSendAll = [defs boolForKey: @"GSTcpSendAll"];
}
+ (void) initialize
{
static BOOL beenHere = NO;
if (NO == beenHere)
{
NSNotificationCenter *nc;
NSUserDefaults *defs;
beenHere = YES;
nc = [NSNotificationCenter defaultCenter];
defs = [NSUserDefaults standardUserDefaults];
[nc addObserver: self
selector: @selector(defaultsChanged:)
name: NSUserDefaultsDidChangeNotification
object: defs];
[self defaultsChanged: nil];
}
}
- (int) recvSize
{
if (tuneReceive > 0)
{
return tuneReceive; // Return receive buffer size
}
if (tuneRBuf > 0)
{
return tuneRBuf; // Return socket receive buffer size
}
return READ_SIZE; // Return hard-coded default
}
- (int) sendSize: (int)bytesToSend
{
if (YES == tuneSendAll)
{
return bytesToSend; // Try to send all in one go
}
if (tuneSBuf > 0 && tuneSBuf <= bytesToSend)
{
return tuneSBuf; // Limit to socket send buffer
}
if (NETBUF_SIZE <= bytesToSend)
{
return NETBUF_SIZE; // Limit to hard coded default
}
return bytesToSend;
}
- (void) tune: (void*)handle
{
int desc = (int)(intptr_t)handle;
int value;
/*
* Enable tcp-level tracking of whether connection is alive.
*/
value = 1;
setsockopt(desc, SOL_SOCKET, SO_KEEPALIVE, (char *)&value, sizeof(value));
if (tuneLinger >= 0)
{
struct linger l;
l.l_onoff = 1;
l.l_linger = tuneLinger;
if (setsockopt(desc, SOL_SOCKET, SO_LINGER, (char *)&l, sizeof(l)) < 0)
{
NSLog(@"Failed to set GSTcpLinger %d: %@",
tuneLinger, [NSError _last]);
}
}
if (tuneRBuf > 0)
{
/* Set the receive buffer for the socket.
*/
if (setsockopt(desc, SOL_SOCKET, SO_RCVBUF,
(char *)&tuneRBuf, sizeof(tuneRBuf)) < 0)
{
NSLog(@"Failed to set GSTcpRcvBuf %d: %@", tuneRBuf, [NSError _last]);
}
else
{
NSDebugMLLog(@"GSTcpTune", @"Set GSTcpRcvBuf %d", tuneRBuf);
}
}
if (tuneSBuf > 0)
{
/* Set the send buffer for the socket.
*/
if (setsockopt(desc, SOL_SOCKET, SO_SNDBUF,
(char *)&tuneSBuf, sizeof(tuneSBuf)) < 0)
{
NSLog(@"Failed to set GSTcpSndBuf %d: %@", tuneSBuf, [NSError _last]);
}
else
{
NSDebugMLLog(@"GSTcpTune", @"Set GSTcpSndBuf %d", tuneSBuf);
}
}
}
@end
// Key to info dictionary for operation mode.
static NSString* NotificationKey = @"NSFileHandleNotificationKey";
@ -112,6 +255,16 @@ static NSString* NotificationKey = @"NSFileHandleNotificationKey";
@implementation GSFileHandle
static GSTcpTune *tune = nil;
+ (void) initialize
{
if (nil == tune)
{
tune = [GSTcpTune new];
}
}
/**
* Encapsulates low level read operation to get data from the operating
* system.
@ -608,8 +761,6 @@ NSString * const GSSOCKSRecvAddr = @"GSSOCKSRecvAddr";
{
static NSString *esocks = nil;
static NSString *dsocks = nil;
static int rbuf = 0;
static int sbuf = 0;
static BOOL beenHere = NO;
int net;
struct sockaddr sin;
@ -617,7 +768,6 @@ NSString * const GSSOCKSRecvAddr = @"GSSOCKSRecvAddr";
NSString *lhost = nil;
NSString *shost = nil;
NSString *sport = nil;
int status;
if (beenHere == NO)
{
@ -638,8 +788,6 @@ NSString * const GSSOCKSRecvAddr = @"GSSOCKSRecvAddr";
}
esocks = [esocks copy];
}
rbuf = (int)[defs integerForKey: @"GSTcpRcvBuf"];
sbuf = (int)[defs integerForKey: @"GSTcpSndBuf"];
}
if (a == nil || [a isEqualToString: @""])
@ -752,40 +900,8 @@ NSString * const GSSOCKSRecvAddr = @"GSSOCKSRecvAddr";
DESTROY(self);
return nil;
}
/*
* Enable tcp-level tracking of whether connection is alive.
*/
status = 1;
setsockopt(net, SOL_SOCKET, SO_KEEPALIVE, (char *)&status, sizeof(status));
if (rbuf > 0)
{
/* Set the receive buffer for the socket.
*/
if (setsockopt(net, SOL_SOCKET, SO_RCVBUF,
(char *)&rbuf, sizeof(rbuf)) < 0)
{
NSLog(@"Failed to set GSTcpRcvBuf %d: %@", rbuf, [NSError _last]);
}
else
{
NSLog(@"Set GSTcpRcvBuf %d", rbuf);
}
}
if (sbuf > 0)
{
/* Set the send buffer for the socket.
*/
if (setsockopt(net, SOL_SOCKET, SO_SNDBUF,
(char *)&sbuf, sizeof(sbuf)) < 0)
{
NSLog(@"Failed to set GSTcpSndBuf %d: %@", sbuf, [NSError _last]);
}
else
{
NSLog(@"Set GSTcpSndBuf %d", sbuf);
}
}
[tune tune: (void*)(intptr_t)net];
if (lhost != nil)
{
@ -1254,7 +1370,8 @@ NSString * const GSSOCKSRecvAddr = @"GSSOCKSRecvAddr";
- (NSData*) availableData
{
char buf[READ_SIZE];
int rmax = [tune recvSize];
char buf[rmax];
NSMutableData* d;
int len;
@ -1323,7 +1440,8 @@ NSString * const GSSOCKSRecvAddr = @"GSSOCKSRecvAddr";
- (NSData*) readDataToEndOfFile
{
char buf[READ_SIZE];
int rmax = [tune recvSize];
char buf[rmax];
NSMutableData* d;
int len;
@ -1350,7 +1468,8 @@ NSString * const GSSOCKSRecvAddr = @"GSSOCKSRecvAddr";
{
NSMutableData *d;
int got;
char buf[READ_SIZE];
int rmax = [tune recvSize];
char buf[rmax];
[self checkRead];
if (isNonBlocking == YES)
@ -1358,7 +1477,7 @@ NSString * const GSSOCKSRecvAddr = @"GSSOCKSRecvAddr";
[self setNonBlocking: NO];
}
d = [NSMutableData dataWithCapacity: len < READ_SIZE ? len : READ_SIZE];
d = [NSMutableData dataWithCapacity: len < sizeof(buf) ? len : sizeof(buf)];
do
{
int chunk = len > sizeof(buf) ? sizeof(buf) : len;
@ -1397,10 +1516,7 @@ NSString * const GSSOCKSRecvAddr = @"GSSOCKSRecvAddr";
{
int toWrite = len - pos;
if (toWrite > NETBUF_SIZE)
{
toWrite = NETBUF_SIZE;
}
toWrite = [tune sendSize: toWrite];
rval = [self write: (char*)ptr+pos length: toWrite];
if (rval < 0)
{
@ -1590,7 +1706,7 @@ NSString * const GSSOCKSRecvAddr = @"GSSOCKSRecvAddr";
[self ignoreReadDescriptor];
[self ignoreWriteDescriptor];
[self setNonBlocking: wasNonBlocking];
[self setNonBlocking: NO];
#if USE_ZLIB
if (gzDescriptor != 0)
{
@ -1598,6 +1714,26 @@ NSString * const GSSOCKSRecvAddr = @"GSSOCKSRecvAddr";
gzDescriptor = 0;
}
#endif
if (YES == isSocket)
{
shutdown(descriptor, SHUT_WR);
for(;;)
{
int result;
char buffer[4096];
result = read(descriptor, buffer, sizeof(buffer));
if (result <= 0)
{
if (result < 0)
{
NSLog(@"%@ read fail on socket shutdown: %@",
self, [NSError _last]);
}
break;
}
}
}
(void)close(descriptor);
descriptor = -1;
acceptOK = NO;
@ -1917,17 +2053,11 @@ NSString * const GSSOCKSRecvAddr = @"GSSOCKSRecvAddr";
GSFileHandle *h;
struct sockaddr sin;
unsigned int size = sizeof(sin);
int status;
/*
* Enable tcp-level tracking of whether connection is alive.
*/
status = 1;
setsockopt(desc, SOL_SOCKET, SO_KEEPALIVE, (char *)&status,
sizeof(status));
[tune tune: (void*)(intptr_t)desc];
h = [[[self class] alloc] initWithFileDescriptor: desc
closeOnDealloc: YES];
closeOnDealloc: YES];
h->isSocket = YES;
getpeername(desc, &sin, &size);
[h setAddr: &sin];
@ -1946,7 +2076,8 @@ NSString * const GSSOCKSRecvAddr = @"GSSOCKSRecvAddr";
NSMutableData *item;
int length;
int received = 0;
char buf[READ_SIZE];
int rmax = [tune recvSize];
char buf[rmax];
item = [readInfo objectForKey: NSFileHandleNotificationDataItem];
/*