android - IOS 到 Android,反之亦然 TCP 服务器客户端连接使用套接字

标签 android ios sockets tcp

我已经成功地实现了从 android 到 ios 的 tcp 服务器客户端设置 Android 是 tcp 服务器,而 ios 是客户端 我从 tcp 服务器处理客户端,但是我面临着将 ios 作为服务器端和 android 做同样事情的问题客户端 clent android 连接到 ios 服务器,但我不知道如何在连接 android 设备后将消息从 ios 服务器端发送到 android 客户端任何帮助表示感谢

下面是建立tcp服务器的代码

#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

问题是一旦我使用这段代码连接到服务器

- (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];    
}

如何从服务器向客户端发送消息?

最佳答案

我在打开输出流 (NSOutputStream) 后能够发送消息,我面临的唯一问题是 android 仅在我关闭输出流时才接收消息,任何人都可以告诉我哪里出错了

android的客户端代码如下

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);
    }
}

关于android - IOS 到 Android,反之亦然 TCP 服务器客户端连接使用套接字,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23994591/

相关文章:

javascript - Phonegap、jQueryMobile 和 Web 服务

android - 将 CardView 移动到 NavigationView 下方

android - 使用域更改 SSL reshape Android 应用程序

iphone - 从 URL 读取 JSON 并添加 MKAnnotations

perl - 如何在 Perl 中监听多个套接字?

python - 除了 KeyboardInterrupt : is outside the class 之外,如何使用 sock.close() 正常关闭套接字

android - recyclerview 或 viewpager 上的 setRotationY(180) 在 Android 9(API 28)中创建滚动问题

ios - 是否可以在委托(delegate)方法中使用完成处理程序 - Swift

ios - 使用 AutoLayout 获取 UILabel 宽度很热门吗?

c++ - 接收整个 UDP 数据包