Receive an image on the iPhone (TCP Client) - iphone

I'm programming a client on the iPhone. I want to send some strings, and receive an image from the server.
I found this tutorial (http://www.devx.com/wireless/Article/43551), it has been very useful. It works if I want to receive strings, but now I want to receive an image, and I can't make it work.
This is my code when the iPhone receives data. It's a mix made with the tutorial and this answer from JeremyP (http://stackoverflow.com/questions/4613218):
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
switch(eventCode) {
case NSStreamEventHasBytesAvailable: {
if (data == nil) {
data = [[NSMutableData alloc] init];
}
uint8_t buf[102400];
unsigned int len = 0;
len = [(NSInputStream *)stream read:buf maxLength:102400];
if(len) {
[data appendBytes:(const void *)buf length:len];
// Recibir img
uint8_t size; // Let's make sure size is an explicit width.
NSInteger totalBytesRead = 0;
NSInteger bytesRead = [iStream read: &size maxLength: sizeof size];
while (bytesRead > 0 && totalBytesRead + bytesRead < sizeof size) {
totalBytesRead+= bytesRead;
bytesRead = [iStream read: &size + totalBytesRead maxLength: (sizeof size) - totalBytesRead];
}
if (bytesRead >= 0) {
totalBytesRead += bytesRead;
}
else {
// read failure, report error and bail
}
if (totalBytesRead < sizeof size) {
// connection closed before we got the whole size, report and bail
}
size = ntohl(size); // assume wire protocol uses network byte ordering
NSMutableData* buffer = [[NSMutableData alloc] initWithLength: size];
totalBytesRead = 0;
bytesRead = [iStream read: [buffer mutableBytes] maxLength: size];
while (bytesRead > 0 && totalBytesRead + bytesRead < size) {
totalBytesRead+= bytesRead;
bytesRead = [iStream read: [buffer mutableBytes] + totalBytesRead maxLength: size - totalBytesRead];
}
if (bytesRead >= 0) {
totalBytesRead += bytesRead;
}
else {
// read failure, report error and bail (not forgetting to release buffer)
}
if (totalBytesRead < size) {
// connection closed before we got the whole image, report and bail (not forgetting to release buffer)
}
else {
[buffer setLength: size];
}
imgResultado.image = [UIImage imageWithData: buffer];
[buffer release];
[data release];
data = nil;
}
else {
NSLog(#"No data.");
}
} break;
}}
It doesn't work... could you help me?

Well, I made it work this way, but I'm no sure if there's a better way.
- (void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
switch(eventCode) {
case NSStreamEventHasBytesAvailable:
{
uint32_t max_size = 1000000; // Max size of the received imaged.
NSMutableData* buffer = [[NSMutableData alloc] initWithLength: max_size];
NSInteger totalBytesRead = 0;
NSInteger bytesRead = [(NSInputStream *)stream read: [buffer mutableBytes] maxLength: max_size];
if (bytesRead != 0) {
while (bytesRead > 0 && totalBytesRead + bytesRead < max_size) {
totalBytesRead+= bytesRead;
bytesRead = [(NSInputStream *)stream read: [buffer mutableBytes] + totalBytesRead maxLength: max_size - totalBytesRead];
}
if (bytesRead >= 0) {
totalBytesRead += bytesRead;
}
else {
// read failure, report error and bail (not forgetting to release buffer)
}
[buffer setLength: totalBytesRead];
imgResultado.image = [UIImage imageWithData: buffer];
[buffer release];
}
} break;
}}
With this method, the received image must be smaller than max_size.
If you know a better way to do this, please let me know! ;)

If all you want to do is download an image from the server to display, take a look at this:
https://github.com/michaelkamprath/iPhoneMK/tree/master/Views/MKNetworkImageView
There are a lot of built in functions to the iOS that will handle image downloading for you. That is, if you can encapsulate the entities you want to download as separate URLs (e.g., a web server).
If, however, you are trying to send and receive data across the same TCP connection, I would strongly advise that you change to a more RESTful approach. Partly because it is easier to implement from the client side, partly because you don't have to worry (as much) about the underlying TCP connection.

Related

ZLib in iPhone unable to decompress data

I am trying to decompress data using the ZLib in iPhone, but it always through error of "Invalid header Check".
To compress the data I am using the following in Java
Implementation: Standard Java implementation for Zlib
Deflator : java.util.zip.Deflater
version 1.45, 04/07/06
Compression level: BEST_COMPRESSION
In iPhone the following is the code for decompressing:
- (NSData *)zlibInflate
{
if ([self length] == 0) return self;
unsigned full_length = [self length];
unsigned half_length = [self length] / 2;
NSMutableData *decompressed = [NSMutableData dataWithLength: full_length + half_length];
BOOL done = NO;
int status;
z_stream strm;
strm.next_in = (Bytef *)[self bytes];
strm.avail_in = [self length];
strm.total_out = 0;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
if (inflateInit (&strm) != Z_OK) return nil;
while (!done)
{
// Make sure we have enough room and reset the lengths.
if (strm.total_out >= [decompressed length])
[decompressed increaseLengthBy: half_length];
strm.next_out = [decompressed mutableBytes] + strm.total_out;
strm.avail_out = [decompressed length] - strm.total_out;
// Inflate another chunk.
status = inflate (&strm, Z_SYNC_FLUSH);
if (status == Z_STREAM_END) done = YES;
else if (status != Z_OK) {
NSLog(#"%s", strm.msg);
break;
}
}
if (inflateEnd (&strm) != Z_OK) return nil;
// Set real length.
if (done)
{
[decompressed setLength: strm.total_out];
return [NSData dataWithData: decompressed];
}
else return nil;
}
Following is a sample compressed string:
xÚÝUko²Jþ~?­ó?¥¾?¤?©?´ÚjCMX,Òµ?ª?µßVX¹È­?¿.øë_?¯¶ZÏ%íùxHH&Ã<ÏÌ3ÌÎ
#2.ðE?ºqþpéEzÏ09IoÒ?ª? ?®?£àÌönì$brÛ#fl95?¿»a//Tçáò?¢?¿½
µ©ÊÃÉPÔ¼:8y¦ý.äÎ?µ?¥?¼y?©ã¯9ö?¥½?¢±ÝûwÛ?§ãga?©á8?¨?­m\Õ?»6,'Îe?¬}(L}7ÆÅ6#gJ(¥7´s?¬d.ó,Ë°¦prßýÕÖ? 
Below is the function for compresser:
public static byte[] compress(String s) {
Deflater comp = new Deflater();
//comp.setLevel(Deflater.BEST_COMPRESSION);
comp.setInput(s.getBytes());
comp.finish();
ByteArrayOutputStream bos = new ByteArrayOutputStream(s.length());
// Compress the data
byte[] buf = new byte[1024];
try {
while (!comp.finished()) {
int count = comp.deflate(buf);
bos.write(buf, 0, count);
}
bos.close();
} catch (Exception e) {
//Log.d(TAG, e.getMessage());
e.printStackTrace();
}
// Get the compressed data
byte[] compressedData = bos.toByteArray();
// put in this fix for Symbol scanners
byte[] compressedDataForSymbol = mungeForSymbol(compressedData);
/*
* byte[] decompressedDataForSymbol =
* decompressedDataAfterSymbol(compressedDataForSymbol); // check they
* are the same for(int i=0;i<compressedData.length;i++) { if
* (compressedData[i] != decompressedDataForSymbol[i]) {
* //System.out.println("Error at " + i); } }
*/
return compressedDataForSymbol;
// return s.getBytes();
}
Using Java Deflater with default compression level creates output encoded data with header with first two bytes 0x78 0x9c. These are not used in IOS. Just remove the first two bytes and try the Inflate ie decompression in IOS. It should work.
I had faced the same issue, But i wanted data from IOS(compressed) to android(decompressed).

Can't receive jpeg image from server

I am trying to receive a jpeg image from my c# server. The weird thing is when I run it with the debugger and have a break point anywhere in the method it works perfectly fine. Without a breakpoint then I get this error
Corrupt JPEG data: premature end of data segment
Here is my code
(void)stream:(NSStream *)stream handleEvent:(NSStreamEvent)eventCode {
NSMutableData *data;
data = [NSMutableData new];
switch(eventCode) {
case NSStreamEventHasBytesAvailable:
{
uint8_t buffer[1024];
int len;
while([inputStream hasBytesAvailable]) {
len = [inputStream read:buffer maxLength:sizeof(buffer)];
if (len > 0)
{
[data appendBytes:(const void*)buffer length:sizeof(buffer)];
}
}
UIImage *images = [[UIImage alloc]initWithData:data];
[dvdCover setImage:images];
} break;
case NSStreamEventEndEncountered:
{
//UIImage *images = [[UIImage alloc]initWithData:data];
//[dvdCover setImage:images];
} break;
}
}
hi you can check this code hop it will help you...
case NSStreamEventHasBytesAvailable:
{
uint32_t max_size = 1000000; // Max size of the received imaged you can modify it as your reqirement.
NSMutableData* buffer = [[NSMutableData alloc] initWithLength: max_size];
NSInteger totalBytesRead = 0;
NSInteger bytesRead = [(NSInputStream *)stream read: [buffer mutableBytes] maxLength: max_size];
if (bytesRead != 0) {
while (bytesRead > 0 && totalBytesRead + bytesRead < max_size) {
totalBytesRead+= bytesRead;
bytesRead = [(NSInputStream *)stream read: [buffer mutableBytes] + totalBytesRead maxLength: max_size - totalBytesRead];
}
if (bytesRead >= 0) {
totalBytesRead += bytesRead;
}
else {
// read failure, report error and bail (not forgetting to release buffer)
}
[buffer setLength: totalBytesRead];
yourImageName.image = [UIImage imageWithData: buffer];
[buffer release];
} break;
It seems you are assuming the whole JPEG image will be transferred in one chunk, and you can just read it with one occurence of 'HasBytesAvailable' event. However you should also consider the case where the JPEG image is transferred to you in multiple chunks.
It might work for you if you set breakpoint, because your code execution might be halted somewhere, and your network buffer had plenty time to receive all bytes of the image. But without breakpoints it might not have time to do so.
Try refactoring your code to accumulate the bytes chunk, and only assume it's done when all bytes have been transferred. (Normally you have to know beforehand how many bytes the image is going to be -- or you can just capture the end of stream event)

Unable to write data into NSOutputStream

I am trying to send large sized images from the server to client using the write method of the NSOuptutStream. However I find that after writing 684032 bytes, no more bytes are getting written by the stream. I find this particularly strange. I am able to send images of size < 680432 and receive them properly at the client side. Attached code snippet below. It would be great if someone can point out where exactly I am going wrong.
UIImage *img;
img = [UIImage imageWithContentsOfFile:path];
NSMutableData *imgData = [UIImageJPEGRepresentation(img, 1.0) mutableCopy];
const void *bytes = [imgData mutableBytes];
NSInteger length = [imgData length];
self.bufferLimit = length;
NSLog(#"self.bufferoffset %d self.bufferlimit %d", self.bufferOffset, self.bufferLimit);
uint8_t *crypto_data = (uint8_t *)bytes;
NSInteger bytesWritten;
if([self.outputStream hasSpaceAvailable]) {
NSLog(#"Space Available");
} else NSLog(#"Space Not Available");
while (self.bufferOffset < self.bufferLimit) {
bytesWritten = [self.outputStream write:(uint8_t *)crypto_data maxLength:1024];
NSLog(#"server: bytes weritten %d",bytesWritten);
assert(bytesWritten != 0);
if (bytesWritten == -1) {
[self stopSendWithStatus:#"output buffer write error"];
} else {
self.bufferOffset += bytesWritten;
}
NSLog(#"self.bufferoffset %d self.bufferlimit %d", self.bufferOffset, self.bufferLimit);
}
NSLog(#"Done sending");
[self.outputStream close];
When my code hangs this is the output I am left with
...
2012-07-04 13:53:20.903 BasicClientServer[1707:f803] self.bufferoffset 681984 self.bufferlimit 1334301
2012-07-04 13:53:20.903 BasicClientServer[1707:f803] server: bytes weritten 1024
2012-07-04 13:53:20.904 BasicClientServer[1707:f803] self.bufferoffset 683008 self.bufferlimit 1334301
2012-07-04 13:53:20.905 BasicClientServer[1707:f803] server: bytes weritten 1024
2012-07-04 13:53:20.906 BasicClientServer[1707:f803] self.bufferoffset 684032 self.bufferlimit 1334301
and doesnt go beyond this point

How to send integer through NSOutputStream?

So I'm making an iOS app that constantly sends and array of points through NSStreams, but because sometimes the sender writes two arrays before the receiver receives one, I decided to first write the length, then the array data itself, so that the receiver knows how many bytes to process.
sender:
if (_outStream && [_outStream hasSpaceAvailable]){
const uint8_t *buffer = [data bytes];
uint64_t length = CFSwapInt64HostToLittle([data length]);
NSLog(#"Sending bytes with length: %llu", length);
int er1 = [_outStream write:(const uint8_t *)&length maxLength:sizeof(length)];
int er2 = [_outStream write:buffer maxLength:length];
if (er1 < 0 || er2 < 0) {
[self _showAlert:#"Failed sending data to peer"];
}
}
receiver:
case NSStreamEventHasBytesAvailable:
{
if (stream == _inStream) {
uint8_t *b;
int len = 0;
len = [_inStream read:b maxLength:8];
uint64_t dataLength = CFSwapInt64LittleToHost(*b);
NSLog(#"Received bytes with length: %llu", dataLength);
if(len < 0) {
if ([stream streamStatus] != NSStreamStatusAtEnd)
[self _showAlert:#"Failed reading data from peer"];
} else if (len > 0){
uint8_t bytes[dataLength];
int length = [_inStream read:bytes maxLength:dataLength];
[currentDownload appendBytes:bytes length:length];
id pointsArray = [NSKeyedUnarchiver unarchiveObjectWithData:currentDownload];
[currentDownload release];
currentDownload = [[NSMutableData alloc] init];
if ([pointsArray isKindOfClass:[NSArray class]]) {
[drawScene addNewPoint:[[pointsArray objectAtIndex:0] CGPointValue] previousPoint:[[pointsArray objectAtIndex:1] CGPointValue]];
}
}
}
break;
}
The problem is that the receiver receives an incorrect integer, hence it reads an incorrect amount of bytes.
Can anyone help me with this?
Could it be that when you read the data, you're starting with the introductory bits intended to convey length, rather than starting with the actual data?
For:
[currentDownload appendBytes:bytes length:length];
try substituting something like this:
NSRange rangeLeftover = NSMakeRange(sizeof(uint8_t), [currentDownload length] - sizeof(uint8_t));
NSData *dataLeftover = [currentDownload subdataWithRange:rangeLeftover];
uint8_t bytesLeftover[[dataLeftover length]];
[dataLeftover getBytes:&bytesLeftover length:[dataLeftover length]];
currentDownload = [NSMutableData data]; // clear
[currentDownload appendBytes:bytesLeftover length:[dataLeftover length]];

In iPhone programming, How can i receive image file with TCP/IP from server which has XP os?

I am making image receiver with iPhone.
The server send image file with byte.
and, i tried to make this bytes to image.
But, i can not make image. already, several days past...
i really need your help...
this is source code that i use.
Byte recvBuffer[500];
memset(recvBuffer, '\0', sizeof(recvBuffer));
[iStream read:recvBuffer maxLength:sizeof(recvBuffer)-1];
NSUInteger len = sizeof(recvBuffer);
NSData *webdata = [NSData dataWithBytes:recvBuffer length:len];
imageview.image = [UIImage imageWithData:webdata];
if you give a comment, i will really appriciate! thank you for reading!
badgerr's answer is substantially the one I would use, but since the question is tagged Objective-C the code should really be in Objective-C - omething like the following. Note that over a TCP/IP connection, -read:maxLength: may return before the requested number of bytes has been read.
uint32_t size; // Let's make sure size is an explicit width.
NSInteger totalBytesRead = 0;
NSInteger bytesRead = [istream read: &size maxLength: sizeof size];
while (bytesRead > 0 && totalBytesRead + bytesRead < sizeof size)
{
totalBytes+= bytesRead;
bytesRead = [istream read: &size + totalBytesRead maxLength: (sizeof size) - totalBytesRead];
}
if (bytesRead >= 0)
{
totalBytesRead += bytesRead;
}
else
{
// read failure, report error and bail
}
if (totalBytesRead < sizeof size)
{
// connection closed before we got the whole size, report and bail
}
size = ntohl(size); // assume wire protocol uses network byte ordering
NSMutableData* buffer = [[NSMutableData alloc] initWithLength: size];
totalBytesRead = 0;
bytesRead = [istream read: [buffer mutableBytes] maxLength: size];
while (bytesRead > 0 && totalBytesRead + bytesRead < size)
{
totalBytes+= bytesRead;
bytesRead = [istream read: (char*)[buffer mutableBytes] + totalBytesRead maxLength: size - totalBytesRead];
}
if (bytesRead >= 0)
{
totalBytesRead += bytesRead;
}
else
{
// read failure, report error and bail (not forgetting to release buffer)
}
if (totalBytesRead < size)
{
// connection closed before we got the whole image, report and bail (not forgetting to release buffer)
}
else
{
[buffer setLength: size];
}
imageView.image = [UIImage imageWithData: buffer];
[buffer release];
I typed the above straight in to SO, so it is not tested or even compiled.
TCP/IP is a protocol which is OS independent, so the fact that you're using XP on the server should not affect anything, aside from potentially using the windows API for socket operations.
Claus Broch is correct in saying that 499 bytes looks a bit small for an image. What I suggest you do instead is first find the total size of the image (in bytes) on the server, then send that value at the beginning of the TCP stream, followed by the image data. On the receiving end, your code should first read this size value, allocate some memory of that size, then read that many bytes into it. You haven't posted your server code, so I can't help with that, but the client could look a little something like:
unsigned int size = 0; //assumes the size is sent as an int
Byte* recvBuffer;
[iStream read:&size maxLength:sizeof(unsigned int)];
recvBuffer = new Byte[size];
memset(recvBuffer, 0, size);
[iStream read:recvBuffer maxLength:size];
NSData *webdata = [NSData dataWithBytes:recvBuffer length:size];
imageview.image = [UIImage imageWithData:webdata];
delete [] recvBuffer;
Edit My inner C++ made me use new and delete. While you can use this in Obj-C using the .mm file suffix, this question is tagged Objective-C. You should use JeremyP's answer, as it also includes error checking and a more reliable way of reading the complete image.