I have been working on a Server (in Java) and client in JavaScript using websockets, and everything works just find in Android, but when I tested it on iPhone, the JavaScript page gets hung up on any websocket.send method.
On Android, the gameButton function runs through, outputting "Send!" on the page, and the server receives the message. On IOS, the gameButton function outputs something like "Input: u", but apparently hangs on connection.send, and does not output "Sent!", and the server does not receive the message. The onopen function is never called on iPhone, but it is on Android. Any ideas? Let me know if you need to see some of the server code..
Sorry for the long code!
Javascript for the client:
<!DOCTYPE html>
<html>
<head>
<title>Echo Test</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
</head>
<body onload="pageLoad()" style="background:black; color:cyan">
<div id="joinform">
<input type="text" id="username" value="Username..."></input>
<button id="joinbutton" onclick="joinSubmit()">Join!</button></div>
<div id="gamecontrols">
<button onclick="gameButton('u')" id="upbutton"> UP </button>
<button onclick="gameButton('d')" id="downbutton">DOWN</button></div>
<div id="leftDiv" style="float:left; width=10%; height=300px"><canvas id="leftCanvas" style="border:2px solid cyan"></canvas> </div>
<div id="rightDiv" style="float:left; width=10%; height=300px"><canvas id="rightCanvas" style="border:2px solid cyan"></canvas> </div>
<div id="statusdiv"> </div>
<div id="messages"></div>
</body>
<script type="text/javascript">
var connection = new WebSocket('ws://174.51.xxx.xx:9091');
var messages = document.getElementById("messages");
var username = document.getElementById("username");
var joinbutton = document.getElementById("joinbutton");
var game = document.getElementById("gamecontrols");
var joinform = document.getElementById("joinform");
var state = 0;
var leftCanvas = document.getElementById('leftCanvas')
var rightCanvas = document.getElementById('rightCanvas');
var statusdiv = document.getElementById('statusdiv')
var startx = 0
var starty = 0;
var distx = 0;
var disty = 0;
// When the connection is open, send some data to the server
connection.onopen = function(e) {
writeResponse('Open!');
connection.send('TEST');
}
// Log errors
connection.onerror = function(error) {
writeResponse('Error: ' + error);
}
connection.onclose = function(e) {
writeResponse("Disconnected: " + e.data);
}
// Log messages from the server
connection.onmessage = function(e) {
writeResponse('Server: ' + e.data);
}
function pageLoad() {
game.style.visibility = "hidden";
}
function writeResponse(text) {
messages.innerHTML += "<br/>" + text;
}
function joinSubmit() {
game.style.visibility = "visible";
//connection.send(username.value);
writeResponse("Username sent to server.");
joinform.remove();
leftCanvas.addEventListener('touchstart', function(e) {
var touchobj = e.changedTouches[0] // reference first touch point (ie: first finger)
startx = parseInt(touchobj.clientX) // get x position of touch point relative to left edge of browser
starty = parseInt(touchobj.clientY)
statusdiv.innerHTML = 'Status: touchstart<br> ClientX: ' + startx + 'px'
drawJoystick(startx, starty);
e.preventDefault()
}, false)
leftCanvas.addEventListener('touchmove', function(e) {
e.preventDefault()
var touchobj = e.changedTouches[0] // reference first touch point for this event
var distx = parseInt(touchobj.clientX) - startx
var disty = parseInt(touchobj.clientY) - starty
statusdiv.innerHTML = 'Status: touchmove<br> Horizontal distance traveled: ' + distx + 'px<br> Vertical distance traveled: ' + disty + 'px' +
'<br>Dist: ' + Math.sqrt((startx - (distx + startx)) * (startx - (distx + startx)) + (starty - (disty + starty)) * (starty - (disty +
starty)));
//connection.send("L=" + distx + "=" + disty);
connection.send("A=" + Math.floor(angleBetween2Lines(startx, starty, startx + distx, starty + disty) + 90) * -1);
connection.send("B=" + distx + "=" + disty);
}, false)
leftCanvas.addEventListener('touchend', function(e) {
var touchobj = e.changedTouches[0] // reference first touch point for this event
statusdiv.innerHTML = 'Status: touchend<br> Resting x coordinate: ' + touchobj.clientX + 'px'
e.preventDefault()
}, false)
}
function gameButton(ctrl) {
writeResponse("Control: " + ctrl);
connection.send(ctrl);
writeResponse("Sent!");
}
function find_angle(x1, y1, x2, y2) {
var AB = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
var BC = Math.sqrt(Math.pow(x2 - (x1 - 10), 2) + Math.pow(y2 - y1, 2));
var AC = Math.sqrt(Math.pow((x1 - 10) - x1, 2) + Math.pow(y1 - y1, 2));
return Math.acos((BC * BC + AB * AB - AC * AC) / (2 * BC * AB)) * (180 / Math.PI);
}
function angleBetween2Lines(x1, y1, x2, y2) {
var angle1 = Math.atan2(y1 - y1,
x1 - (x1 + 100));
var angle2 = Math.atan2(y1 - y2,
x1 - x2);
return angle1 - angle2 * (180 / Math.PI);
}
</script>
</html>
EDIT: I tested the exact code from this page http://websocket.org/echo.html and it seems to work fine on both Android and iOS, so I'm not really sure what is causing my websocket implementation to break on iOS... Any ideas what would cause the connection.send() function to freeze up and block the rest of the javascript method to run?
UPDATE: I reduced my server code to the shortest form possible, and it still works on Android but not on any iPhone I've tried. Also, the javascript code above is not calling the "connection.onopen" function in iPhone, but it is being called on Android...
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
import javax.xml.bind.DatatypeConverter;
public class DateServer {
static String[] headers = new String[10];
static String[] bodies = new String[10];
public static void main(String[] args) throws IOException, NoSuchAlgorithmException {
ServerSocket listener = new ServerSocket(9091);
try {
while (true) {
Socket socket = listener.accept();
System.out.println("Accepted");
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String msg;
while ((msg = input.readLine()) != null) {
if (msg.length() == 0)
break;
parseHeaders(msg);
}
String httpResponse = returnHeader();
try {
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
out.println(httpResponse + "\n");
out.flush();
System.out.println("Sent HTTP Response");
} finally {
}
}
}
finally {
listener.close();
}
}
public static void parseHeaders(String in) {
System.out.println(in);
String[] thisLine = in.split(": ");
if (thisLine[0].equals("Host")) {
bodies[0] = thisLine[1];
}
else if (thisLine[0].equals("Upgrade")) {
bodies[1] = thisLine[1];
}
else if (thisLine[0].equals("Connection")) {
bodies[2] = thisLine[1];
}
else if (thisLine[0].equals("Sec-WebSocket-Key")) {
bodies[3] = thisLine[1];
}
else if (thisLine[0].equals("Sec-WebSocket-Version")) {
bodies [4] = thisLine[1];
}
}
public static String returnHeader() throws NoSuchAlgorithmException, UnsupportedEncodingException {
String head = "HTTP/1.1 101 Web Socket Protocol Handshake\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: ";
String key = bodies[3];
String encoded = WebSocketAccept(key);
String httpResponse = head.concat(encoded);
httpResponse.concat("\r\n\r\n"); // is concat not working??
return httpResponse;
}
private static String WebSocketAccept(String message) throws NoSuchAlgorithmException, UnsupportedEncodingException {
message = message.concat("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
MessageDigest digest = MessageDigest.getInstance("SHA-1");
byte[] hashedBytes = digest.digest(message.getBytes("UTF-8"));
return convertByteArrayToHexString(hashedBytes);
}
private static String convertByteArrayToHexString(byte[] arrayBytes) {
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i < arrayBytes.length; i++) {
stringBuffer.append(Integer.toString((arrayBytes[i] & 0xff) + 0x100, 16).substring(1));
}
return convertHexToBase64(stringBuffer.toString());
}
private static String convertHexToBase64(String hex) {
byte[] hexToBytes = DatatypeConverter.parseHexBinary(hex);
String base64 = Base64.getEncoder().encodeToString(hexToBytes);
System.out.println(base64);
return base64;
}
}
Here are the headers the server receives from Android vs iPhone:
Android:
GET / HTTP/1.1
Host: 174.51.xxx.xx:9091
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Origin: http://server.site88.net
Sec-WebSocket-Version: 13
User-Agent: Mozilla/5.0 (Linux; Android 5.0; SM-G900V Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.91 Mobile Safari/537.36
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
Sec-WebSocket-Key: IKTSyKHA8zclXsm2hFzm5Q==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
iPhone:
GET / HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Host: 174.51.xxx.xx:9091
Origin: http://server.site88.net
Pragma: no-cache
Cache-Control: no-cache
Sec-WebSocket-Key: Sq0xOYhkomBmiyMbGwp/kQ==
Sec-WebSocket-Version: 13
Sec-WebSocket-Extensions: x-webkit-deflate-frame
User-Agent: Mozilla/5.0 (iPhone; CPU iPhone OS 9_2_1 like Mac OS X) AppleWebKit/601.1.46 (KHTML, like Gecko) Version/9.0 Mobile/13D15 Safari/601.1
I figured it out. When I create the headers to send back to the browser from the server, I used "httpResponse.concat("\r\n\r\n")" to add line breaks at the end of the headers, which simply did not work. Changing it to "httpResponse = httpResponse + "\r\n\r\n";" does work.