Added implementation for android and wp8 platforms

This commit is contained in:
Martin Obrátil 2014-09-26 15:21:24 +02:00
parent 84b16cced7
commit 988ebdeb41
15 changed files with 1213 additions and 0 deletions

66
plugin.xml Normal file
View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0" id="cz.blocshop.socketsforcordova" version="0.1.0">
<name>SocketsForCordova</name>
<description>Cordova plugin for socket communication</description>
<license>Apache 2.0</license>
<keywords>cordova,sockets,socket</keywords>
<js-module src="socket.js" name="Socket">
<clobbers target="window.Socket" />
</js-module>
<!-- android -->
<platform name="android">
<config-file target="res/xml/config.xml" parent="/*">
<feature name="SocketsForCordova">
<param name="android-package" value="cz.blocshop.socketsforcordova.SocketPlugin"/>
</feature>
</config-file>
<config-file target="AndroidManifest.xml" parent="/manifest">
<uses-permission android:name="android.permission.INTERNET" />
</config-file>
<source-file src="src/android/src/cz/blocshop/socketsforcordova/Logging.java" target-dir="src/cz/blocshop/socketsforcordova" />
<source-file src="src/android/src/cz/blocshop/socketsforcordova/Consumer.java" target-dir="src/cz/blocshop/socketsforcordova" />
<source-file src="src/android/src/cz/blocshop/socketsforcordova/SocketAdapter.java" target-dir="src/cz/blocshop/socketsforcordova" />
<source-file src="src/android/src/cz/blocshop/socketsforcordova/SocketAdapterImpl.java" target-dir="src/cz/blocshop/socketsforcordova" />
<source-file src="src/android/src/cz/blocshop/socketsforcordova/SocketAdapterOptions.java" target-dir="src/cz/blocshop/socketsforcordova" />
<source-file src="src/android/src/cz/blocshop/socketsforcordova/SocketPlugin.java" target-dir="src/cz/blocshop/socketsforcordova" />
</platform>
<!-- ios
<platform name="ios">
<config-file target="config.xml" parent="/*">
<feature name="InAppBrowser">
<param name="ios-package" value="CDVInAppBrowser" />
</feature>
</config-file>
<header-file src="src/ios/CDVInAppBrowser.h" />
<source-file src="src/ios/CDVInAppBrowser.m" />
<framework src="CoreGraphics.framework" />
</platform> -->
<!-- wp8 -->
<platform name="wp8">
<config-file target="config.xml" parent="/*">
<feature name="SocketsForCordova">
<param name="wp-package" value="Blocshop.ScoketsForCordova.SocketPlugin"/>
</feature>
</config-file>
<source-file src="src/wp8/src/SocketAdapter.cs" target-dir="src" />
<source-file src="src/wp8/src/SocketAdapterOptions.cs" target-dir="src" />
<source-file src="src/wp8/src/SocketExtensions.cs" target-dir="src" />
<source-file src="src/wp8/src/SocketPlugin.cs" target-dir="src" />
<source-file src="src/wp8/src/SocketEvent.cs" target-dir="src" />
<source-file src="src/wp8/src/SocketStorage.cs" target-dir="src" />
</platform>
</plugin>

154
socket.js Normal file
View File

@ -0,0 +1,154 @@
var exec = require('cordova/exec');
var SOCKET_EVENT = "SOCKET_EVENT";
var CORDOVA_SERVICE_NAME = "SocketsForCordova";
function Socket() {
this.onData = null;
this.onClose = null;
this.onError = null;
this.socketKey = guid();
}
Socket.create = function(callback) {
var socket = new Socket();
function socketEventHandler(event) {
var payload = event.payload;
if (payload.socketKey !== socket.socketKey) {
return;
}
switch(payload.type) {
case "Close":
console.debug("SocketsForCordova: Close event, socket key: " + payload.socketKey);
window.document.removeEventListener(SOCKET_EVENT, socketEventHandler);
socket.onClose();
break;
case "DataReceived":
console.debug("SocketsForCordova: DataReceived event, socket key: " + payload.socketKey);
socket.onData(new Int8Array(payload.data));
break;
case "Error":
console.debug("SocketsForCordova: Error event, socket key: " + payload.socketKey);
socket.onError(payload.errorMessage);
break;
default:
console.error("SocketsForCordova: Unknown event type " + payload.type + ", socket key: " + payload.socketKey);
break;
}
}
window.document.addEventListener(SOCKET_EVENT, socketEventHandler);
exec(
function() {
console.debug("SocketsForCordova: Socket object successfully constructed.");
callback(socket);
},
function(error) {
console.error("SocketsForCordova: Unexpected error during constructing Socket object. Error: " + error);
},
CORDOVA_SERVICE_NAME,
"create",
[ socket.socketKey ]);
};
Socket.prototype.connect = function (host, port, success, error) {
exec(
function() {
console.debug("SocketsForCordova: Socket successfully connected.");
if (success)
success();
},
function(errorMessage) {
console.error("SocketsForCordova: Error during socket connecting. Error: " + errorMessage);
if (error)
error(errorMessage);
},
CORDOVA_SERVICE_NAME,
"connect",
[ this.socketKey, host, port ]);
};
Socket.prototype.write = function (data, success, error) {
var dataToWrite = data instanceof Int8Array
? Socket._copyToArray(data)
: data;
exec(
function() {
console.debug("SocketsForCordova: Data successfully written to socket. Number of bytes: " + data.length);
if (success)
success();
},
function(errorMessage) {
console.error("SocketsForCordova: Error during writing data to socket. Error: " + errorMessage);
if (error)
error(errorMessage);
},
CORDOVA_SERVICE_NAME,
"write",
[ this.socketKey, dataToWrite ]);
};
Socket._copyToArray = function(array) {
var outputArray = new Array(array.length);
for (var i = 0; i < array.length; i++) {
outputArray[i] = array[i];
}
return outputArray;
};
Socket.prototype.close = function () {
exec(
function() {
console.debug("SocketsForCordova: Close successfully closed.");
},
function(errorMessage) {
console.error("SocketsForCordova: Error when call close on socket. Error: " + errorMessage);
},
CORDOVA_SERVICE_NAME,
"close",
[ this.socketKey ]);
};
Socket.dispatchEvent = function(event) {
var eventReceive = document.createEvent('Events');
eventReceive.initEvent(SOCKET_EVENT, true, true);
eventReceive.payload = event;
document.dispatchEvent(eventReceive);
};
var guid = (function() {
function s4() {
return Math.floor((1 + Math.random()) * 0x10000)
.toString(16)
.substring(1);
}
return function() {
return s4() + s4() + '-' + s4() + '-' + s4() + '-' +
s4() + '-' + s4() + s4() + s4();
};
})();
// Register event dispatcher for Windows Phone
if (navigator.userAgent.match(/iemobile/i)) {
window.document.addEventListener("deviceready", function() {
exec(
Socket.dispatchEvent,
function(errorMessage) {
console.error("SocketsForCordova: Cannot register WP event dispatcher, Error: " + errorMessage);
},
CORDOVA_SERVICE_NAME,
"registerWPEventDispatcher",
[ ]);
});
}
module.exports = Socket;

View File

@ -0,0 +1,5 @@
package cz.blocshop.socketsforcordova;
public interface Consumer<T> {
void accept(T t);
}

View File

@ -0,0 +1,10 @@
package cz.blocshop.socketsforcordova;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Logging {
public static void Error(String klass, String message, Throwable t) {
Logger.getLogger(klass).log(Level.SEVERE, message, t);
}
}

View File

@ -0,0 +1,15 @@
package cz.blocshop.socketsforcordova;
import java.io.IOException;
import java.net.SocketException;
public interface SocketAdapter {
public void connect(String host, int port) throws IOException;
public void write(byte[] data) throws IOException;
public void close() throws IOException;
public void setOptions(SocketAdapterOptions options) throws SocketException;
public void setDataConsumer(Consumer<byte[]> dataConsumer);
public void setCloseEventHandler(Consumer<Boolean> closeEventHandler);
public void setErrorHandler(Consumer<IOException> errorHandler);
}

View File

@ -0,0 +1,142 @@
package cz.blocshop.socketsforcordova;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketException;
import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SocketAdapterImpl implements SocketAdapter {
private final int INPUT_STREAM_BUFFER_SIZE = 16 * 1024;
private final Socket socket;
private Consumer<byte[]> dataConsumer;
private Consumer<Boolean> closeEventHandler;
private Consumer<IOException> exceptionHandler;
public SocketAdapterImpl() {
this.socket = new Socket();
}
@Override
public void connect(String host, int port) throws IOException {
this.socket.connect(new InetSocketAddress(host, port));
this.submitReadTask();
}
@Override
public void write(byte[] data) throws IOException {
this.socket.getOutputStream().write(data);
}
@Override
public void close() throws IOException {
if (!this.socket.isClosed()) {
this.socket.shutdownOutput();
}
}
@Override
public void setOptions(SocketAdapterOptions options) throws SocketException {
if (options.getKeepAlive() != null) {
this.socket.setKeepAlive(options.getKeepAlive());
}
if (options.getOobInline() != null) {
this.socket.setOOBInline(options.getOobInline());
}
if (options.getSoLinger() != null) {
this.socket.setSoLinger(true, options.getSoLinger());
}
if (options.getSoTimeout() != null) {
this.socket.setSoTimeout(options.getSoTimeout());
}
if (options.getReceiveBufferSize() != null) {
this.socket.setReceiveBufferSize(options.getReceiveBufferSize());
}
if (options.getSendBufferSize() != null) {
this.socket.setSendBufferSize(options.getSendBufferSize());
}
if (options.getTrafficClass() != null) {
this.socket.setTrafficClass(options.getTrafficClass());
}
}
@Override
public void setDataConsumer(Consumer<byte[]> dataConsumer) {
this.dataConsumer = dataConsumer;
}
@Override
public void setCloseEventHandler(Consumer<Boolean> closeEventHandler) {
this.closeEventHandler = closeEventHandler;
}
@Override
public void setErrorHandler(Consumer<IOException> exceptionHandler) {
this.exceptionHandler = exceptionHandler;
}
private void submitReadTask() {
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(new Runnable() {
@Override
public void run() {
runRead();
}
});
}
private void runRead() {
boolean hasError = false;
try {
runReadLoop();
} catch (IOException e) {
Logging.Error(SocketAdapterImpl.class.getName(), "Error during reading of socket input stream", e);
hasError = true;
invokeExceptionHandler(e);
} finally {
try {
socket.close();
} catch (IOException e) {
Logging.Error(SocketAdapterImpl.class.getName(), "Error during closing of socket", e);
} finally {
invokeCloseEventHandler(hasError);
}
}
}
private void runReadLoop() throws IOException {
byte[] buffer = new byte[INPUT_STREAM_BUFFER_SIZE];
int bytesRead = 0;
while ((bytesRead = socket.getInputStream().read(buffer)) >= 0) {
byte[] data = buffer.length == bytesRead
? buffer
: Arrays.copyOfRange(buffer, 0, bytesRead);
this.invokeDataConsumer(data);
}
}
private void invokeDataConsumer(byte[] data) {
if (this.dataConsumer != null) {
this.dataConsumer.accept(data);
}
}
private void invokeCloseEventHandler(boolean hasError) {
if (this.closeEventHandler != null) {
this.closeEventHandler.accept(hasError);
}
}
private void invokeExceptionHandler(IOException exception) {
if (this.exceptionHandler != null) {
this.exceptionHandler.accept(exception);
}
}
}

View File

@ -0,0 +1,110 @@
package cz.blocshop.socketsforcordova;
public class SocketAdapterOptions {
private Boolean keepAlive;
private Boolean oobInline;
private Integer soLinger;
private Integer soTimeout;
private Integer receiveBufferSize;
private Integer sendBufferSize;
private Integer trafficClass;
/**
* @return the keepAlive
*/
public Boolean getKeepAlive() {
return keepAlive;
}
/**
* @param keepAlive the keepAlive to set
*/
public void setKeepAlive(Boolean keepAlive) {
this.keepAlive = keepAlive;
}
/**
* @return the oobInline
*/
public Boolean getOobInline() {
return oobInline;
}
/**
* @param oobInline the oobInline to set
*/
public void setOobInline(Boolean oobInline) {
this.oobInline = oobInline;
}
/**
* @return the soLinger
*/
public Integer getSoLinger() {
return soLinger;
}
/**
* @param soLinger the soLinger to set
*/
public void setSoLinger(Integer soLinger) {
this.soLinger = soLinger;
}
/**
* @return the soTimeout
*/
public Integer getSoTimeout() {
return soTimeout;
}
/**
* @param soTimeout the soTimeout to set
*/
public void setSoTimeout(Integer soTimeout) {
this.soTimeout = soTimeout;
}
/**
* @return the receiveBufferSize
*/
public Integer getReceiveBufferSize() {
return receiveBufferSize;
}
/**
* @param receiveBufferSize the receiveBufferSize to set
*/
public void setReceiveBufferSize(Integer receiveBufferSize) {
this.receiveBufferSize = receiveBufferSize;
}
/**
* @return the sendBufferSize
*/
public Integer getSendBufferSize() {
return sendBufferSize;
}
/**
* @param sendBufferSize the sendBufferSize to set
*/
public void setSendBufferSize(Integer sendBufferSize) {
this.sendBufferSize = sendBufferSize;
}
/**
* @return the trafficClass
*/
public Integer getTrafficClass() {
return trafficClass;
}
/**
* @param trafficClass the trafficClass to set
*/
public void setTrafficClass(Integer trafficClass) {
this.trafficClass = trafficClass;
}
}

View File

@ -0,0 +1,206 @@
package cz.blocshop.socketsforcordova;
import android.annotation.SuppressLint;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaArgs;
import org.apache.cordova.CordovaPlugin;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
public class SocketPlugin extends CordovaPlugin {
Map<String, SocketAdapter> socketAdapters = new HashMap<String, SocketAdapter>();
@Override
public boolean execute(String action, CordovaArgs args, CallbackContext callbackContext) throws JSONException {
if(action.equals("create")) {
this.create(args, callbackContext);
} else if (action.equals("connect")) {
this.connect(args, callbackContext);
} else if (action.equals("write")) {
this.write(args, callbackContext);
} else if (action.equals("close")) {
this.close(args, callbackContext);
} else if (action.equals("setOptions")) {
this.setOptions(args, callbackContext);
} else {
callbackContext.error(String.format("SocketPlugin - invalid action:", action));
return false;
}
return true;
}
private void create(CordovaArgs args, CallbackContext callbackContext) throws JSONException {
final String socketKey = args.getString(0);
SocketAdapter socketAdapter = new SocketAdapterImpl();
socketAdapter.setCloseEventHandler(new CloseEventHandler(socketKey));
socketAdapter.setDataConsumer(new DataConsumer(socketKey));
socketAdapter.setErrorHandler(new ErrorHandler(socketKey));
this.socketAdapters.put(socketKey, socketAdapter);
callbackContext.success(socketKey);
}
private void connect(CordovaArgs args, CallbackContext callbackContext) throws JSONException {
String socketKey = args.getString(0);
String host = args.getString(1);
int port = args.getInt(2);
SocketAdapter socket = this.getSocketAdapter(socketKey);
try {
socket.connect(host, port);
callbackContext.success();
} catch (IOException e) {
callbackContext.error(e.toString());
}
}
private void write(CordovaArgs args, CallbackContext callbackContext) throws JSONException {
String socketKey = args.getString(0);
JSONArray data = args.getJSONArray(1);
byte[] dataBuffer = new byte[data.length()];
for(int i = 0; i < dataBuffer.length; i++) {
dataBuffer[i] = (byte) data.getInt(i);
}
SocketAdapter socket = this.getSocketAdapter(socketKey);
try {
socket.write(dataBuffer);
callbackContext.success();
} catch (IOException e) {
callbackContext.error(e.toString());
}
}
private void close(CordovaArgs args, CallbackContext callbackContext) throws JSONException {
String socketKey = args.getString(0);
SocketAdapter socket = this.getSocketAdapter(socketKey);
try {
socket.close();
callbackContext.success();
} catch (IOException e) {
callbackContext.error(e.toString());
}
}
private void setOptions(CordovaArgs args, CallbackContext callbackContext) throws JSONException {
String socketKey = args.getString(0);
JSONObject optionsJSON = args.getJSONObject(1);
SocketAdapter socket = this.getSocketAdapter(socketKey);
SocketAdapterOptions options = new SocketAdapterOptions();
options.setKeepAlive(getBooleanPropertyFromJSON(optionsJSON, "keepAlive"));
options.setOobInline(getBooleanPropertyFromJSON(optionsJSON, "oobInline"));
options.setReceiveBufferSize(getIntegerPropertyFromJSON(optionsJSON, "receiveBufferSize"));
options.setSendBufferSize(getIntegerPropertyFromJSON(optionsJSON, "sendBufferSize"));
options.setSoLinger(getIntegerPropertyFromJSON(optionsJSON, "soLinger"));
options.setSoTimeout(getIntegerPropertyFromJSON(optionsJSON, "soTimeout"));
options.setTrafficClass(getIntegerPropertyFromJSON(optionsJSON, "trafficClass"));
try {
socket.close();
callbackContext.success();
} catch (IOException e) {
callbackContext.error(e.toString());
}
}
private Boolean getBooleanPropertyFromJSON(JSONObject jsonObject, String propertyName) throws JSONException {
return jsonObject.has(propertyName) ? jsonObject.getBoolean(propertyName) : null;
}
private Integer getIntegerPropertyFromJSON(JSONObject jsonObject, String propertyName) throws JSONException {
return jsonObject.has(propertyName) ? jsonObject.getInt(propertyName) : null;
}
private SocketAdapter getSocketAdapter(String socketKey) {
if (!this.socketAdapters.containsKey(socketKey)) {
throw new IllegalArgumentException(
String.format("Cannot find socketKey: %s. Connection is probably closed.", socketKey));
}
return this.socketAdapters.get(socketKey);
}
private void dispatchEvent(JSONObject jsonEventObject) {
this.webView.sendJavascript(String.format("window.Socket.dispatchEvent(%s);", jsonEventObject.toString()));
}
private class CloseEventHandler implements Consumer<Boolean> {
private String socketKey;
public CloseEventHandler(String socketKey) {
this.socketKey = socketKey;
}
@Override
public void accept(Boolean hasError) {
socketAdapters.remove(this.socketKey);
try {
JSONObject event = new JSONObject();
event.put("type", "Close");
event.put("hasError", hasError.booleanValue());
event.put("socketKey", this.socketKey);
dispatchEvent(event);
} catch (JSONException e) {
e.printStackTrace();
}
}
}
private class DataConsumer implements Consumer<byte[]> {
private String socketKey;
public DataConsumer(String socketKey) {
this.socketKey = socketKey;
}
@SuppressLint("NewApi")
@Override
public void accept(byte[] data) {
try {
JSONObject event = new JSONObject();
event.put("type", "DataReceived");
event.put("data", new JSONArray(data));
event.put("socketKey", socketKey);
dispatchEvent(event);
} catch (JSONException e) {
e.printStackTrace();
}
}
}
private class ErrorHandler implements Consumer<IOException> {
private String socketKey;
public ErrorHandler(String socketKey) {
this.socketKey = socketKey;
}
@Override
public void accept(IOException exception) {
try {
JSONObject event = new JSONObject();
event.put("type", "Error");
event.put("errorMessage", exception.toString());
event.put("socketKey", socketKey);
dispatchEvent(event);
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}

View File

@ -0,0 +1,37 @@
/*
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
package cz.blocshop.socketsforcordova;
import android.os.Bundle;
import org.apache.cordova.*;
public class SocketsForCordovaActivity extends CordovaActivity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
super.init();
// Set by <content src="index.html" /> in config.xml
super.loadUrl(Config.getStartUrl());
//super.loadUrl("file:///android_asset/www/index.html");
}
}

View File

@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace Blocshop.ScoketsForCordova
{
public interface ISocketAdapter
{
Task Connect(String host, int port);
Task Write(byte[] data);
void Close();
SocketAdapterOptions Options { set; }
Action<byte[]> DataConsumer { set; }
Action<bool> CloseEventHandler { set; }
Action<Exception> ErrorHandler { set; }
}
public class SocketAdapter : ISocketAdapter
{
private const int InputStreamBufferSize = 16 * 1024;
private readonly Socket socket;
public Action<byte[]> DataConsumer { get; set; }
public Action<bool> CloseEventHandler { get; set; }
public Action<Exception> ErrorHandler { get; set; }
public SocketAdapterOptions Options { get; set; }
public SocketAdapter()
{
this.socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
}
public async Task Connect(string host, int port)
{
var connectSocketAsyncEventArgs = new SocketAsyncEventArgs
{
RemoteEndPoint = new DnsEndPoint(host, port)
};
await this.socket.ConnectTaskAsync(connectSocketAsyncEventArgs);
this.StartReadTask();
}
public async Task Write(byte[] data)
{
var socketAsyncEventArgs = new SocketAsyncEventArgs();
socketAsyncEventArgs.SetBuffer(data, 0, data.Length);
await this.socket.SendTaskAsync(socketAsyncEventArgs);
}
public void Close()
{
this.socket.Shutdown(SocketShutdown.Send);
}
private void StartReadTask()
{
Task.Factory.StartNew(() => this.RunRead());
}
private async Task RunRead()
{
bool hasError = false;
try
{
await this.RunReadLoop();
}
catch (SocketException ex)
{
hasError = true;
this.ErrorHandler(ex);
}
catch (Exception ex)
{
}
finally
{
this.socket.Close();
this.CloseEventHandler(hasError);
}
}
private async Task RunReadLoop()
{
byte[] buffer = new byte[InputStreamBufferSize];
int bytesRead = 0;
do
{
var eventArgs = new SocketAsyncEventArgs();
eventArgs.SetBuffer(buffer, 0, InputStreamBufferSize);
await this.socket.ReceiveTaskAsync(eventArgs);
bytesRead = eventArgs.BytesTransferred;
byte[] data = new byte[bytesRead];
Array.Copy(buffer, data, data.Length);
this.DataConsumer(data);
}
while (bytesRead != 0);
}
}
}

View File

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Blocshop.ScoketsForCordova
{
public class SocketAdapterOptions
{
}
}

View File

@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
namespace Blocshop.ScoketsForCordova
{
[DataContract]
public abstract class SocketEvent
{
[DataMember(Name = "type")]
public abstract string Type { get; set; }
[DataMember(Name = "socketKey")]
public string SocketKey { get; set; }
}
[DataContract]
public class CloseSocketEvent : SocketEvent
{
public override string Type
{
get
{
return "Close";
}
set
{
}
}
[DataMember(Name = "hasError")]
public bool HasError { get; set; }
}
[DataContract]
public class DataReceivedSocketEvent : SocketEvent
{
public override string Type
{
get
{
return "DataReceived";
}
set
{
}
}
[DataMember(Name = "data")]
public byte[] Data { get; set; }
}
[DataContract]
public class ErrorSocketEvent : SocketEvent
{
public override string Type
{
get
{
return "Error";
}
set
{
}
}
[DataMember(Name="errorMessage")]
public string ErrorMessage { get; set; }
}
}

View File

@ -0,0 +1,62 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace Blocshop.ScoketsForCordova
{
public static class SocketExtensions
{
public static async Task ConnectTaskAsync(this Socket socket, SocketAsyncEventArgs socketAsyncEventArgs)
{
var task = CreateTaskFromCompletionHandler(socketAsyncEventArgs, SocketAsyncOperation.Connect);
socket.ConnectAsync(socketAsyncEventArgs);
await task;
}
public static async Task SendTaskAsync(this Socket socket, SocketAsyncEventArgs socketAsyncEventArgs)
{
var task = CreateTaskFromCompletionHandler(socketAsyncEventArgs, SocketAsyncOperation.Send);
socket.SendAsync(socketAsyncEventArgs);
await task;
}
public static async Task<byte[]> ReceiveTaskAsync(this Socket socket, SocketAsyncEventArgs socketAsyncEventArgs)
{
var task = CreateTaskFromCompletionHandler(socketAsyncEventArgs, SocketAsyncOperation.Receive);
socket.ReceiveAsync(socketAsyncEventArgs);
await task;
return socketAsyncEventArgs.Buffer;
}
private static Task CreateTaskFromCompletionHandler(SocketAsyncEventArgs socketAsyncEventArgs, SocketAsyncOperation socketAsyncOperation)
{
TaskCompletionSource<string> completionSource = new TaskCompletionSource<string>();
socketAsyncEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>((o, eventArgs) =>
{
if (eventArgs.LastOperation == socketAsyncOperation)
{
if (eventArgs.SocketError == SocketError.Success)
{
completionSource.SetResult("");
}
else
{
completionSource.SetException(new SocketException((int)eventArgs.SocketError));
}
}
});
return completionSource.Task;
}
}
}

152
src/wp8/src/SocketPlugin.cs Normal file
View File

@ -0,0 +1,152 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using WPCordovaClassLib.Cordova;
using WPCordovaClassLib.Cordova.Commands;
using WPCordovaClassLib.Cordova.JSON;
namespace Blocshop.ScoketsForCordova
{
public class SocketPlugin : BaseCommand
{
private readonly ISocketStorage socketStorage;
private string eventDispatcherCallbackId;
public SocketPlugin()
{
System.Diagnostics.Debug.WriteLine("SocketPlugin constructor: " + DateTime.Now.Ticks);
this.socketStorage = SocketStorage.CreateSocketStorage();
}
public void create(string parameters)
{
string socketKey = JsonHelper.Deserialize<string[]>(parameters)[0];
ISocketAdapter socketAdapter = new SocketAdapter();
socketAdapter.CloseEventHandler = (hasError) => this.CloseEventHandler(socketKey, hasError);
socketAdapter.DataConsumer = (data) => this.DataConsumer(socketKey, data);
socketAdapter.ErrorHandler = (ex) => this.ErrorHandler(socketKey, ex);
this.socketStorage.Add(socketKey, socketAdapter);
this.DispatchCommandResult(new PluginResult(PluginResult.Status.OK));
}
public void registerWPEventDispatcher(string parameters)
{
this.eventDispatcherCallbackId = this.CurrentCommandCallbackId;
PluginResult result = new PluginResult(PluginResult.Status.OK);
result.KeepCallback = true;
DispatchCommandResult(result, this.eventDispatcherCallbackId);
}
public void connect(string parameters)
{
string socketKey = JsonHelper.Deserialize<string[]>(parameters)[0];
string host = JsonHelper.Deserialize<string[]>(parameters)[1];
int port = int.Parse(JsonHelper.Deserialize<string[]>(parameters)[2]);
ISocketAdapter socket = this.socketStorage.Get(socketKey);
try
{
socket.Connect(host, port).Wait();
this.DispatchCommandResult(new PluginResult(PluginResult.Status.OK));
}
catch (SocketException ex)
{
this.DispatchCommandResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, ex.Message));
}
}
public void write(string parameters/*, string socketKey, byte[] data*/)
{
string socketKey = JsonHelper.Deserialize<string[]>(parameters)[0];
string dataJsonArray = JsonHelper.Deserialize<string[]>(parameters)[1];
byte[] data = JsonHelper.Deserialize<byte[]>(dataJsonArray);
ISocketAdapter socket = this.socketStorage.Get(socketKey);
try
{
socket.Write(data).Wait();
this.DispatchCommandResult(new PluginResult(PluginResult.Status.OK));
}
catch (SocketException ex)
{
this.DispatchCommandResult(new PluginResult(PluginResult.Status.IO_EXCEPTION, ex.Message));
}
}
public void close(string parameters)
{
string socketKey = JsonHelper.Deserialize<string[]>(parameters)[0];
ISocketAdapter socket = this.socketStorage.Get(socketKey);
socket.Close();
}
//private void setOptions(CordovaArgs args, CallbackContext callbackContext) throws JSONException {
// String socketKey = args.getString(0);
// JSONObject optionsJSON = args.getJSONObject(1);
// SocketAdapter socket = this.getSkocketAdapter(socketKey);
// SocketAdapterOptions options = new SocketAdapterOptions();
// options.setKeepAlive(getBooleanPropertyFromJSON(optionsJSON, "keepAlive"));
// options.setOobInline(getBooleanPropertyFromJSON(optionsJSON, "oobInline"));
// options.setReceiveBufferSize(getIntegerPropertyFromJSON(optionsJSON, "receiveBufferSize"));
// options.setSendBufferSize(getIntegerPropertyFromJSON(optionsJSON, "sendBufferSize"));
// options.setSoLinger(getIntegerPropertyFromJSON(optionsJSON, "soLinger"));
// options.setSoTimeout(getIntegerPropertyFromJSON(optionsJSON, "soTimeout"));
// options.setTrafficClass(getIntegerPropertyFromJSON(optionsJSON, "trafficClass"));
// try {
// socket.close();
// callbackContext.success();
// } catch (IOException e) {
// callbackContext.error(e.toString());
// }
//}
private void CloseEventHandler(string socketKey, bool hasError)
{
socketStorage.Remove(socketKey);
this.DispatchEvent(new CloseSocketEvent
{
HasError = hasError,
SocketKey = socketKey
});
}
private void DataConsumer(string socketKey, byte[] data)
{
this.DispatchEvent(new DataReceivedSocketEvent
{
Data = data,
SocketKey = socketKey
});
}
private void ErrorHandler(string socketKey, Exception exception)
{
this.DispatchEvent(new ErrorSocketEvent
{
ErrorMessage = exception.Message,
SocketKey = socketKey
});
}
private void DispatchEvent(SocketEvent eventObject)
{
PluginResult result = new PluginResult(PluginResult.Status.OK, JsonHelper.Serialize(eventObject));
result.KeepCallback = true;
DispatchCommandResult(result, this.eventDispatcherCallbackId);
}
}
}

View File

@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Blocshop.ScoketsForCordova
{
public interface ISocketStorage
{
void Add(string socketKey, ISocketAdapter socketAdapter);
ISocketAdapter Get(string socketKey);
void Remove(string socketKey);
}
public class SocketStorage : ISocketStorage
{
private readonly IDictionary<string, ISocketAdapter> socketAdapters = new Dictionary<string, ISocketAdapter>();
private object syncRoot = new object();
public void Add(string socketKey, ISocketAdapter socketAdapter)
{
lock (syncRoot)
{
System.Diagnostics.Debug.WriteLine("Add: " + DateTime.Now.Ticks);
this.socketAdapters.Add(socketKey, socketAdapter);
}
}
public ISocketAdapter Get(string socketKey)
{
lock (syncRoot)
{
System.Diagnostics.Debug.WriteLine("Get: " + DateTime.Now.Ticks);
if (!this.socketAdapters.ContainsKey(socketKey))
{
throw new ArgumentException(
string.Format("Cannot find socketKey: {0}. Connection is probably closed.", socketKey));
}
return this.socketAdapters[socketKey];
}
}
public void Remove(string socketKey)
{
lock (syncRoot)
{
System.Diagnostics.Debug.WriteLine("Remove: " + DateTime.Now.Ticks);
this.socketAdapters.Remove(socketKey);
}
}
public static ISocketStorage CreateSocketStorage()
{
return new SocketStorage();
}
}
}