Skip to content

Sockets, libdispatch, and gnustep

August 26, 2013

Here are a client and server to show the use of GNUstep, libdispatch and sockets. This code was developed on FreeBSD.
There are three files:-

  • GNUmakefile
  • server.m
  • client.m

If you’re familiar with sockets code, you can see that the coding is vanilla sockets right up to the comments where the dispatch queues and sources are setup. The sockets code is based on code presented at: http://beej.us/guide/bgnet/ – so you can compare the two.

I used gnustep packages built on FreeBSD with clang – see my previous post. Assuming a working GNUstep and with libdispatch installed, just put all three files in a directory and type “gmake”.

GNUmakefile

include $(GNUSTEP_MAKEFILES)/common.make

TOOL_NAME = client server
client_OBJC_FILES = client.m
server_OBJC_FILES = server.m

include $(GNUSTEP_MAKEFILES)/tool.make

server.m

#include <unistd.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>

#import <Foundation/Foundation.h>
#import <dispatch/dispatch.h>
#define PORT "2305" // the port users will be connecting to
#define BACKLOG 10 // how many pending connections queue will hold

// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
 if (sa->sa_family == AF_INET) {
 return &(((struct sockaddr_in*)sa)->sin_addr);
 }
 return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

int main(void)
{
 int sockfd;
 struct addrinfo hints, *servinfo, *p;
 int yes=1;
 int rv;

memset(&hints, 0, sizeof hints);
 hints.ai_family = AF_UNSPEC;
 hints.ai_socktype = SOCK_STREAM;
 hints.ai_flags = AI_PASSIVE;

if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) {
 fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
 return 1;
 }

//loop to obtain a socket
 for(p = servinfo; p != NULL; p = p->ai_next) {
 if ((sockfd = socket(p->ai_family, p->ai_socktype,
 p->ai_protocol)) == -1) {
 perror("server: socket");
 continue;
 }

if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes,
 sizeof(int)) == -1) {
 perror("setsockopt");
 exit(1);
 }

if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
 close(sockfd);
 perror("server: bind");
 continue;
 }

break;
 }

if (p == NULL) {
 fprintf(stderr, "server: failed to bind\n");
 return 2;
 }

freeaddrinfo(servinfo);

if (listen(sockfd, BACKLOG) == -1) {
 perror("listen");
 exit(1);
 }
 NSLog(@"Listening...");

//use a libdispatch queue to read data from the socket
 dispatch_semaphore_t exitsignal = dispatch_semaphore_create(0);
 dispatch_queue_t dq = dispatch_queue_create("data", NULL);
 dispatch_source_t ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, sockfd, 0, dq);

dispatch_source_set_event_handler(ds, ^{
 int new_fd;
 struct sockaddr_storage their_addr;
 socklen_t sin_size;
 char s[INET6_ADDRSTRLEN];
 sin_size = sizeof their_addr;

new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &sin_size);
 if (new_fd == -1) {
 perror("accept");
 }

inet_ntop(their_addr.ss_family,
 get_in_addr((struct sockaddr *)&their_addr),
 s, sizeof s);
 NSLog(@"server: got connection from %s\n", s);

// reply to client
 if (send(new_fd, "Hello, world!", 13, 0) == -1) perror("send");
 close(new_fd);
 });

dispatch_source_set_cancel_handler(ds, ^{
 int rfd = (int)dispatch_source_get_handle(ds);
 close(rfd);
 dispatch_semaphore_signal(exitsignal);
 });

dispatch_resume(ds);
 dispatch_main(); // suspend run loop waiting for blocks

dispatch_semaphore_wait(exitsignal, dispatch_walltime(NULL, DISPATCH_TIME_FOREVER));

 dispatch_release(ds);
 dispatch_release(dq);
 dispatch_release(exitsignal);

return 0;
}

client.m

#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#import <Foundation/Foundation.h>
#import <dispatch/dispatch.h>

#define PORT "2305" // the port client will be connecting to

#define MAXDATASIZE 100 // max number of bytes we can get at once

// get sockaddr, IPv4 or IPv6:
void *get_in_addr(struct sockaddr *sa)
{
 if (sa->sa_family == AF_INET) {
 return &(((struct sockaddr_in*)sa)->sin_addr);
 }

return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

int main(int argc, char *argv[])
{
 int sockfd;
 struct addrinfo hints, *servinfo, *p;
 int rv;
 char s[INET6_ADDRSTRLEN];

if (argc != 2) {
 fprintf(stderr,"usage: client hostname\n");
 exit(1);
 }

memset(&hints, 0, sizeof hints);
 hints.ai_family = AF_UNSPEC;
 hints.ai_socktype = SOCK_STREAM;

if ((rv = getaddrinfo(argv[1], PORT, &hints, &servinfo)) != 0) {
 fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv));
 return 1;
 }

//loop to obtain a socket
 for(p = servinfo; p != NULL; p = p->ai_next) {
 if ((sockfd = socket(p->ai_family, p->ai_socktype,
 p->ai_protocol)) == -1) {
 perror("client: socket");
 continue;
 }

if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) {
 close(sockfd);
 perror("client: connect");
 continue;
 }

break;
 }

if (p == NULL) {
 fprintf(stderr, "client: failed to connect\n");
 return 2;
 }

inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr),
 s, sizeof s);
 printf("client: connecting to %s\n", s);

freeaddrinfo(servinfo);

//use a libdispatch queue to read data from the socket
 dispatch_semaphore_t exitsignal = dispatch_semaphore_create(0);
 dispatch_queue_t dq = dispatch_queue_create("data", NULL);
 dispatch_source_t ds = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, sockfd, 0, dq);

dispatch_source_set_event_handler(ds, ^{
 int numbytes;
 char buf[MAXDATASIZE];

 int rfd = (int)dispatch_source_get_handle(ds);
 size_t bytesAvail = dispatch_source_get_data(ds);
 if (bytesAvail == 0) {
 dispatch_source_cancel(ds);
 return;
 } else {
 printf("..dispatch source %d has %lu bytes available.\n",rfd, bytesAvail);
 }

if ((numbytes = recv(rfd, buf, MAXDATASIZE-1, 0)) == -1) {
 perror("recv");
 exit(1);
 }
 buf[numbytes] = '\0';
 printf("client: received '%s'\n",buf);
 });

dispatch_source_set_cancel_handler(ds, ^{
 //handler to close the socket - then signal to exit
 int rfd = (int)dispatch_source_get_handle(ds);
 close(rfd);
 dispatch_semaphore_signal(exitsignal);
 });

dispatch_resume(ds);
 // block until the semaphore is signalled - when socket closes
 // second parameter is a timeout - nanoseconds
 dispatch_semaphore_wait(exitsignal, dispatch_walltime(NULL, 5*1000000000));

dispatch_release(ds);
 dispatch_release(dq);
 dispatch_release(exitsignal);

return 0;
}
Advertisements

From → Uncategorized

Leave a Comment

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: