Reputation: 76
i have successfully implemented a tcp server client setup from android to ios Android being the tcp server and ios being the client i handle the client from the tcp server but am facing problems doing the same with ios as the server side and android the client The clent android connects to the ios server but i do not know how to send messages from the ios server side to the android client once the android device is connected any help appreciated
shown below is the code for establishing a tcp server
#import "TCPServer.h"
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <CFNetwork/CFSocketStream.h>
NSString * const TCPServerErrorDomain = @"TCPServerErrorDomain";
@interface TCPServer ()
@property(assign) uint16_t port;
@property (nonatomic, strong, readwrite) NSOutputStream * outputStream;
@end
@implementation TCPServer
@synthesize delegate=_delegate, port=_port;
- (id)init {
return self;
}
- (void)dealloc {
[self stop];
[super dealloc];
}
- (void)handleNewConnectionFromAddress:(NSData *)addr inputStream:(NSInputStream *)istr outputStream:(NSOutputStream *)ostr {
self.outputStream = ostr;
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Port"
message:@"Received Connection"
delegate:self
cancelButtonTitle:@"Ok"
otherButtonTitles:nil, nil];
[alertView performSelectorOnMainThread:@selector(show) withObject:nil waitUntilDone:YES];
if (self.delegate && [self.delegate respondsToSelector:@selector(didAcceptConnectionForServer:inputStream:outputStream:)])
{
[self.delegate didAcceptConnectionForServer:self inputStream:istr outputStream:ostr];
}
}
// This function is called by CFSocket when a new connection comes in.
// We gather some data here, and convert the function call to a method
// invocation on TCPServer.
static void TCPServerAcceptCallBack(CFSocketRef socket, CFSocketCallBackType type, CFDataRef address, const void *data, void *info)
{
TCPServer *server = (TCPServer *)info;
if (kCFSocketAcceptCallBack == type) {
// for an AcceptCallBack, the data parameter is a pointer to a CFSocketNativeHandle
CFSocketNativeHandle nativeSocketHandle = *(CFSocketNativeHandle *)data;
uint8_t name[SOCK_MAXADDRLEN];
socklen_t namelen = sizeof(name);
NSData *peer = nil;
if (0 == getpeername(nativeSocketHandle, (struct sockaddr *)name, &namelen)) {
peer = [NSData dataWithBytes:name length:namelen];
}
CFReadStreamRef readStream = NULL;
CFWriteStreamRef writeStream = NULL;
CFStreamCreatePairWithSocket(kCFAllocatorDefault, nativeSocketHandle, &readStream, &writeStream);
if (readStream && writeStream) {
CFReadStreamSetProperty(readStream, kCFStreamPropertyShouldCloseNativeSocket,
kCFBooleanTrue);
CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);
// NSData *data = [[NSData alloc] initWithData:[@"next" dataUsingEncoding:NSASCIIStringEncoding]];
// [(NSOutputStream *)writeStream write:[data bytes] maxLength:[data length]];
[server handleNewConnectionFromAddress:peer inputStream:(NSInputStream *)readStream outputStream:(NSOutputStream *)writeStream];
}
else {
// on any failure, need to destroy the CFSocketNativeHandle
// since we are not going to use it any more
close(nativeSocketHandle);
}
if (readStream) CFRelease(readStream);
if (writeStream) CFRelease(writeStream);
}
}
- (BOOL)start:(NSError **)error {
NSLog(@"Called start");
CFSocketContext socketCtxt = {0, self, NULL, NULL, NULL};
// Start by trying to do everything with IPv6. This will work for both IPv4 and IPv6 clients
// via the miracle of mapped IPv4 addresses.
witap_socket = CFSocketCreate(kCFAllocatorDefault, PF_INET6, SOCK_STREAM, IPPROTO_TCP, kCFSocketAcceptCallBack, (CFSocketCallBack)&TCPServerAcceptCallBack, &socketCtxt);
if (witap_socket != NULL) // the socket was created successfully
protocolFamily = PF_INET6;
else { // there was an error creating the IPv6 socket - could be running under iOS 3.x
witap_socket = CFSocketCreate(kCFAllocatorDefault, PF_INET, SOCK_STREAM, IPPROTO_TCP, kCFSocketAcceptCallBack, (CFSocketCallBack)&TCPServerAcceptCallBack, &socketCtxt);
if (witap_socket != NULL)
protocolFamily = PF_INET;
}
if (NULL == witap_socket) {
if (error) *error = [[NSError alloc] initWithDomain:TCPServerErrorDomain code:kTCPServerNoSocketsAvailable userInfo:nil];
if (witap_socket) CFRelease(witap_socket);
witap_socket = NULL;
return NO;
}
int yes = 1;
setsockopt(CFSocketGetNative(witap_socket), SOL_SOCKET, SO_REUSEADDR, (void *)&yes, sizeof(yes));
// set up the IP endpoint; use port 0, so the kernel will choose an arbitrary port for us, which will be advertised using Bonjour
if (protocolFamily == PF_INET6) {
struct sockaddr_in6 addr6;
memset(&addr6, 0, sizeof(addr6));
addr6.sin6_len = sizeof(addr6);
addr6.sin6_family = AF_INET6;
addr6.sin6_port = 0;
addr6.sin6_flowinfo = 0;
addr6.sin6_addr = in6addr_any;
NSData *address6 = [NSData dataWithBytes:&addr6 length:sizeof(addr6)];
if (kCFSocketSuccess != CFSocketSetAddress(witap_socket, (CFDataRef)address6)) {
if (error) *error = [[NSError alloc] initWithDomain:TCPServerErrorDomain code:kTCPServerCouldNotBindToIPv6Address userInfo:nil];
if (witap_socket) CFRelease(witap_socket);
witap_socket = NULL;
return NO;
}
// now that the binding was successful, we get the port number
// -- we will need it for the NSNetService
NSData *addr = [(NSData *)CFSocketCopyAddress(witap_socket) autorelease];
memcpy(&addr6, [addr bytes], [addr length]);
self.port = ntohs(addr6.sin6_port);
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Port"
message:[NSString stringWithFormat:@"Port %hu",self.port]
delegate:self
cancelButtonTitle:@"Ok"
otherButtonTitles:nil, nil];
[alertView performSelectorOnMainThread:@selector(show) withObject:nil waitUntilDone:YES];
} else {
struct sockaddr_in addr4;
memset(&addr4, 0, sizeof(addr4));
addr4.sin_len = sizeof(addr4);
addr4.sin_family = AF_INET;
addr4.sin_port = 0;
addr4.sin_addr.s_addr = htonl(INADDR_ANY);
NSData *address4 = [NSData dataWithBytes:&addr4 length:sizeof(addr4)];
if (kCFSocketSuccess != CFSocketSetAddress(witap_socket, (CFDataRef)address4)) {
if (error) *error = [[NSError alloc] initWithDomain:TCPServerErrorDomain code:kTCPServerCouldNotBindToIPv4Address userInfo:nil];
if (witap_socket) CFRelease(witap_socket);
witap_socket = NULL;
return NO;
}
// now that the binding was successful, we get the port number
// -- we will need it for the NSNetService
NSData *addr = [(NSData *)CFSocketCopyAddress(witap_socket) autorelease];
memcpy(&addr4, [addr bytes], [addr length]);
self.port = ntohs(addr4.sin_port);
}
// set up the run loop sources for the sockets
CFRunLoopRef cfrl = CFRunLoopGetCurrent();
CFRunLoopSourceRef source = CFSocketCreateRunLoopSource(kCFAllocatorDefault,witap_socket, 0);
CFRunLoopAddSource(cfrl, source, kCFRunLoopCommonModes);
CFRelease(source);
return YES;
}
- (BOOL)stop {
if (witap_socket) {
CFSocketInvalidate(witap_socket);
CFRelease(witap_socket);
witap_socket = NULL;
}
return YES;
}
-(void)SendMessage:(NSString *)message
{
NSData *data = [[NSData alloc] initWithData:[message dataUsingEncoding:NSASCIIStringEncoding]];
[self.outputStream write:[data bytes] maxLength:[data length]];
}
@end
The question is once i am connected to the server using this code
- (void) initNetworkCommunication {
CFReadStreamRef readStream;
CFWriteStreamRef writeStream;
CFStreamCreatePairWithSocketToHost(NULL, (CFStringRef)@"192.168.1.44",60189, &readStream, &writeStream);
inputStream = (NSInputStream *)readStream;
outputStream = (NSOutputStream *)writeStream;
[inputStream setDelegate:self];
[outputStream setDelegate:self];
[inputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[inputStream open];
[outputStream open];
}
How do i send a message from the server to the client?
Upvotes: 0
Views: 2571
Reputation: 76
Guys i got it working just have to append a new line character after the text i want to send example
NSString *messageToSend = @"message\n";
Upvotes: 1
Reputation: 76
I was able to send messages after i opened the output stream (NSOutputStream) the only problem i am facing is android receives messages only when i close my output stream can any one tell me where i am going wrong
the client code for android is as follows
package com.example.remoteapp;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import android.util.Log;
public class TCPClient {
private String serverMessage;
/**
* Specify the Server Ip Address here. Whereas our Socket Server is started.
* */
//public static final String SERVERIP = "192.168.43.1"; // fixed IP address for hotspot
public static final String SERVERIP = "192.168.1.44"; //computer ip address
//public static final int SERVERPORT = 4321;
public static final int SERVERPORT = 57917;
private OnMessageReceived mMessageListener = null;
private boolean mRun = false;
private PrintWriter out = null;
private BufferedReader in = null;
/**
* Constructor of the class. OnMessagedReceived listens for the messages received from server
*/
public TCPClient(final OnMessageReceived listener)
{
mMessageListener = listener;
}
/**
* Sends the message entered by client to the server
* @param message text entered by client
*/
public void sendMessage(String message){
if (out != null && !out.checkError()) {
System.out.println("message: "+ message);
// out.println(message);
// out.flush();
}
}
public void stopClient(){
mRun = false;
}
public void run() {
mRun = true;
try {
//here you must put your computer's IP address.
InetAddress serverAddr = InetAddress.getByName(SERVERIP);
Log.e("TCP SI Client", "SI: Connecting...");
//create a socket to make the connection with the server
Socket socket = new Socket(serverAddr, SERVERPORT);
try {
//send the message to the server
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
Log.e("TCP SI Client", "SI: Sent.");
Log.e("TCP SI Client", "SI: Done.");
sendMessage("initial message");
//receive the message which the server sends back
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//in this while the client listens for the messages sent by the server
while (mRun) {
serverMessage = in.readLine();
if (serverMessage != null && mMessageListener != null) {
//call the method messageReceived from MyActivity class
mMessageListener.messageReceived(serverMessage);
Log.e("RESPONSE FROM SERVER", "S: Received Message: '" + serverMessage + "'");
}
serverMessage = null;
}
}
catch (Exception e)
{
Log.e("TCP SI Error", "SI: Error", e);
e.printStackTrace();
}
finally
{
//the socket must be closed. It is not possible to reconnect to this socket
// after it is closed, which means a new socket instance has to be created.
socket.close();
}
} catch (Exception e) {
Log.e("TCP SI Error", "SI: Error", e);
}
}
//Declare the interface. The method messageReceived(String message) will must be implemented in the MyActivity
//class at on asynckTask doInBackground
public interface OnMessageReceived {
public void messageReceived(String message);
}
}
Upvotes: 1