I tried this on 10.3.1 and it’s working for me. I’ve pasted in my code below, so you can try it for yourself. Here’s how I tested this:
I ran the app and started the listener
From my Mac I used telnet to connect to the server; the server immediately closes the socket, which puts the connection in the
TIME_WAIT
stateI stopped the listener
I started the listener again
Without the
SO_REUSEADDR
the
bind
in step 4 consistently fails with
EADDRINUSE
. With the
SO_REUSEADDR
it works as expected.
btw Is there a reason you’re using BSD Sockets for your listener rather than NSNetService. The latter is much easier.
Share and Enjoy
—
Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware
let myEmail = "eskimo" + "1" + "@apple.com"
#import "MainViewController.h"
#include <sys/socket.h>
#include <netinet/in.h>
@interface MainViewController ()
@property (nonatomic, assign, readwrite) int listener;
@end
@implementation MainViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.listener = -1;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
#pragma unused(tableView)
#pragma unused(indexPath)
if (self.listener == -1) {
[self start];
} else {
[self stop];
}
[self.tableView deselectRowAtIndexPath:indexPath animated:YES];
}
- (void)start {
NSLog(@"listener will start");
self.listener = socket(AF_INET, SOCK_STREAM, 0);
assert(self.listener >= 0);
struct sockaddr_in addr = {
.sin_len = sizeof(struct sockaddr_in),
.sin_family = AF_INET,
.sin_port = htons(12345),
.sin_addr.s_addr = INADDR_ANY
};
static const int kOne = 1;
int err = setsockopt(self.listener, SOL_SOCKET, SO_REUSEADDR, &kOne, sizeof(kOne));
assert(err == 0);
err = bind(self.listener, (const struct sockaddr *) &addr, sizeof(addr));
if (err < 0) {
NSLog(@"bind failed: %d", errno);
[self stop];
return;
}
err = listen(self.listener, 5);
if (err < 0) {
NSLog(@"listen failed: %d", errno);
[self stop];
return;
}
[NSThread detachNewThreadSelector:@selector(rejectThreadEntry) toTarget:self withObject:nil];
NSLog(@"listener did start");
}
- (void)rejectThreadEntry {
NSLog(@"reject will start");
// This code is /very very/ bogus and should not be used in a real app. Specifically,
// there's a race between this thread and the main thread calling `close` that could
// result in us calling `accept` on some unrelated file descriptor.
int fd = self.listener;
do {
int fd2 = accept(fd, NULL, NULL);
if (fd2 >= 0) {
int junk = close(fd2);
assert(junk == 0);
NSLog(@"reject did reject");
} else {
NSLog(@"reject did error: %d", errno);
break;
}
} while (YES);
NSLog(@"reject did stop");
}
- (void)stop {
int junk = close(self.listener);
assert(junk == 0);
self.listener = -1;
NSLog(@"listener stopped");
}
@end