iOS and OS X Network Programming Cookbook
上QQ阅读APP看书,第一时间看更新

Creating a data client

In the Creating a data server recipe of this chapter, we updated our BSDSocketServer class so we could set up a server that could receive data. In this recipe, we will be updating our BSDSocketClient class so we can set up a client to upload data to our data server.

Getting ready

This recipe is compatible with both iOS and OS X. No extra frameworks or libraries are required.

How to do it…

Let's update the BSDSocketClient class to include our data client.

Updating the BSDSocketClient header file

Since we will be able to use the same constructor (initWithAddress:andPort:) that we used when we connected to the echo server, all we need to do is to add a method to send the data itself. This method will be called sendData:toSocket:. The following is the new BSDSocketClient header file:

  #import <Foundation/Foundation.h>
  
  typedef NS_ENUM(NSUInteger, BSDClientErrorCode) {
    NOERRROR,
    SOCKETERROR,
    CONNECTERROR,
    READERROR,
    WRITEERROR
};  
  #define MAXLINE 4096
  
  @interface BSDSocketClient : NSObject
  
  @property int errorCode, sockfd;
  
  -(id)initWithAddress:(NSString *)addr andPort:(int)port;
  -(ssize_t) writtenToSocket:(int)sockfdNum withChar:(NSString *)vptr;
  -(NSString *) recvFromSocket:(int)lsockfd withMaxChar:(int)max;
  -(ssize_t)sendData:(NSData *)data toSocket:(int)lsockfd;
  
  @end

Updating the BSDSocketClient implementation file

We now need to add the sendData:toSocket: method to our BSDSocketClient class:

-(ssize_t)sendData:(NSData *)data toSocket:(int)lsockfd
 {
     NSLog(@"sending");
     ssize_t n;
     const UInt8 *buf = (const UInt8 *)[data bytes];
 
     if ((n = send(lsockfd, buf,[data length],0)) <=0) {
         errorCode = WRITEERROR;
         return -1;
     } else {
         errorCode = NOERRROR;
         return n;
     }
 }

The sendData:toSocket: method accepts two parameters: the data to send to the server and the socket descriptor to which we want to send the data. Since the BSD Socket Library does not recognize the NSData objects, we will need to convert the data to bytes and then to a UInt8 buffer prior to sending it.

Once we have our UInt8 buffer, we use the send() function to send the data to the server. The send() function will return the number of bytes sent to the server; if that number is less than 0, it means there is a problem and we return an error.

Using the BSDSocketClient to connect to our data server

Let's take a look at the sample code that uses the sendData:toSocket method:

  BSDSocketClient *bsdCli = [[BSDSocketClient alloc] initWithAddress:@"127.0.0.1" andPort:2006];
  if (bsdCli.errorCode == NOERRROR) {
      NSData *data = [NSData dataWithContentsOfFile:@"/Users/hoffmanjon/Documents/GreenGuyLarge.png"];
      [bsdCli sendData:data toSocket:bsdCli.sockfd];
  } else {
      NSLog(@"%@",[NSString stringWithFormat:@"Error code %d recieved. ", bsdCli.errorCode]);
  }

We start off by initializing the BSDSocketClient object with the IP address 127.0.0.1 and with a port of 2006. If you are running the sample server on another device, you will need to change the IP address. If there are no issues initializing the client, we load an image and convert it to an NSData object. You will need to change the location of the file to the location on your machine that contains an image.

We then pass the NSData object that contains the image to the sendData:toSocket method.

How it works…

When we created the BSD data server, we went through a three-step process to prepare the TCP server and to create listen on the socket. These were socket (create a socket), bind (bind the socket to the interface), and listen (listen for incoming connections).

When we create the BSD data client, we make the connection in a two-step process. These steps are socket (create a socket just like the echo server) and connect (this connects to the server). The client calls the connect() function to establish a connection with the server. If no error occurs, we have a connection between the server and the client. This connection process is the same code we used to establish a connection with the echo server.

Once we have the connection established with the server, we need to send our data to the server. In our example, we will be sending an image file over; however, this same code can be used to send any binary file. The client and the server just need to agree on what type of file is to be sent.

The first thing we need to do is to convert the file to an NSData object and pass that to the sendData:toScoket: method. When the sendData:toSocket: method has the NSData object, it converts it to a Uint8 buffer. We then use the send() function to send the Uint8 buffer to the server.