dongying3830 2013-11-06 03:29
浏览 33

越野车自定义聊天客户端

There's a topic on the HackThisSite forums that details the workings of Omegle (the anonymous chat client at omegle.com): https://www.hackthissite.org/forums/viewtopic.php?f=76&t=3783

That's a little out of date, but it's still mostly accurate. Using that as a jumping-off point, I've been working on a custom Omegle client. At first I programmed it in Game Maker, but then I switched over to Javascript/AJAX (with a little PHP backend to work around some XSS issues), where I thought it would be more appropriate for a web app service.

Basically, the way Omegle works is that you keep polling the server, and when you do, it sends back a list of events that have occurred since the last poll. These events are things like, "waiting" (for a pair-up), "connected" (to a stranger), "gotMessage" (an event containing the message received), "strangerDisconnected" (when the other user disconnects), etc.

Like I said, that link is a little out of date, and one of the new things Omegle has added is an event called statusInfo, which holds a JSON object with some server information (including a list of currently working servers). The idea is that every time you check for new events, you also get an updated list of working servers so you can server-hop if one goes down.

This is all fine and dandy, and my custom client works fine some of the time...but there's a bug. Every so often, when it polls the server for events, the server decides not to respond. It doesn't even send back an acknowledgement or status code of any sort, so the AJAX request just sits there waiting for a response that never comes. The weird thing is that when this happens, if I server-hop, it often doesn't help. It'll hang on the next requests anyway, regardless of server, until it randomly decides to start working again. And all of this seems completely arbitrary--it works perfectly some of the time, and hangs the rest of the time, without any common thread between the two categories as far as I can tell.

Even weirder, if I try to disconnect while such a hang is occurring (which is simply sending a request to server.omegle.com/disconnect with the user's ID posted), Omegle sends back an "Internal Server Error" message; it doesn't do that when I disconnect if there's no hanging in the events request.

I'd love to link you to my client, but I'm currently behind a firewall that I have little control over, so instead I'll have to post the code here...it's only 2 small files and 1 large one, though, so hopefully someone can test it anyway?

Without further ado, here's the entire HTML/Javascript I'm using for my client (no libraries used; all coded from scratch). I just went in to comment it all for you; hopefully it's clear.

<style>
.error { color:#ff0000; }
.them { color: #ff0000; }
.you { color:#0000ff; }
.typer { color:#555555; font-weight:bold; font-size:11pt; }
</style>

<script>

/* Cross-platform XMLHttpRequest function */
function Request() {
  if (window.XMLHttpRequest ) { return new XMLHttpRequest(); }
  else if (window.ActiveXObject) {
    try {
      return new ActiveXObject("Msxml2.XMLHTTP");
    }
    catch (e) {
      try {
        return new ActiveXObject("Microsoft.XMLHTTP");
      }
      catch (e) {
        return false;
      }
    }
  }
}

/* Common array shuffling function used to shuffle the list of servers */
function shuffle(a) {
  var m = a.length, t, i;

  // While there remain elements to shuffle...
  while (m) {

    // Pick a remaining element...
    i = Math.floor(Math.random() * m--);

    // And swap it with the current element.
    t = a[m];
    a[m] = a[i];
    a[i] = t;
  }

  return a;
}

/* A general-purpose handler for the XMLHttpRequests, which simply checks for a "completed" state and then calls the associated handler. I plan on making this more
   robust once the overall system is working at all. */
function HandleXHR() {
  if (this.readyState==4) {
    this.handler(this.responseText);
  }
}

server=null;
servs=null;

/* Whenever a statusInfo message is received, it includes a list of working servers. This code, almost identical to Omegle's own, modifies our own list of servers
   to include all of those (and remove no-longer-working ones) without changing the relative order of the current servers on the list */
function updateServers(list) {
  list=shuffle(list);
  for (i=0; i<list.length; ++i) {
    if (servs.indexOf(list[i])<0) {
      servs.unshift(list[i]);
    }
  }

  for (i=0; i<servs.length; ++i) {
    if (list.indexOf(servs[i])<0) {
      servs.splice(i--, 1);
    }
  }

  server=servs[0];

}

typing=false;

/* Initiate looking for a connection; this requests a user ID from the server, which itself is randomly chosen from the list of servers. This is actually what Omegle's
   own code does--it shuffles the server list around for uniform usage. */
function Connect(what) {
  typing=false;
  what=JSON.parse(what);
  servs=shuffle(what.servers);
  server=servs[0];

  req=Request();
  where="http://"+server+"/start?rcs=1&spid=";
  req.open("GET", "getpage.php?url="+escape(where));
  req.handler=GotID;
  req.onreadystatechange=HandleXHR;
  req.send(null);
}

id=null;
eventwatch=null;

/* When a connection is made and we have a user ID, start polling for events immediately */
function GotID(what) {
  id=what.split('"')[1];
  eventwatch=setTimeout(GetEvents, 1);
}

/* Initiate the poll to the server for any recent events */
function GetEvents() {
  if (id==null) { return false; }

  where="http://"+server+"/events";  
  req=Request();
  req.open("POST", "getposted.php?url="+escape(where));
  req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

  req.handler=ParseEvents;
  req.onreadystatechange=HandleXHR;

  req.send("id="+escape(id));
}

/* The function that parses all the events when they've been received from a poll */
attempts=0;
function ParseEvents(what) {

  spot=document.getElementById("codespot");

  try {
    what=JSON.parse(what);
    attempts=0;
  }

  /* If the messages couldn't be parsed, i.e. the server didn't return anything or returned malformed JSON (or an error),
     hop servers and try again, up to 3 times before giving up */
  catch (e) {
    attempts++;

    /* Move the first server on the list to the end and pick the next server */
    temp=servs.shift();
    servs.push(temp);
    server=servs[0];

    if (attempts>=3) {
      spot.innerHTML+="<b class='error'>Error: "+e.message+"</b><br />";
      Disconnect();
      return false;
    }

    eventwatch=setTimeout(GetEvents, 2500);
    return false;
  }

  /* Process each message */
  for (p in what) {
    code=what[p][0];

    /* Connected event */
    if (code=="connected") {
      spot.innerHTML+="<b>Connected!</b><br />";
      document.getElementById("waiter").innerHTML="";
    }

    /* Message reception */
    else if (code=="gotMessage") { spot.innerHTML+="<b class='them'>Stranger:</b> "+what[p][1]+"<br />"; }

    /* The other user disconnected, so stop checking for events and set your user ID to null (meaning no connection) */
    else if (code=="strangerDisconnected") { typing=false; id=null; spot.innerHTML+="<b>Stranger disconnected</b><br />"; window.clearTimeout(eventwatch); }

    /* Process the statusInfo events by updating the server list */
    else if (code=="statusInfo") {
      updateServers(what[p][1].servers);
    }

    /* These are only used for Omegle logs, which I'm not implementing, so ignore them */
    else if (code=="identDigests") { /* Ignore these for now */ }

    /* Change stranger-typing status */
    else if (code=="typing") { typing=!typing; }
    else if (code=="stoppedTyping") { typing=false; }

    /* Before a connection is made, let the user know you're waiting for one */
    else if (code=="waiting") {
      document.getElementById("waiter").innerHTML="Looking for a stranger...";
    }

    /* If all else fails, we don't know what this message is, so output it for debugging purposes--hasn't happened in all my tests yet */
    else { spot.innerHTML+="Didn't understand: "+code+"<br />"; }
  }

  /* Update the "is typing" display and set a timeout for the next event poll */
  document.getElementById("typer").innerHTML=typing ? "Stranger is typing..." : "";
  eventwatch=setTimeout(GetEvents, 2500);
}

/* The function that attempts to disconnect from the conversation */
function Disconnect() {
  where="http://"+server+"/disconnect?id="+id;

  req=Request();
  req.open("POST", "getposted.php?url="+escape(where));
  req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

  req.handler=Disconnected;
  req.onreadystatechange=HandleXHR;

  req.send("id="+escape(id));
}

/* The function to send a message */
function Send(place) {
  if (id==null) { return false; }
  what=place.value;
  place.disabled=true;

  where="http://"+server+"/send";

  req=Request();
  req.open("POST", "getposted.php?url="+escape(where));
  req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

  req.handler=Sent;
  req.onreadystatechange=HandleXHR;

  req.send("id="+escape(id)+"&msg="+escape(what));
}

/* The function to handle the display when a message you've typed has been sent */
function Sent(what) {
  if (id==null) { return false; }
  document.getElementById("codespot").innerHTML+="<b class='you'>You:</b> "+document.main.mess.value+"<br />";
  document.main.mess.disabled=false;
  document.main.mess.value="";
}

/* The function to handle cleanup once you've disconnected */
function Disconnected(what) {
  typing=false;
  id=null;
  window.clearTimeout(eventwatch);
  document.getElementById("codespot").innerHTML+="<b>Disconnected";
  if (what.substr(0, 3)!="win") { document.getElementById("codespot").innerHTML+=" -- "+what; }
  document.getElementById("codespot").innerHTML+="</b><br />";
}

/* The function to initiate the requests to get the list of active Omegle servers */
function GetServer() {
  req=Request();
  where="http://omegle.com/status";
  req.open("GET", "getpage.php?url="+escape(where));
  req.handler=Connect;
  req.onreadystatechange=HandleXHR;
  req.send(null);
}

</script>

<!-- The chat area -->
<div id="waiter" class="typer"></div>
<div id="codespot"></div>
<div id="typer" class="typer"></div><br />

<!-- The form where you connect, disconnect, and send messages from -->
<form name="main" onSubmit="Send(document.main.mess); return false"><input type="button" value="Connect" onClick="GetServer()" /><input type='text' name='mess' /><input type='submit' value='Send' />
<input type="button" value="Disconnect" onClick="Disconnect()" />

And the backend PHP scripts getpage.php and getposted.php simply use cURL to get the responses from Omegle (despite being on a different domain) and echo them directly, allowing the XHR to access out-of-domain pages.

Can anyone help me figure out why the stalling occurs sometimes?

If you want the simple code for the getposted.php and getpage.php code, it's this:

getposted.php:

<?php
  set_time_limit(60);
  $curl=curl_init();
  curl_setopt($curl, CURLOPT_URL, $_GET["url"]);
  curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // Return value instead of outputting
  curl_setopt($curl,CURLOPT_POST, count($_POST));
  curl_setopt($curl,CURLOPT_POSTFIELDS, http_build_query($_POST, '', '&'));

  $all=curl_exec($curl);
  curl_close($curl);
  echo $all;
?>

getpage.php

<?php
  set_time_limit(60);
  $curl=curl_init();
  curl_setopt($curl, CURLOPT_URL, $_GET["url"]);
  curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); // Return value instead of outputting

  $all=curl_exec($curl);
  curl_close($curl);
  echo $all;
?>
  • 写回答

0条回答 默认 最新

    报告相同问题?

    悬赏问题

    • ¥15 素材场景中光线烘焙后灯光失效
    • ¥15 请教一下各位,为什么我这个没有实现模拟点击
    • ¥15 执行 virtuoso 命令后,界面没有,cadence 启动不起来
    • ¥50 comfyui下连接animatediff节点生成视频质量非常差的原因
    • ¥20 有关区间dp的问题求解
    • ¥15 多电路系统共用电源的串扰问题
    • ¥15 slam rangenet++配置
    • ¥15 有没有研究水声通信方面的帮我改俩matlab代码
    • ¥15 ubuntu子系统密码忘记
    • ¥15 保护模式-系统加载-段寄存器