lol
This commit is contained in:
commit
613bef2496
108 changed files with 8397 additions and 0 deletions
487
src/zutil/network/FTPClient.java
Normal file
487
src/zutil/network/FTPClient.java
Normal file
|
|
@ -0,0 +1,487 @@
|
|||
package zutil.network;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintStream;
|
||||
import java.net.Socket;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import javax.security.auth.login.AccountException;
|
||||
|
||||
import zutil.MultiPrintStream;
|
||||
|
||||
/**
|
||||
* A simple FTP client class
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
* http://en.wikipedia.org/wiki/List_of_FTP_commands
|
||||
* http://en.wikipedia.org/wiki/List_of_FTP_server_return_codes
|
||||
* http://www.ietf.org/rfc/rfc959.txt
|
||||
*
|
||||
* TODO: file info, rename, Active mode
|
||||
*/
|
||||
public class FTPClient extends Thread{
|
||||
public static final int FTP_ACTIVE = 0;
|
||||
public static final int FTP_PASSIVE = 1;
|
||||
public static final int FTP_PORT = 21;
|
||||
public static final int FTP_DATA_PORT = 20;
|
||||
public static final int FTP_NOOP_INT = 120;
|
||||
|
||||
//************** FTP Return Codes ******************
|
||||
public static final int FTPC_USER_OK = 331;
|
||||
public static final int FTPC_NEED_PASS = 331;
|
||||
public static final int FTPC_LOGIN_NO = 530;
|
||||
public static final int FTPC_LOGIN_OK = 230;
|
||||
|
||||
public static final int FTPC_ENTERING_PASSIVE = 227;
|
||||
public static final int FTPC_FILE_ACTION_OK = 250;
|
||||
public static final int FTPC_PATH_CREATED = 257;
|
||||
//***************************************************
|
||||
|
||||
private BufferedReader in;
|
||||
private PrintStream out;
|
||||
private Socket socket;
|
||||
private long last_sent;
|
||||
|
||||
public static void main(String[] args){
|
||||
try {
|
||||
FTPClient client = new FTPClient("koc.se", 21, "kth", "****", FTP_PASSIVE);
|
||||
client.createDir("./ziver/lol");
|
||||
client.removeDir("./ziver/lol");
|
||||
|
||||
MultiPrintStream.out.dump(client.getFileList("./ziver"));
|
||||
client.sendFile("./ziver/test.txt", "lol");
|
||||
MultiPrintStream.out.dump(client.getFileList("./ziver"));
|
||||
|
||||
MultiPrintStream.out.dump(client.getFile("./ziver/test.txt"));
|
||||
client.readCommand(true);
|
||||
|
||||
MultiPrintStream.out.println(client.getFileInfo("./ziver/test.txt"));
|
||||
|
||||
MultiPrintStream.out.dump(client.getFileList("./ziver"));
|
||||
client.removeFile("./ziver/test.txt");
|
||||
MultiPrintStream.out.dump(client.getFileList("./ziver"));
|
||||
|
||||
client.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a FTP connection and logs in
|
||||
*
|
||||
* @param url The address to server
|
||||
* @param port Port number
|
||||
* @param user User name
|
||||
* @param pass Password
|
||||
* @param connection_type Pasive or Active
|
||||
*/
|
||||
public FTPClient(String url, int port, String user, String pass, int connection_type) throws UnknownHostException, IOException, AccountException{
|
||||
socket = new Socket(url, port);
|
||||
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
out = new PrintStream(socket.getOutputStream());
|
||||
|
||||
readCommand(true);
|
||||
sendCommand("USER "+user);
|
||||
sendNoReplyCommand("PASS "+pass, false);
|
||||
System.out.println("PASS ***");
|
||||
String tmp = readMultipleCommands(true);
|
||||
if(parseReturnCode(tmp) == FTPC_LOGIN_NO){
|
||||
close();
|
||||
throw new AccountException(tmp);
|
||||
}
|
||||
|
||||
start();
|
||||
}
|
||||
|
||||
//**************************************************************************************
|
||||
//**************************************************************************************
|
||||
//********************************* Command channel ************************************
|
||||
|
||||
/**
|
||||
* Sends the given line to the server and returns a status integer
|
||||
*
|
||||
* @param cmd The command to send
|
||||
* @return The return code from the server
|
||||
* @throws IOException
|
||||
*/
|
||||
public synchronized int sendCommand(String cmd) throws IOException{
|
||||
return parseReturnCode(sendCommand(cmd, true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the given line to the server and returns the last line
|
||||
*
|
||||
* @param cmd The command to send
|
||||
* @param print To print out the received lines
|
||||
* @return Last String line from the server
|
||||
* @throws IOException
|
||||
*/
|
||||
private synchronized String sendCommand(String cmd, boolean print) throws IOException{
|
||||
sendNoReplyCommand(cmd, print);
|
||||
return readCommand(print);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a given command and don't cares about the reply
|
||||
*
|
||||
* @param cmd The command
|
||||
* @param print If it should print to System.out
|
||||
* @throws IOException
|
||||
*/
|
||||
private synchronized void sendNoReplyCommand(String cmd, boolean print) throws IOException{
|
||||
out.println(cmd);
|
||||
last_sent = System.currentTimeMillis();
|
||||
if(print)System.out.println(cmd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads on line from the command channel
|
||||
*
|
||||
* @param print If the method should print the input line
|
||||
* @return The input line
|
||||
* @throws IOException
|
||||
*/
|
||||
private synchronized String readCommand(boolean print) throws IOException{
|
||||
String tmp = in.readLine();
|
||||
if(print)System.out.println(tmp);
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads from the command channel until there are nothing
|
||||
* left to read and returns the last line
|
||||
* Multiple
|
||||
* @param print To print out the received lines
|
||||
* @return The last received line
|
||||
* @throws IOException
|
||||
*/
|
||||
private synchronized String readMultipleCommands(boolean print) throws IOException{
|
||||
String tmp = in.readLine();
|
||||
if(print)System.out.println(tmp);
|
||||
try{ Thread.sleep(500); }catch(Exception e){}
|
||||
while(in.ready()){
|
||||
tmp = in.readLine();
|
||||
if(print)System.out.println(tmp);
|
||||
try{ Thread.sleep(500); }catch(Exception e){}
|
||||
}
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the return line from the server and returns the status code
|
||||
*
|
||||
* @param msg The message from the server
|
||||
* @return The status code
|
||||
* @throws IOException
|
||||
*/
|
||||
private synchronized int parseReturnCode(String msg){
|
||||
return Integer.parseInt(msg.substring(0, 3));
|
||||
}
|
||||
|
||||
//**************************************************************************************
|
||||
//**************************************************************************************
|
||||
//****************************** File system actions ************************************
|
||||
|
||||
/**
|
||||
* Returns a LinkedList with the names of all the files in the directory
|
||||
*
|
||||
* @param path Path to the files to be listed
|
||||
* @return LinkedList whit filenames
|
||||
* @throws IOException
|
||||
*/
|
||||
public LinkedList<String> getFileList(String path) throws IOException{
|
||||
LinkedList<String> list = new LinkedList<String>();
|
||||
|
||||
BufferedReader data_in = getDataInputStream();
|
||||
sendCommand("NLST "+path, true);
|
||||
|
||||
String tmp = "";
|
||||
while((tmp = data_in.readLine()) != null){
|
||||
list.add(tmp);
|
||||
}
|
||||
|
||||
data_in.close();
|
||||
readCommand(true);
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns information about the file or directory
|
||||
* (This is system specific information)
|
||||
*
|
||||
* TODO:
|
||||
* @deprecated DOSENT WORK!!!!
|
||||
* @param path The path and filename of a file or a directory
|
||||
* @return A String with information
|
||||
* @throws IOException
|
||||
*/
|
||||
public String getFileInfo(String path) throws IOException{
|
||||
StringBuffer info = new StringBuffer("");
|
||||
|
||||
BufferedReader data_in = getDataInputStream();
|
||||
sendCommand("LIST "+path, true);
|
||||
|
||||
String tmp = "";
|
||||
while((tmp = data_in.readLine()) != null){
|
||||
info.append(tmp);
|
||||
}
|
||||
|
||||
data_in.close();
|
||||
readCommand(true);
|
||||
return info.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a file at the server with the given data
|
||||
*
|
||||
* @param path The path and filename
|
||||
* @param data The data to put in the file
|
||||
* @throws IOException
|
||||
*/
|
||||
public void sendFile(String path, String data) throws IOException{
|
||||
PrintStream data_out = getDataOutputStream();
|
||||
sendCommand("STOR "+path, true);
|
||||
data_out.println(data);
|
||||
data_out.close();
|
||||
readCommand(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a directory at the server
|
||||
*
|
||||
* @param path The path to the directory
|
||||
* @throws IOException
|
||||
*/
|
||||
public boolean createDir(String path) throws IOException{
|
||||
if(sendCommand("MKD "+path) == FTPC_PATH_CREATED)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a BufferedReader with the file data
|
||||
* WARNING: you must run readCommand(true); after you close the stream
|
||||
*
|
||||
* @param path The path and filename
|
||||
* @return Stream with the file
|
||||
* @throws IOException
|
||||
*/
|
||||
private BufferedReader getFile(String path) throws IOException{
|
||||
BufferedReader ret = getDataInputStream();
|
||||
sendCommand("RETR "+path, true);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads a file from the FTP server to a local file
|
||||
*
|
||||
* @param source The source file on the server
|
||||
* @param destination The local file to save to
|
||||
* @throws IOException
|
||||
*/
|
||||
public void getFile(String source, String destination) throws IOException{
|
||||
BufferedReader file_in = getFile(source);
|
||||
PrintStream file_out = new PrintStream(new File(destination));
|
||||
|
||||
String tmp = "";
|
||||
while((tmp = file_in.readLine()) != null){
|
||||
file_out.println(tmp);
|
||||
}
|
||||
readCommand(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a file from the FTP server
|
||||
*
|
||||
* @param path The path and filename of the file to be deleted
|
||||
* @return True if the command was successful or false otherwise
|
||||
* @throws IOException
|
||||
*/
|
||||
public boolean removeFile(String path) throws IOException{
|
||||
if(sendCommand("DELE "+path) == FTPC_FILE_ACTION_OK)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a directory from the FTP server
|
||||
*
|
||||
* @param path The path of the directory to be deleted
|
||||
* @return True if the command was successful or false otherwise
|
||||
* @throws IOException
|
||||
*/
|
||||
public boolean removeDir(String path) throws IOException{
|
||||
if(sendCommand("RMD "+path) == FTPC_FILE_ACTION_OK)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
//**************************************************************************************
|
||||
//**************************************************************************************
|
||||
//******************************** Data Connection *************************************
|
||||
|
||||
/**
|
||||
* Starts a connection to the server. It automatically handles
|
||||
* passive or active mode
|
||||
*
|
||||
* @return The PrintStream for the channel
|
||||
* @throws IOException
|
||||
*/
|
||||
public synchronized PrintStream getDataOutputStream() throws IOException{
|
||||
int port = getDataConnectionPortType();
|
||||
if(port < 0){ // Active Mode
|
||||
port *= -1;
|
||||
return getActiveDataOutputStream(port);
|
||||
}
|
||||
else{
|
||||
System.out.println("port: "+port);
|
||||
return getPassiveDataOutputStream(port);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a connection to the server. It automatically handles
|
||||
* passive or active mode
|
||||
*
|
||||
* @return The BufferedReader for the channel
|
||||
* @throws IOException
|
||||
*/
|
||||
public synchronized BufferedReader getDataInputStream() throws IOException{
|
||||
int port = getDataConnectionPortType();
|
||||
if(port < 0){ // Active Mode
|
||||
port *= -1;
|
||||
return getActiveDataInputStream(port);
|
||||
}
|
||||
else{
|
||||
return getPassiveDataInputStream(port);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method chooses the appropriate data connection type
|
||||
* to the server (Passive or Active) and returns the port number
|
||||
*
|
||||
* @return A port number. If port > 0 = Passive AND port < 0 Active
|
||||
* @throws IOException
|
||||
*/
|
||||
private int getDataConnectionPortType() throws IOException{
|
||||
return setPassiveMode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects to the data port on the server and returns the InputStream
|
||||
*
|
||||
* @param port The port to connect to
|
||||
* @return The InputStream for the data channel
|
||||
* @throws IOException
|
||||
*/
|
||||
private BufferedReader getPassiveDataInputStream(int port) throws IOException{
|
||||
Socket data_socket = new Socket(socket.getInetAddress().getHostAddress(), port);
|
||||
BufferedReader data_in = new BufferedReader(new InputStreamReader(data_socket.getInputStream()));
|
||||
|
||||
return data_in;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects to the data port on the server and returns the OutputStream
|
||||
*
|
||||
* @param port The port to connect to
|
||||
* @return The OutputStream for the data channel
|
||||
* @throws IOException
|
||||
*/
|
||||
private PrintStream getPassiveDataOutputStream(int port) throws IOException{
|
||||
Socket data_socket = new Socket(socket.getInetAddress().getHostAddress(), port);
|
||||
PrintStream data_out = new PrintStream(data_socket.getOutputStream());
|
||||
|
||||
return data_out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens on a local port for a connection from the server
|
||||
* and returns with the InputStream of the connection from the server
|
||||
*
|
||||
* @param port The port to listen to
|
||||
* @return The InputStream for the data channel
|
||||
* @throws IOException
|
||||
*/
|
||||
private BufferedReader getActiveDataInputStream(int port) throws IOException{
|
||||
// TODO:
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens on a local port for a connection from the server
|
||||
* and returns with the OutputStream of the connection from the server
|
||||
*
|
||||
* @param port The port to listen to
|
||||
* @return The OutputStream for the data channel
|
||||
* @throws IOException
|
||||
*/
|
||||
private PrintStream getActiveDataOutputStream(int port) throws IOException{
|
||||
// TODO:
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets Passive mode at the server and returns the port number
|
||||
* for the data channel
|
||||
*
|
||||
* @return Port number for data channel
|
||||
* @throws IOException
|
||||
*/
|
||||
private int setPassiveMode() throws IOException{
|
||||
String tmp = sendCommand("PASV", true);
|
||||
if(parseReturnCode(tmp) != FTPC_ENTERING_PASSIVE){
|
||||
throw new IOException(tmp);
|
||||
}
|
||||
tmp = tmp.substring(tmp.indexOf('(')+1, tmp.indexOf(')'));
|
||||
String[] tmpArray = tmp.split("[,]");
|
||||
|
||||
if(tmpArray.length <= 1)
|
||||
return Integer.parseInt(tmpArray[0]);
|
||||
else
|
||||
return Integer.parseInt(tmpArray[4])*256 + Integer.parseInt(tmpArray[5]);
|
||||
}
|
||||
|
||||
//**************************************************************************************
|
||||
//**************************************************************************************
|
||||
|
||||
/**
|
||||
* To keep the connection alive
|
||||
*/
|
||||
public void run(){
|
||||
try {
|
||||
while(true){
|
||||
if(last_sent > System.currentTimeMillis() + FTP_NOOP_INT*1000){
|
||||
sendCommand("NOOP");
|
||||
}
|
||||
try{ Thread.sleep(5000); }catch(Exception e){}
|
||||
}
|
||||
} catch (IOException e1) {
|
||||
e1.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the FTP connection
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public void close() throws IOException{
|
||||
sendCommand("QUIT", true);
|
||||
in.close();
|
||||
out.close();
|
||||
socket.close();
|
||||
this.stop();
|
||||
}
|
||||
}
|
||||
73
src/zutil/network/ServerFind.java
Normal file
73
src/zutil/network/ServerFind.java
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
package zutil.network;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.MulticastSocket;
|
||||
|
||||
import zutil.MultiPrintStream;
|
||||
|
||||
/**
|
||||
* This class broadcast its address in the LAN so that
|
||||
* the ServerFindClient can get the server IP
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public class ServerFind extends Thread {
|
||||
public String broadcastAddress = "230.0.0.1";
|
||||
|
||||
private InetAddress group;
|
||||
private MulticastSocket Msocket;
|
||||
|
||||
private boolean avsluta;
|
||||
private int port;
|
||||
|
||||
/**
|
||||
* Creates a ServerFind Thread an the specified port
|
||||
*
|
||||
* @param port The port to run the ServerFind Server on
|
||||
* @throws IOException
|
||||
*/
|
||||
public ServerFind (int port) throws IOException {
|
||||
this.port = port;
|
||||
avsluta = false;
|
||||
group = InetAddress.getByName(broadcastAddress);
|
||||
Msocket = new MulticastSocket(port);
|
||||
Msocket.joinGroup(group);
|
||||
|
||||
start();
|
||||
}
|
||||
|
||||
public void run (){
|
||||
byte[] buf = new byte[256];
|
||||
DatagramPacket packet;
|
||||
DatagramSocket lan_socket = null;
|
||||
|
||||
while (!avsluta){
|
||||
try {
|
||||
packet = new DatagramPacket(buf, buf.length);
|
||||
Msocket.receive(packet);
|
||||
|
||||
lan_socket = new DatagramSocket(port , packet.getAddress());
|
||||
packet = new DatagramPacket(buf, buf.length, group, port);
|
||||
lan_socket.send(packet);
|
||||
lan_socket.close();
|
||||
} catch (Exception e) {
|
||||
MultiPrintStream.out.println("Error Establishing ServerFind Connection!!!\n" + e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the broadcast socket
|
||||
*/
|
||||
public void close(){
|
||||
avsluta = true;
|
||||
Msocket.close();
|
||||
}
|
||||
}
|
||||
50
src/zutil/network/ServerFindClient.java
Normal file
50
src/zutil/network/ServerFindClient.java
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
package zutil.network;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.MulticastSocket;
|
||||
|
||||
/**
|
||||
* This class is the client for ServerFind that receives the server IP
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public class ServerFindClient{
|
||||
public String broadcastAddress = "230.0.0.1";
|
||||
|
||||
private int port;
|
||||
|
||||
/**
|
||||
* Creates a ServerFind Client
|
||||
*
|
||||
* @param port The port to contact the server on
|
||||
*/
|
||||
public ServerFindClient(int port){
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests IP from server
|
||||
*
|
||||
* @return The address of the server
|
||||
* @throws IOException
|
||||
*/
|
||||
public InetAddress find() throws IOException{
|
||||
InetAddress group = InetAddress.getByName(broadcastAddress);
|
||||
DatagramSocket lan_socket = new DatagramSocket();
|
||||
MulticastSocket Msocket = new MulticastSocket(port);
|
||||
Msocket.joinGroup(group);
|
||||
|
||||
byte[] buf = new byte[256];
|
||||
DatagramPacket packet = new DatagramPacket(buf, buf.length, group, port);
|
||||
lan_socket.send(packet);
|
||||
|
||||
packet = new DatagramPacket(buf, buf.length);
|
||||
Msocket.receive(packet);
|
||||
|
||||
return packet.getAddress();
|
||||
}
|
||||
}
|
||||
134
src/zutil/network/UpdateClient.java
Normal file
134
src/zutil/network/UpdateClient.java
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
package zutil.network;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.net.Socket;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import zutil.FileFinder;
|
||||
import zutil.MultiPrintStream;
|
||||
import zutil.ProgressListener;
|
||||
|
||||
/**
|
||||
* This class connects to a update server and updates a path
|
||||
* with the servers
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public class UpdateClient{
|
||||
private ArrayList<FileHash> clientFileList;
|
||||
private Socket socket;
|
||||
private String path;
|
||||
private ProgressListener progress;
|
||||
private int speed;
|
||||
private long totalReceived;
|
||||
|
||||
/**
|
||||
* Creates a UpdateClient
|
||||
*
|
||||
* @param address Address to the UpdateServer
|
||||
* @param port The port on the server
|
||||
* @param path Path to the files to update
|
||||
* @throws Exception
|
||||
*/
|
||||
public UpdateClient(String address, int port, String path) throws Exception{
|
||||
clientFileList = UpdateServer.getFileList(path);
|
||||
socket = new Socket(address, port);
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public void setProgressListener(ProgressListener p){
|
||||
progress = p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the files
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void update() throws Exception{
|
||||
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
|
||||
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
|
||||
|
||||
// send client file list
|
||||
out.writeObject(clientFileList);
|
||||
out.flush();
|
||||
|
||||
// receive file updates
|
||||
FileHash fileInfo = (FileHash)in.readObject();
|
||||
File tmpPath = FileFinder.find(path);
|
||||
while(!fileInfo.path.isEmpty()){
|
||||
MultiPrintStream.out.println("Updating: "+path+fileInfo.path);
|
||||
// reading new file data
|
||||
File file = new File(tmpPath.getAbsolutePath()+fileInfo.path);
|
||||
File tmpFile = File.createTempFile(file.getName(), ".tmp", tmpPath);
|
||||
tmpFile.getParentFile().mkdirs();
|
||||
tmpFile.deleteOnExit();
|
||||
|
||||
FileOutputStream fileOut = new FileOutputStream(tmpFile);
|
||||
byte[] buffer = new byte[socket.getReceiveBufferSize()];
|
||||
|
||||
int bytesReceived = 0;
|
||||
totalReceived = 0;
|
||||
long time = System.currentTimeMillis();
|
||||
long timeTotalRecived = 0;
|
||||
|
||||
while((bytesReceived = in.read(buffer)) > 0) {
|
||||
fileOut.write(buffer, 0, bytesReceived);
|
||||
|
||||
if(time+1000 < System.currentTimeMillis()){
|
||||
time = System.currentTimeMillis();
|
||||
speed = (int)(totalReceived - timeTotalRecived);
|
||||
timeTotalRecived = totalReceived;
|
||||
}
|
||||
|
||||
totalReceived += bytesReceived;
|
||||
if(progress != null) progress.progressUpdate(this, fileInfo, (double)totalReceived/fileInfo.size*100);
|
||||
}
|
||||
fileOut.close();
|
||||
speed = 0;
|
||||
|
||||
// delete old file and replace whit new
|
||||
file.delete();
|
||||
if(!tmpFile.renameTo(file)){
|
||||
throw new Exception("Cannot update file: "+file.getAbsolutePath());
|
||||
}
|
||||
// read new message
|
||||
fileInfo = (FileHash)in.readObject();
|
||||
}
|
||||
|
||||
MultiPrintStream.out.println("Update Done!!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the speed of the transfer
|
||||
*
|
||||
* @return The speed in bytes/s
|
||||
*/
|
||||
public int speed(){
|
||||
return speed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total amount of data received for the
|
||||
* current file
|
||||
*
|
||||
* @return The speed in bytes/s
|
||||
*/
|
||||
public long totalReceived(){
|
||||
return totalReceived;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the connection
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void close() throws IOException{
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
165
src/zutil/network/UpdateServer.java
Normal file
165
src/zutil/network/UpdateServer.java
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
package zutil.network;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.URISyntaxException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import zutil.FileFinder;
|
||||
import zutil.Hasher;
|
||||
import zutil.MultiPrintStream;
|
||||
|
||||
public class UpdateServer extends Thread{
|
||||
private ArrayList<FileHash> fileList;
|
||||
private ServerSocket server;
|
||||
private boolean close;
|
||||
private String path;
|
||||
|
||||
/**
|
||||
* Creates a UpdateServer Thread
|
||||
*
|
||||
* @param path The path to sync the clients with
|
||||
* @throws IOException
|
||||
* @throws URISyntaxException
|
||||
* @throws NoSuchAlgorithmException
|
||||
*/
|
||||
public UpdateServer(int port, String path) throws Exception{
|
||||
fileList = getFileList(path);
|
||||
server = new ServerSocket(port);
|
||||
close = false;
|
||||
this.path = path;
|
||||
|
||||
this.start();
|
||||
MultiPrintStream.out.println("Update Server Online!!!");
|
||||
}
|
||||
|
||||
public void run(){
|
||||
while (!close){
|
||||
try {
|
||||
new UpdateServerThread(server.accept()).start();
|
||||
MultiPrintStream.out.println("Update Server: Client Connected!!!");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles all the connecting clients
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
class UpdateServerThread extends Thread{
|
||||
private ObjectOutputStream out;
|
||||
private ObjectInputStream in;
|
||||
private Socket client;
|
||||
|
||||
/**
|
||||
* Creates a UpdateServerThread
|
||||
* @param client The socket to the client
|
||||
* @throws IOException
|
||||
*/
|
||||
public UpdateServerThread(Socket c) throws IOException {
|
||||
client = c;
|
||||
out = new ObjectOutputStream(client.getOutputStream());
|
||||
in = new ObjectInputStream(client.getInputStream());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void run(){
|
||||
try {
|
||||
// receive the clients filelist
|
||||
ArrayList<FileHash> clientFileList = (ArrayList<FileHash>)in.readObject();
|
||||
File tmpPath = FileFinder.find(path);
|
||||
|
||||
for(FileHash file : fileList){
|
||||
if(!clientFileList.contains(file)){
|
||||
// send new file to client
|
||||
out.writeObject(file);
|
||||
out.flush();
|
||||
|
||||
// send file data
|
||||
FileInputStream input = new FileInputStream(tmpPath.getAbsolutePath()+file.path);
|
||||
byte[] nextBytes = new byte[client.getSendBufferSize()];
|
||||
int bytesRead = 0;
|
||||
while((bytesRead = input.read(nextBytes)) > 0){
|
||||
out.write(nextBytes,0,bytesRead);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// send update done message
|
||||
out.writeObject(new FileHash("","",0));
|
||||
out.flush();
|
||||
|
||||
out.close();
|
||||
in.close();
|
||||
client.close();
|
||||
} catch (Exception e) {
|
||||
MultiPrintStream.out.println("Update Server: Client Error!!! "+e.getMessage());
|
||||
} finally {
|
||||
MultiPrintStream.out.println("Update Server: Client Update Done!!!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a ArrayList with all the files in the specified folder and there
|
||||
* MD5 hashes
|
||||
*
|
||||
* @param path The path to search
|
||||
* @return A ArrayList with all the files in the path
|
||||
* @throws URISyntaxException
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws IOException
|
||||
*/
|
||||
public static ArrayList<FileHash> getFileList(String path) throws Exception{
|
||||
ArrayList<FileHash> fileHash = new ArrayList<FileHash>();
|
||||
|
||||
ArrayList<File> files = FileFinder.search(FileFinder.find(path));
|
||||
for(File file : files){
|
||||
fileHash.add(new FileHash(
|
||||
FileFinder.relativePath(file, path),
|
||||
Hasher.hash(file, "MD5"),
|
||||
file.length()));
|
||||
}
|
||||
|
||||
return fileHash;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is used to store the files
|
||||
* and there hashes
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
class FileHash implements Serializable{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public String path;
|
||||
public String hash;
|
||||
public long size;
|
||||
|
||||
public FileHash(String p, String h, long s){
|
||||
path = p;
|
||||
hash = h;
|
||||
size = s;
|
||||
}
|
||||
|
||||
public boolean equals(Object comp){
|
||||
FileHash tmp = (FileHash)comp;
|
||||
return path.equals(tmp.path) && hash.equals(tmp.hash);
|
||||
}
|
||||
|
||||
public String toString(){
|
||||
return path;
|
||||
}
|
||||
}
|
||||
232
src/zutil/network/Zupdater.java
Normal file
232
src/zutil/network/Zupdater.java
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
/*
|
||||
* Zupdater.java
|
||||
*
|
||||
* Created on den 27 juli 2008, 23:32
|
||||
*/
|
||||
|
||||
package zutil.network;
|
||||
|
||||
import java.awt.Dimension;
|
||||
|
||||
import zutil.ProgressListener;
|
||||
import zutil.StringUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public class Zupdater extends javax.swing.JFrame implements ProgressListener{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Creates new form Zupdater */
|
||||
public Zupdater() {
|
||||
super("Zupdater");
|
||||
initComponents();
|
||||
centerScreen();
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
public void centerScreen(){
|
||||
Dimension screen = getToolkit().getScreenSize();
|
||||
this.setBounds(
|
||||
(screen.width-getWidth())/2,
|
||||
(screen.height-getHeight())/2,
|
||||
getWidth(),
|
||||
getHeight() );
|
||||
}
|
||||
|
||||
public void progressUpdate(Object source, Object info, double percent) {
|
||||
if(info instanceof FileHash){
|
||||
FileHash fileHash = (FileHash) info;
|
||||
fileLabel.setText(fileHash.toString());
|
||||
fileProgressBar.setValue((int)percent);
|
||||
percentLabel.setText((int)percent+"%");
|
||||
|
||||
speedLabel.setText(StringUtil.formatBytesToString(((UpdateClient)source).speed())+"/s");
|
||||
transferedLabel.setText(StringUtil.formatBytesToString(((UpdateClient)source).totalReceived())+
|
||||
" / "+StringUtil.formatBytesToString(fileHash.size));
|
||||
}
|
||||
}
|
||||
|
||||
/** This method is called from within the constructor to
|
||||
* initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is
|
||||
* always regenerated by the Form Editor.
|
||||
*/
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">
|
||||
private void initComponents() {
|
||||
|
||||
jSeparator1 = new javax.swing.JSeparator();
|
||||
cancelButton = new javax.swing.JButton();
|
||||
jPanel1 = new javax.swing.JPanel();
|
||||
jLabel1 = new javax.swing.JLabel();
|
||||
totalProgressBar = new javax.swing.JProgressBar();
|
||||
jLabel2 = new javax.swing.JLabel();
|
||||
fileProgressBar = new javax.swing.JProgressBar();
|
||||
fileLabel = new javax.swing.JLabel();
|
||||
totalProgressLabel = new javax.swing.JLabel();
|
||||
speedLabel = new javax.swing.JLabel();
|
||||
percentLabel = new javax.swing.JLabel();
|
||||
transferedLabel = new javax.swing.JLabel();
|
||||
etaLabel = new javax.swing.JLabel();
|
||||
jLabel3 = new javax.swing.JLabel();
|
||||
|
||||
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
|
||||
|
||||
cancelButton.setText("Cancel");
|
||||
cancelButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
cancel();
|
||||
}
|
||||
});
|
||||
|
||||
jPanel1.setBorder(javax.swing.BorderFactory.createTitledBorder("Update"));
|
||||
|
||||
jLabel1.setFont(new java.awt.Font("Tahoma", 1, 11));
|
||||
jLabel1.setText("Total Progress:");
|
||||
|
||||
totalProgressBar.setIndeterminate(true);
|
||||
|
||||
jLabel2.setFont(new java.awt.Font("Tahoma", 1, 11));
|
||||
jLabel2.setText("File: ");
|
||||
|
||||
fileLabel.setFont(new java.awt.Font("Tahoma", 0, 11));
|
||||
fileLabel.setText("file");
|
||||
|
||||
totalProgressLabel.setFont(new java.awt.Font("Tahoma", 0, 11));
|
||||
totalProgressLabel.setText("totalProgress");
|
||||
|
||||
speedLabel.setFont(new java.awt.Font("Tahoma", 1, 11));
|
||||
speedLabel.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
|
||||
speedLabel.setText("speed");
|
||||
|
||||
percentLabel.setFont(new java.awt.Font("Tahoma", 1, 11));
|
||||
percentLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
|
||||
percentLabel.setText("0%");
|
||||
|
||||
transferedLabel.setFont(new java.awt.Font("Tahoma", 2, 11));
|
||||
transferedLabel.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
|
||||
transferedLabel.setText("transfer");
|
||||
|
||||
etaLabel.setFont(new java.awt.Font("Tahoma", 0, 11));
|
||||
etaLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
|
||||
etaLabel.setText("eta");
|
||||
|
||||
jLabel3.setFont(new java.awt.Font("Tahoma", 1, 11));
|
||||
jLabel3.setText("ETA:");
|
||||
|
||||
javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
|
||||
jPanel1.setLayout(jPanel1Layout);
|
||||
jPanel1Layout.setHorizontalGroup(
|
||||
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addComponent(jLabel1)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(totalProgressLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 464, Short.MAX_VALUE))
|
||||
.addComponent(totalProgressBar, javax.swing.GroupLayout.DEFAULT_SIZE, 555, Short.MAX_VALUE)
|
||||
.addComponent(fileProgressBar, javax.swing.GroupLayout.DEFAULT_SIZE, 555, Short.MAX_VALUE)
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jLabel2)
|
||||
.addComponent(jLabel3))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addComponent(etaLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 175, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(percentLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(transferedLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 207, Short.MAX_VALUE))
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addComponent(fileLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 399, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(speedLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 119, Short.MAX_VALUE)))))
|
||||
.addContainerGap())
|
||||
);
|
||||
jPanel1Layout.setVerticalGroup(
|
||||
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(jLabel1)
|
||||
.addComponent(totalProgressLabel))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(totalProgressBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(jLabel2)
|
||||
.addComponent(fileLabel)
|
||||
.addComponent(speedLabel))
|
||||
.addGap(6, 6, 6)
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(jLabel3)
|
||||
.addComponent(etaLabel)
|
||||
.addComponent(transferedLabel)
|
||||
.addComponent(percentLabel))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(fileProgressBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
);
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
|
||||
getContentPane().setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jSeparator1, javax.swing.GroupLayout.DEFAULT_SIZE, 587, Short.MAX_VALUE)
|
||||
.addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(cancelButton, javax.swing.GroupLayout.Alignment.TRAILING))
|
||||
.addContainerGap())
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(cancelButton)
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
);
|
||||
|
||||
pack();
|
||||
}// </editor-fold>
|
||||
|
||||
public void cancel(){
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param args the command line arguments
|
||||
*/
|
||||
public static void main(String args[]) {
|
||||
java.awt.EventQueue.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
new Zupdater().setVisible(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Variables declaration - do not modify
|
||||
private javax.swing.JButton cancelButton;
|
||||
private javax.swing.JLabel etaLabel;
|
||||
private javax.swing.JLabel fileLabel;
|
||||
private javax.swing.JProgressBar fileProgressBar;
|
||||
private javax.swing.JLabel jLabel1;
|
||||
private javax.swing.JLabel jLabel2;
|
||||
private javax.swing.JLabel jLabel3;
|
||||
private javax.swing.JPanel jPanel1;
|
||||
private javax.swing.JSeparator jSeparator1;
|
||||
private javax.swing.JLabel percentLabel;
|
||||
private javax.swing.JLabel speedLabel;
|
||||
private javax.swing.JProgressBar totalProgressBar;
|
||||
private javax.swing.JLabel totalProgressLabel;
|
||||
private javax.swing.JLabel transferedLabel;
|
||||
// End of variables declaration
|
||||
|
||||
}
|
||||
28
src/zutil/network/http/HttpPage.java
Normal file
28
src/zutil/network/http/HttpPage.java
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
package zutil.network.http;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* This is a interface for a ordinary page for the HttpServer
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public interface HttpPage{
|
||||
/**
|
||||
* This method has to be implemented for every page.
|
||||
* This method is called when a client wants a response
|
||||
* from this specific page.
|
||||
*
|
||||
* @param out The PrintStream to the client
|
||||
* @param client_info Information about the client
|
||||
* @param session Session values for the client
|
||||
* @param cookie Cookie information from the client
|
||||
* @param request POST and GET requests from the client
|
||||
*/
|
||||
public abstract void respond(HttpPrintStream out,
|
||||
HashMap<String,String> client_info,
|
||||
HashMap<String,String> session,
|
||||
HashMap<String,String> cookie,
|
||||
HashMap<String,String> request);
|
||||
}
|
||||
135
src/zutil/network/http/HttpPrintStream.java
Normal file
135
src/zutil/network/http/HttpPrintStream.java
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
package zutil.network.http;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* This PrintStream is written for HTTP use
|
||||
* It has buffer capabilities and cookie management.
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public class HttpPrintStream extends PrintStream{
|
||||
private HashMap<String, String> cookie;
|
||||
private StringBuffer buffer;
|
||||
private boolean buffer_enabled;
|
||||
|
||||
public HttpPrintStream(OutputStream out) {
|
||||
super(out);
|
||||
|
||||
cookie = new HashMap<String, String>();
|
||||
buffer = new StringBuffer();
|
||||
buffer_enabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable the buffering capability of the PrintStream.
|
||||
* Nothing will be sent to the client when buffering
|
||||
* is enabled until you close or flush the stream.
|
||||
*
|
||||
* @param b
|
||||
*/
|
||||
public void enableBuffering(boolean b){
|
||||
buffer_enabled = b;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a cookie that will be sent to the client
|
||||
*
|
||||
* @param key The name of the cookie
|
||||
* @param value The value of the cookie
|
||||
* @throws Exception Throws exception if the header has already been sent
|
||||
*/
|
||||
public void setCookie(String key, String value) throws Exception{
|
||||
if(cookie == null)
|
||||
throw new Exception("Header already sent!!!");
|
||||
cookie.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the given header directly to the client.
|
||||
* No buffering involved.
|
||||
*
|
||||
* @param header The header to send
|
||||
* @throws Exception Throws exception if the header has already been sent
|
||||
*/
|
||||
public void sendHeader(String header) throws Exception{
|
||||
if(cookie == null)
|
||||
throw new Exception("Header already sent!!!");
|
||||
super.println(header);
|
||||
}
|
||||
|
||||
/**
|
||||
* prints whit a new line
|
||||
*/
|
||||
public void println(String s){
|
||||
printOrBuffer(s+"\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* NOT TO BE USED!!!!
|
||||
* use printOrBuffer(String s) instead
|
||||
*/
|
||||
@Deprecated
|
||||
public void print(String s){
|
||||
super.print(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* prints to all
|
||||
*/
|
||||
public void printOrBuffer(String s){
|
||||
if(buffer_enabled){
|
||||
buffer.append(s);
|
||||
}
|
||||
else{
|
||||
if(cookie != null){
|
||||
for(String key : cookie.keySet()){
|
||||
super.println("Set-Cookie: "+key+"="+cookie.get(key)+"; ");
|
||||
}
|
||||
super.println(" \n");
|
||||
cookie = null;
|
||||
}
|
||||
super.print(s);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends out all the buffer and clears it
|
||||
*/
|
||||
public void flush(){
|
||||
if(buffer_enabled){
|
||||
buffer_enabled = false;
|
||||
printOrBuffer(buffer.toString());
|
||||
buffer.delete(0, buffer.length());
|
||||
buffer_enabled = true;
|
||||
}
|
||||
super.flush();
|
||||
}
|
||||
|
||||
public void close(){
|
||||
flush();
|
||||
super.close();
|
||||
}
|
||||
|
||||
public void println(){ println("");}
|
||||
public void println(boolean x){ println(""+x);}
|
||||
public void println(char x){ println(""+x);}
|
||||
public void println(char[] x){ println(new String(x));}
|
||||
public void println(double x){ println(""+x);}
|
||||
public void println(float x){ println(""+x);}
|
||||
public void println(int x){ println(""+x);}
|
||||
public void println(long x){ println(""+x);}
|
||||
public void println(Object x){ println(""+x);}
|
||||
|
||||
public void print(boolean x){ printOrBuffer(""+x);}
|
||||
public void print(char x){ printOrBuffer(""+x);}
|
||||
public void print(char[] x){ printOrBuffer(new String(x));}
|
||||
public void print(double x){ printOrBuffer(""+x);}
|
||||
public void print(float x){ printOrBuffer(""+x);}
|
||||
public void print(int x){ printOrBuffer(""+x);}
|
||||
public void print(long x){ printOrBuffer(""+x);}
|
||||
public void print(Object x){ printOrBuffer(""+x);}
|
||||
}
|
||||
282
src/zutil/network/http/HttpServer.java
Normal file
282
src/zutil/network/http/HttpServer.java
Normal file
|
|
@ -0,0 +1,282 @@
|
|||
package zutil.network.http;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.util.HashMap;
|
||||
|
||||
import zutil.MultiPrintStream;
|
||||
|
||||
/**
|
||||
* A simple web server that handles both cookies and
|
||||
* sessions for all the clients
|
||||
*
|
||||
* @author Ziver
|
||||
* TODO: File upload
|
||||
*/
|
||||
public class HttpServer extends Thread{
|
||||
public static final boolean DEBUG = false;
|
||||
public static final String SERVER_VERSION = "Evil HttpServer 1.0";
|
||||
public static final int COOKIE_TTL = 200;
|
||||
public static final int SESSION_TTL = 200;
|
||||
|
||||
public final String server_url;
|
||||
public final int server_port;
|
||||
|
||||
private HashMap<String,HttpPage> pages;
|
||||
private HttpPage defaultPage;
|
||||
private HashMap<String,HashMap<String,String>> sessions;
|
||||
private int nextSessionId;
|
||||
|
||||
/**
|
||||
* Creates a new instance of the sever
|
||||
*
|
||||
* @param url The address to the server
|
||||
* @param port The port that the server should listen to
|
||||
*/
|
||||
public HttpServer(String url, int port){
|
||||
this.server_url = url;
|
||||
this.server_port = port;
|
||||
|
||||
pages = new HashMap<String,HttpPage>();
|
||||
sessions = new HashMap<String,HashMap<String,String>>();
|
||||
nextSessionId = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a HttpPage to a specific URL
|
||||
*
|
||||
* @param name The URL or name of the page
|
||||
* @param page The page itself
|
||||
*/
|
||||
public void setPage(String name, HttpPage page){
|
||||
pages.put(name, page);
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a default page that will be shown
|
||||
* if there is no other matching page,
|
||||
*
|
||||
* @param page The HttpPage that will be shown
|
||||
*/
|
||||
public void setDefaultPage(HttpPage page){
|
||||
defaultPage = page;
|
||||
}
|
||||
|
||||
public void run(){
|
||||
try{
|
||||
ServerSocket ss = new ServerSocket(server_port);
|
||||
MultiPrintStream.out.println("Http Server Running!!!");
|
||||
|
||||
while(true){
|
||||
new HttpServerThread(ss.accept());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal class that handles all the requests
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
class HttpServerThread extends Thread{
|
||||
private HttpPrintStream out;
|
||||
private BufferedReader in;
|
||||
private Socket socket;
|
||||
|
||||
public HttpServerThread(Socket socket) throws IOException{
|
||||
out = new HttpPrintStream(socket.getOutputStream());
|
||||
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
this.socket = socket;
|
||||
start();
|
||||
if(DEBUG)MultiPrintStream.out.println("New Connection!!! "+socket.getInetAddress().getHostName());
|
||||
}
|
||||
|
||||
public void run(){
|
||||
String tmp = null;
|
||||
int tmpi;
|
||||
|
||||
String page_url = "";
|
||||
HashMap<String,String> client_info = new HashMap<String,String>();
|
||||
HashMap<String,String> cookie = new HashMap<String,String>();
|
||||
HashMap<String,String> request = new HashMap<String,String>();
|
||||
|
||||
//**************************** REQUEST *********************************
|
||||
try {
|
||||
if(DEBUG)MultiPrintStream.out.println("Reciving Http Request!!!");
|
||||
while(!(tmp=in.readLine()).isEmpty()){
|
||||
//System.err.println(tmp);
|
||||
//*********** Handling Get variables
|
||||
if(tmp.startsWith("GET")){
|
||||
// Gets the file URL and get values
|
||||
tmp = (tmp.substring(5, tmp.indexOf("HTTP/"))).trim();
|
||||
page_url = parseHttpHeader(tmp, request);
|
||||
}
|
||||
//********* Handling Post variable data
|
||||
else if(tmp.startsWith("POST")){
|
||||
// Gets the file URL and get values
|
||||
tmp = (tmp.substring(6, tmp.indexOf("HTTP/"))).trim();
|
||||
page_url = parseHttpHeader(tmp, request);
|
||||
}
|
||||
//********* Handling Cookies
|
||||
else if(tmp.startsWith("Cookie")){
|
||||
tmp = tmp.substring(tmp.indexOf(':')+1, tmp.length());
|
||||
while(!tmp.isEmpty()){
|
||||
tmpi = ( (tmpi = tmp.indexOf(';')) == -1 ? tmp.length() : tmpi);
|
||||
cookie.put(
|
||||
(tmp.substring(0, tmp.indexOf('=')).trim() ), // Key
|
||||
(tmp.substring(tmp.indexOf('=')+1, tmpi)).trim() ); //Value
|
||||
if(tmp.indexOf(';') > 0)
|
||||
tmp = tmp.substring(tmp.indexOf(';')+1, tmp.length());
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
//********* Handling Client info
|
||||
else{
|
||||
if(tmp.indexOf(':') > -1){
|
||||
client_info.put(
|
||||
(tmp.substring(0, tmp.indexOf(':')).trim() ), // Key
|
||||
(tmp.substring(tmp.indexOf(':')+1, tmp.length())).trim() ); //Value
|
||||
}
|
||||
else{
|
||||
MultiPrintStream.out.println("Faild to parsse header: "+tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//******* Read in the post data if available
|
||||
if(client_info.containsKey("Content-Length")){
|
||||
// Reads the post data size
|
||||
tmp = client_info.get("Content-Length");
|
||||
int post_data_length = Integer.parseInt(
|
||||
tmp.substring(tmp.indexOf(':')+1, tmp.length()).trim() );
|
||||
|
||||
if(client_info.get("Content-Type").equals("application/x-www-form-urlencoded")){
|
||||
StringBuffer tmpb = new StringBuffer();
|
||||
// read the data
|
||||
for(int i=0; i<post_data_length ;i++){
|
||||
tmpb.append((char)in.read());
|
||||
}
|
||||
// get the variables
|
||||
parseVariables(tmpb.toString(), request);
|
||||
}
|
||||
else if(client_info.get("Content-Type").contains("multipart/form-data")){
|
||||
// TODO:
|
||||
throw new Exception("\"multipart/form-data\" Not implemented!!!");
|
||||
}
|
||||
}
|
||||
//*****************
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
try {
|
||||
out.sendHeader("HTTP/1.0 500 ERROR");
|
||||
} catch (Exception e1) {}
|
||||
out.println("500 Internal Error(Header: "+tmp+"): "+e.getMessage());
|
||||
}
|
||||
try {
|
||||
//**************************** HANDLE REQUEST *********************************
|
||||
// Get the client session or create one
|
||||
HashMap<String,String> client_session;
|
||||
if(cookie.containsKey("session_id") && sessions.containsKey(cookie.get("session_id"))){
|
||||
client_session = sessions.get(cookie.get("session_id"));
|
||||
}
|
||||
else{
|
||||
client_session = new HashMap<String,String>();
|
||||
client_session.put("session_id", ""+nextSessionId);
|
||||
sessions.put(""+nextSessionId, client_session);
|
||||
nextSessionId++;
|
||||
}
|
||||
// Debug
|
||||
if(DEBUG){
|
||||
MultiPrintStream.out.println("# page_url: "+page_url);
|
||||
MultiPrintStream.out.println("# cookie: "+cookie);
|
||||
MultiPrintStream.out.println("# client_session: "+client_session);
|
||||
MultiPrintStream.out.println("# client_info: "+client_info);
|
||||
MultiPrintStream.out.println("# request: "+request);
|
||||
}
|
||||
//**************************** RESPONSE ************************************
|
||||
if(DEBUG)MultiPrintStream.out.println("Sending Http Response!!!");
|
||||
out.sendHeader("HTTP/1.0 200 OK");
|
||||
out.sendHeader("Server: "+SERVER_VERSION);
|
||||
out.sendHeader("Content-Type: text/html");
|
||||
out.setCookie("session_id", client_session.get("session_id"));
|
||||
|
||||
|
||||
if(!page_url.isEmpty() && pages.containsKey(page_url)){
|
||||
pages.get(page_url).respond(out, client_info, client_session, cookie, request);
|
||||
}
|
||||
else if(defaultPage != null){
|
||||
defaultPage.respond(out, client_info, client_session, cookie, request);
|
||||
}
|
||||
else{
|
||||
out.println("404 ERROR");
|
||||
}
|
||||
|
||||
//********************************************************************************
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
out.println("500 Internal Error: "+e.getMessage());
|
||||
}
|
||||
|
||||
try{
|
||||
if(DEBUG)MultiPrintStream.out.println("Conection Closed!!!");
|
||||
out.close();
|
||||
in.close();
|
||||
socket.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the first header line and ads the values to
|
||||
* the map and returns the file name and path
|
||||
*
|
||||
* @param header The header String
|
||||
* @param map The HashMap to put the variables to
|
||||
* @return The path and file name as a String
|
||||
*/
|
||||
private String parseHttpHeader(String header, HashMap<String, String> map){
|
||||
String page_url = "";
|
||||
// cut out the page name
|
||||
if(header.indexOf('?') > -1){
|
||||
page_url = header.substring(0, header.indexOf('?'));
|
||||
header = header.substring(header.indexOf('?')+1, header.length());
|
||||
parseVariables(header, map);
|
||||
}
|
||||
else{
|
||||
page_url = header;
|
||||
}
|
||||
|
||||
return page_url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a String with variables from a get or post
|
||||
* from a client and puts the data into a HashMap
|
||||
*
|
||||
* @param header A String with all the variables
|
||||
* @param map The HashMap to put all the variables into
|
||||
*/
|
||||
private void parseVariables(String header, HashMap<String, String> map){
|
||||
int tmpi;
|
||||
// get the variables
|
||||
while(!header.isEmpty()){
|
||||
tmpi = ( (tmpi = header.indexOf('&')) == -1 ? header.length() : tmpi);
|
||||
map.put(
|
||||
(header.substring(0, header.indexOf('=')).trim() ), // Key
|
||||
(header.substring(header.indexOf('=')+1, tmpi )).trim() ); //Value
|
||||
if(header.indexOf('&') > 0)
|
||||
header = header.substring(header.indexOf('&')+1, header.length());
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
57
src/zutil/network/nio/NioClient.java
Normal file
57
src/zutil/network/nio/NioClient.java
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
package zutil.network.nio;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.channels.Selector;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.nio.channels.spi.SelectorProvider;
|
||||
|
||||
import zutil.network.nio.message.Message;
|
||||
import zutil.network.nio.message.type.ResponseRequestMessage;
|
||||
import zutil.network.nio.response.ResponseEvent;
|
||||
|
||||
|
||||
public class NioClient extends NioNetwork{
|
||||
private SocketChannel serverSocket;
|
||||
|
||||
/**
|
||||
* Creates a NioClient that connects to a server
|
||||
*
|
||||
* @param hostAddress The server address
|
||||
* @param port The port to listen on
|
||||
*/
|
||||
public NioClient(InetAddress serverAddress, int port) throws IOException {
|
||||
super(InetAddress.getLocalHost(), port, NetworkType.CLIENT);
|
||||
serverSocket = initiateConnection(new InetSocketAddress(serverAddress, port));
|
||||
Thread thread = new Thread(this);
|
||||
thread.setDaemon(false);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
protected Selector initSelector() throws IOException {
|
||||
// Create a new selector
|
||||
return SelectorProvider.provider().openSelector();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a Message to the default server
|
||||
*
|
||||
* @param data The data to be sent
|
||||
* @throws IOException Something got wrong
|
||||
*/
|
||||
public void send(Message data) throws IOException {
|
||||
send(serverSocket, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is for the Client to send a message to the server
|
||||
*
|
||||
* @param handler The response handler
|
||||
* @param data The data to send
|
||||
* @throws IOException
|
||||
*/
|
||||
public void send(ResponseEvent handler, ResponseRequestMessage data) throws IOException {
|
||||
send(serverSocket, handler, data);
|
||||
}
|
||||
}
|
||||
433
src/zutil/network/nio/NioNetwork.java
Normal file
433
src/zutil/network/nio/NioNetwork.java
Normal file
|
|
@ -0,0 +1,433 @@
|
|||
package zutil.network.nio;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ConnectException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.Selector;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import zutil.Converter;
|
||||
import zutil.Encrypter;
|
||||
import zutil.MultiPrintStream;
|
||||
import zutil.network.nio.message.type.ResponseRequestMessage;
|
||||
import zutil.network.nio.message.type.SystemMessage;
|
||||
import zutil.network.nio.response.ResponseEvent;
|
||||
import zutil.network.nio.server.ChangeRequest;
|
||||
import zutil.network.nio.server.ClientData;
|
||||
import zutil.network.nio.worker.SystemWorker;
|
||||
import zutil.network.nio.worker.Worker;
|
||||
|
||||
|
||||
public abstract class NioNetwork implements Runnable {
|
||||
/**
|
||||
* Debug level
|
||||
* 0 = nothing
|
||||
* 1 = connection debug
|
||||
* 2 = message debug
|
||||
* 3 = selector debug
|
||||
*/
|
||||
public static final int DEBUG = 1;
|
||||
public static enum NetworkType {SERVER, CLIENT};
|
||||
|
||||
private NetworkType type;
|
||||
|
||||
// The host:port combination to listen on
|
||||
protected InetAddress address;
|
||||
protected int port;
|
||||
|
||||
// The channel on which we'll accept connections
|
||||
protected ServerSocketChannel serverChannel;
|
||||
// The selector we'll be monitoring
|
||||
private Selector selector;
|
||||
// The buffer into which we'll read data when it's available
|
||||
private ByteBuffer readBuffer = ByteBuffer.allocate(8192);
|
||||
protected Worker worker;
|
||||
protected SystemWorker systemWorker;
|
||||
|
||||
// This map contains all the clients that are conncted
|
||||
protected Map<InetSocketAddress, ClientData> clients = new HashMap<InetSocketAddress, ClientData>();
|
||||
|
||||
// A list of PendingChange instances
|
||||
private List<ChangeRequest> pendingChanges = new LinkedList<ChangeRequest>();
|
||||
// Maps a SocketChannel to a list of ByteBuffer instances
|
||||
private Map<SocketChannel, List<ByteBuffer>> pendingData = new HashMap<SocketChannel, List<ByteBuffer>>();
|
||||
// The encrypter
|
||||
private Encrypter encrypter;
|
||||
|
||||
/**
|
||||
* Create a nio network class
|
||||
*
|
||||
* @param hostAddress The host address
|
||||
* @param port The port
|
||||
* @param type The type of network host
|
||||
* @throws IOException
|
||||
*/
|
||||
public NioNetwork(InetAddress address, int port, NetworkType type) throws IOException {
|
||||
this.port = port;
|
||||
this.address = address;
|
||||
this.type = type;
|
||||
this.selector = initSelector();
|
||||
this.systemWorker = new SystemWorker(this);
|
||||
}
|
||||
|
||||
protected abstract Selector initSelector() throws IOException;
|
||||
|
||||
/**
|
||||
* Sets the Worker for the network messages
|
||||
*
|
||||
* @param worker The worker that handles the incoming messages
|
||||
*/
|
||||
public void setDefaultWorker(Worker worker){
|
||||
this.worker = worker;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the encrypter to use in the network
|
||||
*
|
||||
* @param enc The encrypter to use or null fo no encryption
|
||||
*/
|
||||
public void setEncrypter(Encrypter enc){
|
||||
encrypter = enc;
|
||||
MultiPrintStream.out.println("Network Encryption "+
|
||||
(encrypter != null ? "Enabled("+encrypter.getAlgorithm()+")" : "Disabled")+"!!");
|
||||
}
|
||||
|
||||
public void send(SocketChannel socket, Object data) {
|
||||
send(socket, Converter.toBytes(data));
|
||||
}
|
||||
|
||||
public void send(InetSocketAddress address, Object data){
|
||||
send(address, Converter.toBytes(data));
|
||||
}
|
||||
|
||||
public void send(InetSocketAddress address, byte[] data){
|
||||
send(getSocketChannel(address), data);
|
||||
}
|
||||
|
||||
public void send(SocketChannel socket, ResponseEvent handler, ResponseRequestMessage data) throws IOException {
|
||||
// Register the response handler
|
||||
systemWorker.addResponseHandler(handler, data);
|
||||
|
||||
queueSend(socket,Converter.toBytes(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* This method sends data true the given socket
|
||||
*
|
||||
* @param socket The socket
|
||||
* @param data The data to send
|
||||
*/
|
||||
public void send(SocketChannel socket, byte[] data) {
|
||||
queueSend(socket,data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues the message to be sent and wakeups the selector
|
||||
*
|
||||
* @param socket The socet to send the message thrue
|
||||
* @param data The data to send
|
||||
*/
|
||||
protected void queueSend(SocketChannel socket, byte[] data){
|
||||
if(DEBUG>=3)MultiPrintStream.out.println("Sending Queue...");
|
||||
// And queue the data we want written
|
||||
synchronized (pendingData) {
|
||||
List<ByteBuffer> queue = pendingData.get(socket);
|
||||
if (queue == null) {
|
||||
queue = new ArrayList<ByteBuffer>();
|
||||
pendingData.put(socket, queue);
|
||||
}
|
||||
//encrypts
|
||||
if(encrypter != null)
|
||||
queue.add(ByteBuffer.wrap(encrypter.encrypt(data)));
|
||||
else queue.add(ByteBuffer.wrap(data));
|
||||
}
|
||||
// Changing the key state to write
|
||||
synchronized (pendingChanges) {
|
||||
// Indicate we want the interest ops set changed
|
||||
pendingChanges.add(new ChangeRequest(socket, ChangeRequest.CHANGEOPS, SelectionKey.OP_WRITE));
|
||||
}
|
||||
if(DEBUG>=3)MultiPrintStream.out.println("selector.wakeup();");
|
||||
// Finally, wake up our selecting thread so it can make the required changes
|
||||
selector.wakeup();
|
||||
}
|
||||
|
||||
public void run() {
|
||||
if(DEBUG>=1)MultiPrintStream.out.println("NioNetwork Started!!!");
|
||||
while (true) {
|
||||
try {
|
||||
// Process any pending changes
|
||||
synchronized (pendingChanges) {
|
||||
Iterator<ChangeRequest> changes = pendingChanges.iterator();
|
||||
while (changes.hasNext()) {
|
||||
ChangeRequest change = changes.next();
|
||||
switch (change.type) {
|
||||
case ChangeRequest.CHANGEOPS:
|
||||
SelectionKey key = change.socket.keyFor(selector);
|
||||
key.interestOps(change.ops);
|
||||
if(DEBUG>=3)MultiPrintStream.out.println("change.ops "+change.ops);
|
||||
break;
|
||||
case ChangeRequest.REGISTER:
|
||||
change.socket.register(selector, change.ops);
|
||||
if(DEBUG>=3)MultiPrintStream.out.println("register socket ");
|
||||
break;
|
||||
}
|
||||
}
|
||||
pendingChanges.clear();
|
||||
}
|
||||
|
||||
// Wait for an event one of the registered channels
|
||||
selector.select();
|
||||
if(DEBUG>=3)MultiPrintStream.out.println("selector is awake");
|
||||
|
||||
// Iterate over the set of keys for which events are available
|
||||
Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator();
|
||||
while (selectedKeys.hasNext()) {
|
||||
SelectionKey key = (SelectionKey) selectedKeys.next();
|
||||
selectedKeys.remove();
|
||||
if(DEBUG>=3)MultiPrintStream.out.println("KeyOP: "+key.interestOps()+" isAcceptable: "+SelectionKey.OP_ACCEPT+" isConnectable: "+SelectionKey.OP_CONNECT+" isWritable: "+SelectionKey.OP_WRITE+" isReadable: "+SelectionKey.OP_READ);
|
||||
|
||||
if (key.isValid()) {
|
||||
// Check what event is available and deal with it
|
||||
if (key.isAcceptable()) {
|
||||
if(DEBUG>=3)MultiPrintStream.out.println("Accepting Connection!!");
|
||||
accept(key);
|
||||
}
|
||||
else if (key.isConnectable()) {
|
||||
if(DEBUG>=3)MultiPrintStream.out.println("Finnishing Connection!!");
|
||||
finishConnection(key);
|
||||
}
|
||||
else if (key.isWritable()) {
|
||||
if(DEBUG>=3)MultiPrintStream.out.println("Writing");
|
||||
write(key);
|
||||
}
|
||||
else if (key.isReadable()) {
|
||||
if(DEBUG>=3)MultiPrintStream.out.println("Reading");
|
||||
read(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Server
|
||||
*/
|
||||
private void accept(SelectionKey key) throws IOException {
|
||||
// For an accept to be pending the channel must be a server socket channel.
|
||||
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
|
||||
|
||||
// Accept the connection and make it non-blocking
|
||||
SocketChannel socketChannel = serverSocketChannel.accept();
|
||||
socketChannel.socket().setReuseAddress(true);
|
||||
socketChannel.configureBlocking(false);
|
||||
|
||||
// Register the new SocketChannel with our Selector, indicating
|
||||
// we'd like to be notified when there's data waiting to be read
|
||||
socketChannel.register(selector, SelectionKey.OP_READ);
|
||||
|
||||
// adds the client to the clients list
|
||||
InetSocketAddress remoteAdr = (InetSocketAddress) socketChannel.socket().getRemoteSocketAddress();
|
||||
if(!clients.containsValue(remoteAdr)){
|
||||
clients.put(remoteAdr, new ClientData(socketChannel));
|
||||
if(DEBUG>=1)MultiPrintStream.out.println("New Connection("+remoteAdr+")!!! Count: "+clients.size());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Client and Server
|
||||
*/
|
||||
private void read(SelectionKey key) throws IOException {
|
||||
SocketChannel socketChannel = (SocketChannel) key.channel();
|
||||
InetSocketAddress remoteAdr = (InetSocketAddress) socketChannel.socket().getRemoteSocketAddress();
|
||||
|
||||
// Clear out our read buffer so it's ready for new data
|
||||
readBuffer.clear();
|
||||
|
||||
// Attempt to read off the channel
|
||||
int numRead;
|
||||
try {
|
||||
numRead = socketChannel.read(readBuffer);
|
||||
} catch (IOException e) {
|
||||
// The remote forcibly closed the connection, cancel
|
||||
// the selection key and close the channel.
|
||||
key.cancel();
|
||||
socketChannel.close();
|
||||
clients.remove(remoteAdr);
|
||||
if(DEBUG>=1)MultiPrintStream.out.println("Connection Forced Close("+remoteAdr+")!!! Connection Count: "+clients.size());
|
||||
if(type == NetworkType.CLIENT) throw new ConnectException("Server Closed The Connection!!!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (numRead == -1) {
|
||||
// Remote entity shut the socket down cleanly. Do the
|
||||
// same from our end and cancel the channel.
|
||||
key.channel().close();
|
||||
key.cancel();
|
||||
clients.remove(remoteAdr);
|
||||
if(DEBUG>=1)MultiPrintStream.out.println("Connection Close("+remoteAdr+")!!! Connection Count: "+clients.size());
|
||||
if(type == NetworkType.CLIENT) throw new ConnectException("Server Closed The Connection!!!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Make a correctly sized copy of the data before handing it
|
||||
// to the client
|
||||
byte[] rspByteData = new byte[numRead];
|
||||
System.arraycopy(readBuffer.array(), 0, rspByteData, 0, numRead);
|
||||
|
||||
handleRecivedMessage(socketChannel, rspByteData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Client and Server
|
||||
*/
|
||||
private void write(SelectionKey key) throws IOException {
|
||||
SocketChannel socketChannel = (SocketChannel) key.channel();
|
||||
|
||||
synchronized (pendingData) {
|
||||
List<ByteBuffer> queue = pendingData.get(socketChannel);
|
||||
if(queue == null){
|
||||
queue = new ArrayList<ByteBuffer>();
|
||||
}
|
||||
|
||||
// Write until there's not more data ...
|
||||
while (!queue.isEmpty()) {
|
||||
ByteBuffer buf = queue.get(0);
|
||||
socketChannel.write(buf);
|
||||
if (buf.remaining() > 0) {
|
||||
// ... or the socket's buffer fills up
|
||||
break;
|
||||
}
|
||||
queue.remove(0);
|
||||
}
|
||||
|
||||
if (queue.isEmpty()) {
|
||||
// We wrote away all data, so we're no longer interested
|
||||
// in writing on this socket. Switch back to waiting for
|
||||
// data.
|
||||
if(DEBUG>=3)MultiPrintStream.out.println("No more Data to write!!");
|
||||
key.interestOps(SelectionKey.OP_READ);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleRecivedMessage(SocketChannel socketChannel, byte[] rspByteData){
|
||||
//Encryption
|
||||
Object rspData;
|
||||
if(encrypter != null)
|
||||
rspData = Converter.toObject(encrypter.decrypt(rspByteData));
|
||||
else rspData = Converter.toObject(rspByteData);
|
||||
if(DEBUG>=2)MultiPrintStream.out.println("Handling incomming message...");
|
||||
|
||||
if(rspData instanceof SystemMessage){
|
||||
if(systemWorker != null){
|
||||
if(DEBUG>=3)MultiPrintStream.out.println("System Message!!!");
|
||||
systemWorker.processData(this, socketChannel, rspData);
|
||||
}
|
||||
else{
|
||||
if(DEBUG>=2)MultiPrintStream.out.println("Unhandled System Message!!!");
|
||||
}
|
||||
}
|
||||
else{
|
||||
// Hand the data off to our worker thread
|
||||
if(worker != null){
|
||||
if(DEBUG>=3)MultiPrintStream.out.println("Worker Message!!!");
|
||||
worker.processData(this, socketChannel, rspData);
|
||||
}
|
||||
else{
|
||||
if(DEBUG>=1)MultiPrintStream.out.println("Unhandled Message!!!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a socket to the server
|
||||
*/
|
||||
protected SocketChannel initiateConnection(InetSocketAddress address) throws IOException {
|
||||
// Create a non-blocking socket channel
|
||||
SocketChannel socketChannel = SocketChannel.open();
|
||||
socketChannel.socket().setReuseAddress(true);
|
||||
socketChannel.configureBlocking(false);
|
||||
if(DEBUG>=1)MultiPrintStream.out.println("Connecting to: "+address);
|
||||
|
||||
// Kick off connection establishment
|
||||
socketChannel.connect(address);
|
||||
|
||||
// Queue a channel registration since the caller is not the
|
||||
// selecting thread. As part of the registration we'll register
|
||||
// an interest in connection events. These are raised when a channel
|
||||
// is ready to complete connection establishment.
|
||||
synchronized(this.pendingChanges) {
|
||||
pendingChanges.add(new ChangeRequest(socketChannel, ChangeRequest.REGISTER, SelectionKey.OP_CONNECT));
|
||||
}
|
||||
|
||||
return socketChannel;
|
||||
}
|
||||
|
||||
protected SocketChannel getSocketChannel(InetSocketAddress address){
|
||||
return clients.get(address).getSocketChannel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Client
|
||||
*/
|
||||
private void finishConnection(SelectionKey key){
|
||||
SocketChannel socketChannel = (SocketChannel) key.channel();
|
||||
|
||||
// Finish the connection. If the connection operation failed
|
||||
// this will raise an IOException.
|
||||
try {
|
||||
socketChannel.finishConnect();
|
||||
} catch (IOException e) {
|
||||
// Cancel the channel's registration with our selector
|
||||
e.printStackTrace();
|
||||
key.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
// Register an interest in writing on this channel
|
||||
key.interestOps(SelectionKey.OP_WRITE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Client
|
||||
* @throws IOException
|
||||
*/
|
||||
protected void closeConnection(SocketChannel socketChannel) throws IOException{
|
||||
socketChannel.close();
|
||||
socketChannel.keyFor(selector).cancel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Client
|
||||
* @throws IOException
|
||||
*/
|
||||
protected void closeConnection(InetSocketAddress address) throws IOException{
|
||||
closeConnection(getSocketChannel(address));
|
||||
}
|
||||
|
||||
/*
|
||||
public void close() throws IOException{
|
||||
if(serverChannel != null){
|
||||
serverChannel.close();
|
||||
serverChannel.keyFor(selector).cancel();
|
||||
}
|
||||
selector.close();
|
||||
}*/
|
||||
|
||||
public NetworkType getType(){
|
||||
return type;
|
||||
}
|
||||
}
|
||||
68
src/zutil/network/nio/NioServer.java
Normal file
68
src/zutil/network/nio/NioServer.java
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
package zutil.network.nio;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.Selector;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
import java.nio.channels.spi.SelectorProvider;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class NioServer extends NioNetwork{
|
||||
|
||||
/**
|
||||
* Creates a NioServer object which listens on localhost
|
||||
*
|
||||
* @param port The port to listen to
|
||||
*/
|
||||
public NioServer(int port) throws IOException {
|
||||
this(null, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a NioServer object which listens to a specific address
|
||||
*
|
||||
* @param address The address to listen to
|
||||
* @param port The port to listen to
|
||||
*/
|
||||
public NioServer(InetAddress address, int port) throws IOException {
|
||||
super(address, port, NetworkType.SERVER);
|
||||
new Thread(this).start();
|
||||
}
|
||||
|
||||
protected Selector initSelector() throws IOException {
|
||||
// Create a new selector
|
||||
Selector socketSelector = SelectorProvider.provider().openSelector();
|
||||
|
||||
// Create a new non-blocking server socket channel
|
||||
serverChannel = ServerSocketChannel.open();
|
||||
serverChannel.socket().setReuseAddress(true);
|
||||
serverChannel.configureBlocking(false);
|
||||
|
||||
// Bind the server socket to the specified address and port
|
||||
InetSocketAddress isa = new InetSocketAddress(address, port);
|
||||
serverChannel.socket().bind(isa);
|
||||
|
||||
// Register the server socket channel, indicating an interest in
|
||||
// accepting new connections
|
||||
serverChannel.register(socketSelector, SelectionKey.OP_ACCEPT);
|
||||
|
||||
return socketSelector;
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts the message to all the connected clients
|
||||
*
|
||||
* @param data The data to broadcast
|
||||
*/
|
||||
public void broadcast(byte[] data){
|
||||
synchronized(clients){
|
||||
Iterator<InetSocketAddress> it = clients.keySet().iterator();
|
||||
while(it.hasNext()){
|
||||
send(it.next(), data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
45
src/zutil/network/nio/message/ChatMessage.java
Normal file
45
src/zutil/network/nio/message/ChatMessage.java
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
package zutil.network.nio.message;
|
||||
|
||||
public class ChatMessage extends Message{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static enum ChatMessageType {REGISTER, UNREGISTER, MESSAGE};
|
||||
|
||||
public ChatMessageType type;
|
||||
public String msg;
|
||||
public String room;
|
||||
|
||||
/**
|
||||
* Registers the user to the main chat
|
||||
*
|
||||
* @param name Name of user
|
||||
*/
|
||||
public ChatMessage(){
|
||||
this("", "", ChatMessageType.REGISTER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the user to the given room
|
||||
*
|
||||
* @param room The room to register to
|
||||
*/
|
||||
public ChatMessage(String room){
|
||||
this("", room, ChatMessageType.REGISTER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a message to the given room
|
||||
*
|
||||
* @param msg The message
|
||||
* @param room The room
|
||||
*/
|
||||
public ChatMessage(String msg, String room){
|
||||
this(msg, room, ChatMessageType.MESSAGE);
|
||||
}
|
||||
|
||||
public ChatMessage(String msg, String room, ChatMessageType type){
|
||||
this.msg = msg;
|
||||
this.room = room;
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
31
src/zutil/network/nio/message/GraphicsSyncMessage.java
Normal file
31
src/zutil/network/nio/message/GraphicsSyncMessage.java
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
package zutil.network.nio.message;
|
||||
|
||||
|
||||
public class GraphicsSyncMessage extends SyncMessage{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public float locX;
|
||||
public float locY;
|
||||
public float locZ;
|
||||
|
||||
public float rotX;
|
||||
public float rotY;
|
||||
public float rotZ;
|
||||
public float rotW;
|
||||
|
||||
public GraphicsSyncMessage(String id){
|
||||
this.type = MessageType.SYNC;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj){
|
||||
if(obj instanceof GraphicsSyncMessage){
|
||||
GraphicsSyncMessage tmp = (GraphicsSyncMessage)obj;
|
||||
return (tmp.locX == locX && tmp.locY == locY &&
|
||||
tmp.locZ == locZ && tmp.rotX == rotX &&
|
||||
tmp.rotY == rotY && tmp.rotZ == rotZ &&
|
||||
tmp.rotW == rotW);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
16
src/zutil/network/nio/message/KeepAliveMessage.java
Normal file
16
src/zutil/network/nio/message/KeepAliveMessage.java
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package zutil.network.nio.message;
|
||||
|
||||
import zutil.network.nio.message.type.SystemMessage;
|
||||
|
||||
/**
|
||||
* Tells the destination that the
|
||||
* source is still online
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public class KeepAliveMessage extends Message implements SystemMessage{
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
}
|
||||
9
src/zutil/network/nio/message/Message.java
Normal file
9
src/zutil/network/nio/message/Message.java
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
package zutil.network.nio.message;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class Message implements Serializable{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
|
||||
}
|
||||
34
src/zutil/network/nio/message/StringMessage.java
Normal file
34
src/zutil/network/nio/message/StringMessage.java
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
package zutil.network.nio.message;
|
||||
|
||||
import zutil.network.nio.message.type.EchoMessage;
|
||||
import zutil.network.nio.message.type.ResponseRequestMessage;
|
||||
|
||||
|
||||
|
||||
public class StringMessage extends EchoMessage implements ResponseRequestMessage{
|
||||
private static final long serialVersionUID = 1L;
|
||||
private double responseId;
|
||||
|
||||
private String msg;
|
||||
|
||||
public StringMessage(String msg){
|
||||
this.msg = msg;
|
||||
responseId = Math.random();
|
||||
}
|
||||
|
||||
public String getString(){
|
||||
return msg;
|
||||
}
|
||||
|
||||
public void setString(String msg){
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
public String toString(){
|
||||
return getString();
|
||||
}
|
||||
|
||||
public double getResponseId() {
|
||||
return responseId;
|
||||
}
|
||||
}
|
||||
13
src/zutil/network/nio/message/SyncMessage.java
Normal file
13
src/zutil/network/nio/message/SyncMessage.java
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package zutil.network.nio.message;
|
||||
|
||||
import zutil.network.nio.message.type.SystemMessage;
|
||||
|
||||
public class SyncMessage extends Message implements SystemMessage{
|
||||
private static final long serialVersionUID = 1L;
|
||||
public static enum MessageType { REQUEST_ID, NEW, REMOVE, SYNC };
|
||||
|
||||
// type of message
|
||||
public MessageType type;
|
||||
// id of the Object
|
||||
public String id;
|
||||
}
|
||||
34
src/zutil/network/nio/message/type/EchoMessage.java
Normal file
34
src/zutil/network/nio/message/type/EchoMessage.java
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
package zutil.network.nio.message.type;
|
||||
|
||||
import zutil.network.nio.message.Message;
|
||||
|
||||
/**
|
||||
* The reciver will echo out this message to the sender
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public abstract class EchoMessage extends Message implements SystemMessage{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private boolean echo;
|
||||
|
||||
public EchoMessage(){
|
||||
echo = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns if the message should be echoed
|
||||
* @return If the message should be echoed
|
||||
*/
|
||||
public boolean echo() {
|
||||
return echo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by the reciver to disable looping of the message
|
||||
*
|
||||
*/
|
||||
public void recived() {
|
||||
echo = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
package zutil.network.nio.message.type;
|
||||
|
||||
/**
|
||||
* This interface means that the sender
|
||||
* wants a reply from the destination
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public interface ResponseRequestMessage {
|
||||
|
||||
/**
|
||||
* The id of the response to identify the response event
|
||||
* @return Response id
|
||||
*/
|
||||
public double getResponseId();
|
||||
|
||||
}
|
||||
12
src/zutil/network/nio/message/type/SystemMessage.java
Normal file
12
src/zutil/network/nio/message/type/SystemMessage.java
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
package zutil.network.nio.message.type;
|
||||
|
||||
/**
|
||||
* A message that implements this will be
|
||||
* handeld internaly by the network engine
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public interface SystemMessage {
|
||||
|
||||
}
|
||||
12
src/zutil/network/nio/response/PrintRsp.java
Normal file
12
src/zutil/network/nio/response/PrintRsp.java
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
package zutil.network.nio.response;
|
||||
|
||||
import zutil.MultiPrintStream;
|
||||
|
||||
public class PrintRsp extends ResponseEvent{
|
||||
|
||||
@Override
|
||||
protected void responseEvent(Object rsp) {
|
||||
MultiPrintStream.out.println(rsp);
|
||||
}
|
||||
|
||||
}
|
||||
43
src/zutil/network/nio/response/ResponseEvent.java
Normal file
43
src/zutil/network/nio/response/ResponseEvent.java
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
package zutil.network.nio.response;
|
||||
|
||||
|
||||
public abstract class ResponseEvent {
|
||||
private Object rsp = null;
|
||||
|
||||
public synchronized boolean handleResponse(Object rsp) {
|
||||
this.rsp = rsp;
|
||||
notify();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Blocks the Thread until there is a response
|
||||
*/
|
||||
public synchronized void waitForResponse() {
|
||||
while(!gotResponse()) {
|
||||
try {
|
||||
this.wait();
|
||||
} catch (InterruptedException e) {}
|
||||
}
|
||||
|
||||
responseEvent(rsp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the response
|
||||
*/
|
||||
public void handleResponse(){
|
||||
if(gotResponse()){
|
||||
responseEvent(rsp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return If there is an response
|
||||
*/
|
||||
public boolean gotResponse(){
|
||||
return (rsp != null);
|
||||
}
|
||||
|
||||
protected abstract void responseEvent(Object rsp);
|
||||
}
|
||||
41
src/zutil/network/nio/response/ResponseHandler.java
Normal file
41
src/zutil/network/nio/response/ResponseHandler.java
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
package zutil.network.nio.response;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public abstract class ResponseHandler implements Runnable{
|
||||
private List<ResponseEvent> queue = new LinkedList<ResponseEvent>();
|
||||
|
||||
public ResponseHandler(){
|
||||
|
||||
}
|
||||
|
||||
public synchronized void addResponseEvent(ResponseEvent re){
|
||||
queue.add(re);
|
||||
notify();
|
||||
}
|
||||
|
||||
public synchronized void removeResponseEvent(ResponseEvent re){
|
||||
queue.remove(re);
|
||||
}
|
||||
|
||||
public void run() {
|
||||
while(true) {
|
||||
try {
|
||||
this.wait();
|
||||
} catch (InterruptedException e) {}
|
||||
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void update(){
|
||||
while(!queue.isEmpty()){
|
||||
queue.get(0).handleResponse();
|
||||
if(queue.get(0).gotResponse()){
|
||||
queue.remove(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
18
src/zutil/network/nio/server/ChangeRequest.java
Normal file
18
src/zutil/network/nio/server/ChangeRequest.java
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package zutil.network.nio.server;
|
||||
|
||||
import java.nio.channels.SocketChannel;
|
||||
|
||||
public class ChangeRequest {
|
||||
public static final int REGISTER = 1;
|
||||
public static final int CHANGEOPS = 2;
|
||||
|
||||
public SocketChannel socket;
|
||||
public int type;
|
||||
public int ops;
|
||||
|
||||
public ChangeRequest(SocketChannel socket, int type, int ops) {
|
||||
this.socket = socket;
|
||||
this.type = type;
|
||||
this.ops = ops;
|
||||
}
|
||||
}
|
||||
29
src/zutil/network/nio/server/ClientData.java
Normal file
29
src/zutil/network/nio/server/ClientData.java
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
package zutil.network.nio.server;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.channels.SocketChannel;
|
||||
|
||||
public class ClientData {
|
||||
private SocketChannel socketChannel;
|
||||
private long lastMessageReceived;
|
||||
|
||||
public ClientData(SocketChannel socketChannel){
|
||||
this.socketChannel = socketChannel;
|
||||
}
|
||||
|
||||
public SocketChannel getSocketChannel(){
|
||||
return socketChannel;
|
||||
}
|
||||
|
||||
public InetSocketAddress getAddress(){
|
||||
return (InetSocketAddress) socketChannel.socket().getRemoteSocketAddress();
|
||||
}
|
||||
|
||||
public void setLastMessageReceived(long time){
|
||||
lastMessageReceived = time;
|
||||
}
|
||||
|
||||
public long getLastMessageReceived(){
|
||||
return lastMessageReceived;
|
||||
}
|
||||
}
|
||||
10
src/zutil/network/nio/service/ChatListener.java
Normal file
10
src/zutil/network/nio/service/ChatListener.java
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
package zutil.network.nio.service;
|
||||
|
||||
/**
|
||||
* Tis is a listener class for new chat messages
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public interface ChatListener {
|
||||
public void messageAction(String msg, String room);
|
||||
}
|
||||
121
src/zutil/network/nio/service/ChatService.java
Normal file
121
src/zutil/network/nio/service/ChatService.java
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
package zutil.network.nio.service;
|
||||
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import zutil.MultiPrintStream;
|
||||
import zutil.network.nio.NioNetwork;
|
||||
import zutil.network.nio.message.ChatMessage;
|
||||
import zutil.network.nio.message.Message;
|
||||
|
||||
public class ChatService extends NetworkService{
|
||||
private HashMap<String,LinkedList<SocketChannel>> rooms;
|
||||
private ChatListener listener;
|
||||
|
||||
public ChatService(NioNetwork nio){
|
||||
super(nio);
|
||||
rooms = new HashMap<String,LinkedList<SocketChannel>>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(Message message, SocketChannel socket) {
|
||||
// New message
|
||||
if(message instanceof ChatMessage){
|
||||
ChatMessage chatmessage = (ChatMessage)message;
|
||||
//is this a new message
|
||||
if(chatmessage.type == ChatMessage.ChatMessageType.MESSAGE){
|
||||
// Is this the server
|
||||
if(nio.getType() == NioNetwork.NetworkType.SERVER){
|
||||
if(rooms.containsKey(chatmessage.room)){
|
||||
LinkedList<SocketChannel> tmpList = rooms.get(chatmessage.room);
|
||||
|
||||
// Broadcast the message
|
||||
for(SocketChannel s : tmpList){
|
||||
if(s.isConnected()){
|
||||
nio.send(s, chatmessage);
|
||||
}
|
||||
else{
|
||||
unRegisterUser(chatmessage.room, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(NioNetwork.DEBUG>=2)MultiPrintStream.out.println("New Chat Message: "+chatmessage.msg);
|
||||
listener.messageAction(chatmessage.msg, chatmessage.room);
|
||||
}
|
||||
// register to a room
|
||||
else if(chatmessage.type == ChatMessage.ChatMessageType.REGISTER){
|
||||
registerUser(chatmessage.room, socket);
|
||||
}
|
||||
// unregister to a room
|
||||
else if(chatmessage.type == ChatMessage.ChatMessageType.UNREGISTER){
|
||||
unRegisterUser(chatmessage.room, socket);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a user to the main room
|
||||
*
|
||||
* @param socket The socket to the user
|
||||
*/
|
||||
public void registerUser(SocketChannel socket){
|
||||
registerUser("", socket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the given user to a specific room
|
||||
*
|
||||
* @param room The room
|
||||
* @param socket The socket to the user
|
||||
*/
|
||||
public void registerUser(String room, SocketChannel socket){
|
||||
addRoom(room);
|
||||
if(NioNetwork.DEBUG>=1)MultiPrintStream.out.println("New Chat User: "+socket);
|
||||
rooms.get(room).add(socket);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a user from a room and removes the room if its empty
|
||||
*
|
||||
* @param room The room
|
||||
* @param socket The socket to the user
|
||||
*/
|
||||
public void unRegisterUser(String room, SocketChannel socket){
|
||||
if(rooms.containsKey(room)){
|
||||
if(NioNetwork.DEBUG>=1)MultiPrintStream.out.println("Remove Chat User: "+socket);
|
||||
rooms.get(room).remove(socket);
|
||||
removeRoom(room);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a room into the list
|
||||
*
|
||||
* @param room The name of the room
|
||||
*/
|
||||
public void addRoom(String room){
|
||||
if(!rooms.containsKey(room)){
|
||||
if(NioNetwork.DEBUG>=1)MultiPrintStream.out.println("New Chat Room: "+room);
|
||||
rooms.put(room, new LinkedList<SocketChannel>());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given room if its empty
|
||||
*
|
||||
* @param room The room
|
||||
*/
|
||||
public void removeRoom(String room){
|
||||
if(rooms.get(room).isEmpty()){
|
||||
if(NioNetwork.DEBUG>=1)MultiPrintStream.out.println("Remove Chat Room: "+room);
|
||||
rooms.remove(room);
|
||||
}
|
||||
}
|
||||
|
||||
public static ChatService getInstance(){
|
||||
return (ChatService)instance;
|
||||
}
|
||||
}
|
||||
25
src/zutil/network/nio/service/NetworkService.java
Normal file
25
src/zutil/network/nio/service/NetworkService.java
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
package zutil.network.nio.service;
|
||||
|
||||
import java.nio.channels.SocketChannel;
|
||||
|
||||
import zutil.network.nio.NioNetwork;
|
||||
import zutil.network.nio.message.Message;
|
||||
|
||||
public abstract class NetworkService {
|
||||
protected static NetworkService instance;
|
||||
protected NioNetwork nio;
|
||||
|
||||
public NetworkService(NioNetwork nio){
|
||||
instance = this;
|
||||
this.nio = nio;
|
||||
}
|
||||
|
||||
public abstract void handleMessage(Message message, SocketChannel socket);
|
||||
|
||||
/**
|
||||
* @return A instance of this class
|
||||
*/
|
||||
public static NetworkService getInstance(){
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
28
src/zutil/network/nio/service/sync/ObjectSync.java
Normal file
28
src/zutil/network/nio/service/sync/ObjectSync.java
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
package zutil.network.nio.service.sync;
|
||||
|
||||
import zutil.network.nio.message.SyncMessage;
|
||||
|
||||
public abstract class ObjectSync {
|
||||
public String id;
|
||||
|
||||
public ObjectSync(String id){
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends sync message if the object has bean changed
|
||||
*/
|
||||
public abstract void sendSync();
|
||||
|
||||
/**
|
||||
* Applies the SyncMessage to the object
|
||||
* @param message
|
||||
* @param object
|
||||
*/
|
||||
public abstract void syncObject(SyncMessage message);
|
||||
|
||||
/**
|
||||
* Called when the object is removed from the sync list
|
||||
*/
|
||||
public abstract void remove();
|
||||
}
|
||||
58
src/zutil/network/nio/service/sync/SyncService.java
Normal file
58
src/zutil/network/nio/service/sync/SyncService.java
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
package zutil.network.nio.service.sync;
|
||||
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.HashMap;
|
||||
|
||||
import zutil.MultiPrintStream;
|
||||
import zutil.network.nio.NioNetwork;
|
||||
import zutil.network.nio.message.Message;
|
||||
import zutil.network.nio.message.SyncMessage;
|
||||
import zutil.network.nio.service.NetworkService;
|
||||
|
||||
public class SyncService extends NetworkService{
|
||||
// list of objects to sync
|
||||
private HashMap<String, ObjectSync> sync;
|
||||
|
||||
public SyncService(NioNetwork nio){
|
||||
super(nio);
|
||||
sync = new HashMap<String, ObjectSync>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a SyncObject to the sync list
|
||||
* @param os The object to sync
|
||||
*/
|
||||
public void addSyncObject(ObjectSync os){
|
||||
sync.put(os.id, os);
|
||||
if(NioNetwork.DEBUG>=1)MultiPrintStream.out.println("New Sync object: "+os);
|
||||
}
|
||||
|
||||
public void handleMessage(Message message, SocketChannel socket){
|
||||
if(message instanceof SyncMessage){
|
||||
SyncMessage syncMessage = (SyncMessage)message;
|
||||
if(syncMessage.type == SyncMessage.MessageType.SYNC){
|
||||
ObjectSync obj = sync.get(syncMessage.id);
|
||||
if(obj != null){
|
||||
if(NioNetwork.DEBUG>=2)MultiPrintStream.out.println("Syncing Message...");
|
||||
obj.syncObject(syncMessage);
|
||||
}
|
||||
}
|
||||
else if(syncMessage.type == SyncMessage.MessageType.REMOVE){
|
||||
sync.remove(syncMessage.id).remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Syncs all the objects whit the server
|
||||
*/
|
||||
public void sync(){
|
||||
for(String id : sync.keySet()){
|
||||
sync.get(id).sendSync();
|
||||
}
|
||||
}
|
||||
|
||||
public static SyncService getInstance(){
|
||||
return (SyncService)instance;
|
||||
}
|
||||
}
|
||||
16
src/zutil/network/nio/worker/EchoWorker.java
Normal file
16
src/zutil/network/nio/worker/EchoWorker.java
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package zutil.network.nio.worker;
|
||||
|
||||
import zutil.MultiPrintStream;
|
||||
|
||||
public class EchoWorker extends ThreadedEventWorker {
|
||||
|
||||
@Override
|
||||
public void messageEvent(WorkerDataEvent dataEvent) {
|
||||
// Return to sender
|
||||
MultiPrintStream.out.println("Recived Msg: "+dataEvent.data);
|
||||
dataEvent.network.send(dataEvent.socket, dataEvent.data);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
126
src/zutil/network/nio/worker/SystemWorker.java
Normal file
126
src/zutil/network/nio/worker/SystemWorker.java
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
package zutil.network.nio.worker;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import zutil.MultiPrintStream;
|
||||
import zutil.network.nio.NioNetwork;
|
||||
import zutil.network.nio.message.ChatMessage;
|
||||
import zutil.network.nio.message.Message;
|
||||
import zutil.network.nio.message.SyncMessage;
|
||||
import zutil.network.nio.message.type.EchoMessage;
|
||||
import zutil.network.nio.message.type.ResponseRequestMessage;
|
||||
import zutil.network.nio.response.ResponseEvent;
|
||||
import zutil.network.nio.service.ChatService;
|
||||
import zutil.network.nio.service.NetworkService;
|
||||
import zutil.network.nio.service.sync.SyncService;
|
||||
|
||||
|
||||
public class SystemWorker extends ThreadedEventWorker {
|
||||
private NioNetwork nio;
|
||||
// Maps a SocketChannel to a RspHandler
|
||||
private Map<Double, ResponseEvent> rspEvents = new HashMap<Double, ResponseEvent>();
|
||||
// Difren services listening on specific messages
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<Class, NetworkService> services = new HashMap<Class, NetworkService>();
|
||||
/**
|
||||
* Creates a new SystemWorker
|
||||
* @param nio The Network
|
||||
*/
|
||||
public SystemWorker(NioNetwork nio){
|
||||
this.nio = nio;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void messageEvent(WorkerDataEvent event) {
|
||||
if(NioNetwork.DEBUG>=2)MultiPrintStream.out.println("System Message: "+event.data.getClass().getName());
|
||||
if(event.data instanceof Message){
|
||||
if(event.data instanceof EchoMessage && ((EchoMessage)event.data).echo()){
|
||||
// Echos back the recived message
|
||||
((EchoMessage)event.data).recived();
|
||||
if(NioNetwork.DEBUG>=3)MultiPrintStream.out.println("Echoing Message: "+event.data);
|
||||
nio.send(event.socket, event.data);
|
||||
}
|
||||
else if(event.data instanceof ResponseRequestMessage &&
|
||||
rspEvents.get(((ResponseRequestMessage)event.data).getResponseId()) != null){
|
||||
// Handle the response
|
||||
handleResponse(((ResponseRequestMessage)event.data).getResponseId(), event.data);
|
||||
if(NioNetwork.DEBUG>=3)MultiPrintStream.out.println("Response Request Message: "+event.data);
|
||||
}
|
||||
else{
|
||||
//Services
|
||||
if(services.containsKey(event.data.getClass()) ||
|
||||
!services.containsKey(event.data.getClass()) && defaultServices(event.data)){
|
||||
services.get(event.data.getClass()).handleMessage((Message)event.data, event.socket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a Service to a specific message
|
||||
*
|
||||
* @param c The Message class
|
||||
* @param ns The service
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void registerService(Class c, NetworkService ns){
|
||||
services.put(c, ns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a service
|
||||
*
|
||||
* @param c The class
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void unregisterService(Class c){
|
||||
services.remove(c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects a ResponseHandler to a specific message
|
||||
* @param handler The Handler
|
||||
* @param data The Message
|
||||
*/
|
||||
public void addResponseHandler(ResponseEvent handler, ResponseRequestMessage data){
|
||||
rspEvents.put(data.getResponseId(), handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Client And Server ResponseEvent
|
||||
*/
|
||||
private void handleResponse(double responseId, Object rspData){
|
||||
// Look up the handler for this channel
|
||||
ResponseEvent handler = rspEvents.get(responseId);
|
||||
// And pass the response to it
|
||||
handler.handleResponse(rspData);
|
||||
|
||||
rspEvents.remove(responseId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the default services in the engin e
|
||||
* if the message needs one of them
|
||||
*
|
||||
* @param o The message
|
||||
*/
|
||||
private boolean defaultServices(Object o){
|
||||
if(o instanceof SyncMessage){
|
||||
if(SyncService.getInstance() == null)
|
||||
registerService(o.getClass(), new SyncService(nio));
|
||||
else
|
||||
registerService(o.getClass(), SyncService.getInstance());
|
||||
return true;
|
||||
}
|
||||
else if(o instanceof ChatMessage){
|
||||
if(ChatService.getInstance() == null)
|
||||
registerService(o.getClass(), new ChatService(nio));
|
||||
else
|
||||
registerService(o.getClass(), ChatService.getInstance());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
34
src/zutil/network/nio/worker/ThreadedEventWorker.java
Normal file
34
src/zutil/network/nio/worker/ThreadedEventWorker.java
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
package zutil.network.nio.worker;
|
||||
|
||||
public abstract class ThreadedEventWorker extends Worker{
|
||||
private Thread thread;
|
||||
|
||||
public ThreadedEventWorker(){
|
||||
thread = new Thread(this);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
public void update() {
|
||||
WorkerDataEvent dataEvent;
|
||||
|
||||
while(true) {
|
||||
try{
|
||||
// Wait for data to become available
|
||||
synchronized(getEventQueue()) {
|
||||
while(getEventQueue().isEmpty()) {
|
||||
try {
|
||||
getEventQueue().wait();
|
||||
} catch (InterruptedException e) {}
|
||||
}
|
||||
dataEvent = (WorkerDataEvent) getEventQueue().remove(0);
|
||||
}
|
||||
messageEvent(dataEvent);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void messageEvent(WorkerDataEvent e);
|
||||
|
||||
}
|
||||
52
src/zutil/network/nio/worker/Worker.java
Normal file
52
src/zutil/network/nio/worker/Worker.java
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
package zutil.network.nio.worker;
|
||||
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import zutil.network.nio.NioNetwork;
|
||||
|
||||
|
||||
public abstract class Worker implements Runnable {
|
||||
private LinkedList<WorkerDataEvent> queue = new LinkedList<WorkerDataEvent>();
|
||||
|
||||
public void processData(NioNetwork server, SocketChannel socket, Object data) {
|
||||
synchronized(queue) {
|
||||
queue.add(new WorkerDataEvent(server, socket, data));
|
||||
queue.notify();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The event queue
|
||||
*/
|
||||
protected List<WorkerDataEvent> getEventQueue(){
|
||||
return queue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return If there is a event in the queue
|
||||
*/
|
||||
protected boolean hasEvent(){
|
||||
return !queue.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Polls a event from the list or waits until there is a event
|
||||
* @return The next event
|
||||
*/
|
||||
protected WorkerDataEvent pollEvent(){
|
||||
while(queue.isEmpty()) {
|
||||
try {
|
||||
this.wait();
|
||||
} catch (InterruptedException e) {}
|
||||
}
|
||||
return queue.poll();
|
||||
}
|
||||
|
||||
public void run(){
|
||||
update();
|
||||
}
|
||||
|
||||
public abstract void update();
|
||||
}
|
||||
18
src/zutil/network/nio/worker/WorkerDataEvent.java
Normal file
18
src/zutil/network/nio/worker/WorkerDataEvent.java
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package zutil.network.nio.worker;
|
||||
|
||||
import java.nio.channels.SocketChannel;
|
||||
|
||||
import zutil.network.nio.NioNetwork;
|
||||
|
||||
|
||||
public class WorkerDataEvent {
|
||||
public NioNetwork network;
|
||||
public SocketChannel socket;
|
||||
public Object data;
|
||||
|
||||
public WorkerDataEvent(NioNetwork server, SocketChannel socket, Object data) {
|
||||
this.network = server;
|
||||
this.socket = socket;
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue