Fabrice E Iyolo

Turn coke into code


Chat application protocol with coffeescript

This application uses node.js to implements a client and server sockets in order to pass messages to each other.
Basically, the server is listenning to a given port and each client request for a connection. Once the connection granted, the client is asked to provide a username which will be used during the chat. The username is checked according normal rules.


The server basically broadcasts all messages sent to clients to the remaining list of clients.

# Each client must run this file

net = require 'net'
util = require("util")
EventEmitter = require("events").EventEmitter
stdin = process.stdin
stdin.setEncoding 'utf8'

domain = 'localhost'

# Can be changed but must keep the same port that the one the server is listenning to
port = 9001

# This function tries to ping the server
ping = (socket, delay) ->
	console.log "Pinging server"
	socket.write "Ping"
	next_ping = -> ping(socket, delay)
	setTimeout next_ping, delay

# This function sends the data to the server
sendMsg = (socket, data) ->
	socket.write(data)
		
# Request a connection to the server
connection = net.createConnection port, domain

# This function listen to the event connect from the server
connection.on 'connect', () ->
	console.log "Opened connection to #{domain}:#{port}"
	stdin = process.openStdin()

#Process data after the press to the key "Enter"
stdin.on 'data', (data) ->
	sendMsg	connection, data	

#Displays data whenever received from the server
connection.on 'data', (data) ->
	console.log "Received #{data}"

#Exit the process when the connection to the server is closed
connection.on 'end', (data) ->
	console.log "Connection closed"
	process.exit()

The client, once connected, can send messages to all others connected clients by sending it to the server.

# This file is maintained in the server side...

net = require 'net'
util = require("util")
EventEmitter = require("events").EventEmitter
stdin = process.stdin
stdin.setEncoding 'utf8'

#The domain and the port that the server will be listenning to
domain = '0.0.0.0'
port = 9001

# This function looks for unallowed characters in the username

checkUsername = (username) ->
  return false unless username.replace(/[A-Za-z0-9]*/, "") is ""
  for used_name of @chatters
    return false if used_name is username
  true
 
#This class handles all the server functionnalities

class myServer
  constructor: ->
    @chatters = {}
    @server = net.createServer @receiveConnection
    @server.listen port, domain
 
 #this method receives a new request for connection and perfom an action accordingly
 
  receiveConnection: (connection) =>
    console.log "Incoming connection from " + connection.remoteAddress
    connection.setEncoding "utf8"
    chatter = new Chatter(connection, this)
    chatter.on "chat", @clientChat
    chatter.on "join", @clientJoin
    chatter.on "leave", @clientExit
 
 # This method alert all connected chatters than a New chatter is connected
  clientChat: (chatter, message) =>
    @broadcastToAllClientsExcept chatter, chatter.username + ": " + message
 
 # This function add the new user in the list of users and broadcast a message to all users
  clientJoin: (chatter) =>
    console.log chatter.username + " has joined the chat."
    @broadcastToAllClients chatter.username + " has joined the chat."
    @newUser chatter
 
 
 #This function handle the client's leave.  So it removes the client from the user's list and broadcast a message to all the remaining users.
  clientExit: (chatter) =>
    console.log chatter.username + " has left the chat."
    @removeChatter chatter
    @broadcastToAllClients chatter.username + " has left the chat."
 
 # This function add a new user in the userlist
  newUser: (chatter) =>
    @chatters[chatter.username] = chatter
 
 # This function removes a user from the userlist.
  removeUser: (chatter) =>
    delete @chatters[chatter.username]
 
 #This is the broadcasting function. Takes a message as input and broadcast it to all connected users.
  broadcastToAllClients: (data) =>
    for username of @chatters
      @chatters[username].send data
 
 # This function broadcast a message to all users except a specified one, which is not concerned.
  broadcastToAllClientsExcept: (chatter, data) =>
    for username of @chatters
      @chatters[username].send data  unless username is chatter.username
 

# This class create the client (Chatter) object defining its properties (username, socket, etc..)
 
class Chatter extends EventEmitter
  constructor: (socket, server) ->
    EventEmitter.call this
    @socket = socket
    @server = server
    @username = ""
    @lineBuffer = new SocketLineBuffer(socket)
    @lineBuffer.on "line", @manageusername
    @socket.on "close", @manageDisconnect
    @send "Welcome! What is your username?"
	
# This function handle the user's username and emit the event join
 
  manageusername: (username) =>
    if checkUsername(username)
      @username = username
      @lineBuffer.removeAllListeners "line"
      @lineBuffer.on "line", @clientChat
      @send "Welcome to the chat, " + username + "!"
      @emit "join", this
    else
      @send "Sorry, but that username is not allowed or is already in use!"
      @send "What is your username?"
	  
# This function emit the event chat to the server
 
  clientChat: (line) =>
    @emit "chat", this, line
 
 
 # This function emit the event leave to the server, which the appropriate function in the server class
  manageDisconnect: =>
    @emit "leave", this
 
 # This function write a message in the client console, the client is specified by its socket
  send: (data) =>
    @socket.write data + "\r\n"
 
 
 
class SocketLineBuffer extends EventEmitter
  constructor: (socket) ->
    EventEmitter.call this
    @socket = socket
    @buffer = ""
    @socket.on "data", @manageData
 
 # This function get the data, emit the event line
  manageData: (data) =>
    console.log "Handling data", data
    i = 0
    @buffer = ""

    while i < data.length
      char = data.charAt(i)
      @buffer += char
      if char is "\n"
        @buffer = @buffer.replace("\r\n", "")
        @buffer = @buffer.replace("\n", "")
        @emit "line", @buffer
        console.log "incoming line: #{@buffer}"
        @buffer = ""
      i++
 
 # This instruction actually starts the server then wait for clients to connect.
 
server = new myServer()

This applications uses the net module which provides with an asynchronous network wrapper. It contains methods for creating both servers and clients (called streams).

You can find more information on the node.js net module api here

To run this application, you have to install node js and coffee script

More information on how to install node js here

More information on how to install coffeescript here

Enjoy it.

Screen of the server running on a console

Screen of the client running on a console

 

 

A propos

Je suis techniquement fort, expérimenté, pratique et curieux. Je travaille depuis plus de 15 ans dans le domaine de technologies de l'information. Je suis passionné de technologie et de ses applications. J'ai des connaissances et des compétences étendues et approfondies dans les domaines de la cloud computing, des outils, de l'automatisation et de la business intelligence.

Contact