CDE
This commit is contained in:
parent
518da4a923
commit
764da6cbbb
6144 changed files with 83610 additions and 11 deletions
1
Task/Chat-server/0DESCRIPTION
Normal file
1
Task/Chat-server/0DESCRIPTION
Normal file
|
|
@ -0,0 +1 @@
|
|||
Write a server for a minimal text based chat. People should be able to connect via ‘telnet’, sign on with a nickname, and type messages which will then be seen by all other connected users. Arrivals and departures of chat members should generate appropriate notification messages.
|
||||
2
Task/Chat-server/1META.yaml
Normal file
2
Task/Chat-server/1META.yaml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
---
|
||||
note: Networking and Web Interaction
|
||||
85
Task/Chat-server/Ada/chat-server.ada
Normal file
85
Task/Chat-server/Ada/chat-server.ada
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
with Ada.Containers.Vectors;
|
||||
with Ada.Command_Line; use Ada.Command_Line;
|
||||
with Ada.Exceptions; use Ada.Exceptions;
|
||||
with Ada.Text_IO; use Ada.Text_IO;
|
||||
with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
|
||||
with Sockets; use Sockets;
|
||||
|
||||
procedure Chat_Server is
|
||||
|
||||
package Client_Vectors is new Ada.Containers.Vectors
|
||||
(Element_Type => Socket_FD, Index_Type => Positive);
|
||||
All_Clients : Client_Vectors.Vector;
|
||||
|
||||
procedure Write (S : String) is
|
||||
procedure Output (Position : Client_Vectors.Cursor) is
|
||||
Sock : Socket_FD := Client_Vectors.Element (Position);
|
||||
begin
|
||||
Put_Line (Sock, S);
|
||||
end Output;
|
||||
begin
|
||||
All_Clients.Iterate (Output'Access);
|
||||
end Write;
|
||||
|
||||
task type Client_Task is
|
||||
entry Start (FD : Socket_FD);
|
||||
end Client_Task;
|
||||
|
||||
task body Client_Task is
|
||||
Sock : Socket_FD;
|
||||
Sock_ID : Positive;
|
||||
Name : Unbounded_String;
|
||||
begin
|
||||
select
|
||||
accept Start (FD : Socket_FD) do
|
||||
Sock := FD;
|
||||
end Start;
|
||||
or
|
||||
terminate;
|
||||
end select;
|
||||
|
||||
while Name = Null_Unbounded_String loop
|
||||
Put (Sock, "Enter Name:");
|
||||
Name := To_Unbounded_String (Get_Line (Sock));
|
||||
end loop;
|
||||
Write (To_String (Name) & " joined.");
|
||||
All_Clients.Append (Sock);
|
||||
Sock_ID := All_Clients.Find_Index (Sock);
|
||||
loop
|
||||
declare
|
||||
Input : String := Get_Line (Sock);
|
||||
begin
|
||||
Write (To_String (Name) & ": " & Input);
|
||||
end;
|
||||
end loop;
|
||||
exception
|
||||
when Connection_Closed =>
|
||||
Put_Line ("Connection closed");
|
||||
Shutdown (Sock, Both);
|
||||
All_Clients.Delete (Sock_ID);
|
||||
Write (To_String (Name) & " left.");
|
||||
end Client_Task;
|
||||
|
||||
Accepting_Socket : Socket_FD;
|
||||
Incoming_Socket : Socket_FD;
|
||||
|
||||
type Client_Access is access Client_Task;
|
||||
Dummy : Client_Access;
|
||||
begin
|
||||
if Argument_Count /= 1 then
|
||||
Raise_Exception (Constraint_Error'Identity,
|
||||
"Usage: " & Command_Name & " port");
|
||||
end if;
|
||||
Socket (Accepting_Socket, PF_INET, SOCK_STREAM);
|
||||
Setsockopt (Accepting_Socket, SOL_SOCKET, SO_REUSEADDR, 1);
|
||||
Bind (Accepting_Socket, Positive'Value (Argument (1)));
|
||||
Listen (Accepting_Socket);
|
||||
loop
|
||||
Put_Line ("Waiting for new connection");
|
||||
Accept_Socket (Accepting_Socket, Incoming_Socket);
|
||||
Put_Line ("New connection acknowledged");
|
||||
|
||||
Dummy := new Client_Task;
|
||||
Dummy.Start (Incoming_Socket);
|
||||
end loop;
|
||||
end Chat_Server;
|
||||
248
Task/Chat-server/C/chat-server.c
Normal file
248
Task/Chat-server/C/chat-server.c
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/select.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/ip.h>
|
||||
|
||||
int tsocket;
|
||||
struct sockaddr_in tsockinfo;
|
||||
fd_set status, current;
|
||||
void ClientText(int handle, char *buf, int buf_len);
|
||||
|
||||
struct client
|
||||
{
|
||||
char buffer[4096];
|
||||
int pos;
|
||||
char name[32];
|
||||
} *connections[FD_SETSIZE];
|
||||
|
||||
void AddConnection(int handle)
|
||||
{
|
||||
connections[handle] = malloc(sizeof(struct client));
|
||||
connections[handle]->buffer[0] = '\0';
|
||||
connections[handle]->pos = 0;
|
||||
connections[handle]->name[0] = '\0';
|
||||
}
|
||||
|
||||
void CloseConnection(int handle)
|
||||
{
|
||||
char buf[512];
|
||||
int j;
|
||||
|
||||
FD_CLR(handle, &status);
|
||||
|
||||
if (connections[handle]->name[0])
|
||||
{
|
||||
sprintf(buf, "* Disconnected: %s\r\n", connections[handle]->name);
|
||||
|
||||
for (j = 0; j < FD_SETSIZE; j++)
|
||||
{
|
||||
if (handle != j && j != tsocket && FD_ISSET(j, &status))
|
||||
{
|
||||
if (write(j, buf, strlen(buf)) < 0)
|
||||
{
|
||||
CloseConnection(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else
|
||||
{
|
||||
printf ("-- Connection %d disconnected\n", handle);
|
||||
}
|
||||
if (connections[handle])
|
||||
{
|
||||
free(connections[handle]);
|
||||
}
|
||||
close(handle);
|
||||
}
|
||||
|
||||
void strip(char *buf)
|
||||
{
|
||||
char *x;
|
||||
|
||||
x = strchr(buf, '\n');
|
||||
if (x) { *x='\0'; }
|
||||
x = strchr(buf, '\r');
|
||||
if (x) { *x='\0'; }
|
||||
}
|
||||
|
||||
int RelayText(int handle)
|
||||
{
|
||||
char *begin, *end;
|
||||
int ret = 0;
|
||||
begin = connections[handle]->buffer;
|
||||
if (connections[handle]->pos == 4000)
|
||||
{
|
||||
if (begin[3999] != '\n')
|
||||
begin[4000] = '\0';
|
||||
else {
|
||||
begin[4000] = '\n';
|
||||
begin[4001] = '\0';
|
||||
}
|
||||
} else {
|
||||
begin[connections[handle]->pos] = '\0';
|
||||
}
|
||||
|
||||
end = strchr(begin, '\n');
|
||||
while (end != NULL)
|
||||
{
|
||||
char output[8000];
|
||||
output[0] = '\0';
|
||||
if (!connections[handle]->name[0])
|
||||
{
|
||||
strncpy(connections[handle]->name, begin, 31);
|
||||
connections[handle]->name[31] = '\0';
|
||||
|
||||
strip(connections[handle]->name);
|
||||
sprintf(output, "* Connected: %s\r\n", connections[handle]->name);
|
||||
ret = 1;
|
||||
} else
|
||||
{
|
||||
sprintf(output, "%s: %.*s\r\n", connections[handle]->name,
|
||||
end-begin, begin);
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
if (output[0])
|
||||
{
|
||||
int j;
|
||||
for (j = 0; j < FD_SETSIZE; j++)
|
||||
{
|
||||
if (handle != j && j != tsocket && FD_ISSET(j, &status))
|
||||
{
|
||||
if (write(j, output, strlen(output)) < 0)
|
||||
{
|
||||
CloseConnection(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
begin = end+1;
|
||||
end = strchr(begin, '\n');
|
||||
}
|
||||
|
||||
strcpy(connections[handle]->buffer, begin);
|
||||
connections[handle]->pos -= begin - connections[handle]->buffer;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ClientText(int handle, char *buf, int buf_len)
|
||||
{
|
||||
int i, j;
|
||||
if (!connections[handle])
|
||||
return;
|
||||
j = connections[handle]->pos;
|
||||
|
||||
for (i = 0; i < buf_len; ++i, ++j)
|
||||
{
|
||||
connections[handle]->buffer[j] = buf[i];
|
||||
|
||||
if (j == 4000)
|
||||
{
|
||||
while (RelayText(handle));
|
||||
j = connections[handle]->pos;
|
||||
}
|
||||
}
|
||||
connections[handle]->pos = j;
|
||||
|
||||
while (RelayText(handle));
|
||||
}
|
||||
|
||||
|
||||
int ChatLoop()
|
||||
{
|
||||
int i, j;
|
||||
FD_ZERO(&status);
|
||||
|
||||
FD_SET(tsocket, &status);
|
||||
FD_SET(0, &status);
|
||||
|
||||
while(1)
|
||||
{
|
||||
current = status;
|
||||
if (select(FD_SETSIZE, ¤t, NULL, NULL, NULL)==-1)
|
||||
{
|
||||
perror("Select");
|
||||
return 0;
|
||||
}
|
||||
for (i = 0; i < FD_SETSIZE; ++i)
|
||||
{
|
||||
if (FD_ISSET(i, ¤t))
|
||||
{
|
||||
if (i == tsocket)
|
||||
{
|
||||
struct sockaddr_in cliinfo;
|
||||
socklen_t addrlen = sizeof(cliinfo);
|
||||
int handle;
|
||||
handle = accept(tsocket, &cliinfo, &addrlen);
|
||||
if (handle == -1)
|
||||
{
|
||||
perror ("Couldn't accept connection");
|
||||
} else if (handle > FD_SETSIZE)
|
||||
{
|
||||
printf ("Unable to accept new connection.\n");
|
||||
close(handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (write(handle, "Enter name: ", 12) >= 0)
|
||||
{
|
||||
printf("-- New connection %d from %s:%hu\n",
|
||||
handle,
|
||||
inet_ntoa (cliinfo.sin_addr),
|
||||
ntohs(cliinfo.sin_port));
|
||||
FD_SET(handle, &status);
|
||||
|
||||
AddConnection(handle);
|
||||
}
|
||||
}
|
||||
} /* Read data, relay to others. */
|
||||
else
|
||||
{
|
||||
char buf[512];
|
||||
int b;
|
||||
|
||||
b = read(i, buf, 500);
|
||||
if (b <= 0)
|
||||
{
|
||||
CloseConnection(i);
|
||||
}
|
||||
else
|
||||
{
|
||||
ClientText(i, buf, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} /* While 1 */
|
||||
}
|
||||
|
||||
int main (int argc, char*argv[])
|
||||
{
|
||||
tsocket = socket(PF_INET, SOCK_STREAM, 0);
|
||||
|
||||
tsockinfo.sin_family = AF_INET;
|
||||
tsockinfo.sin_port = htons(7070);
|
||||
if (argc > 1)
|
||||
{
|
||||
tsockinfo.sin_port = htons(atoi(argv[1]));
|
||||
}
|
||||
tsockinfo.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
printf ("Socket %d on port %hu\n", tsocket, ntohs(tsockinfo.sin_port));
|
||||
|
||||
if (bind(tsocket, &tsockinfo, sizeof(tsockinfo)) == -1)
|
||||
{
|
||||
perror("Couldn't bind socket");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (listen(tsocket, 10) == -1)
|
||||
{
|
||||
perror("Couldn't listen to port");
|
||||
}
|
||||
|
||||
ChatLoop();
|
||||
|
||||
return 0;
|
||||
}
|
||||
107
Task/Chat-server/CoffeeScript/chat-server.coffee
Normal file
107
Task/Chat-server/CoffeeScript/chat-server.coffee
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
net = require("net")
|
||||
sys = require("sys")
|
||||
EventEmitter = require("events").EventEmitter
|
||||
|
||||
isNicknameLegal = (nickname) ->
|
||||
return false unless nickname.replace(/[A-Za-z0-9]*/, "") is ""
|
||||
for used_nick of @chatters
|
||||
return false if used_nick is nickname
|
||||
true
|
||||
|
||||
class ChatServer
|
||||
constructor: ->
|
||||
@chatters = {}
|
||||
@server = net.createServer @handleConnection
|
||||
@server.listen 1212, "localhost"
|
||||
|
||||
handleConnection: (connection) =>
|
||||
console.log "Incoming connection from " + connection.remoteAddress
|
||||
connection.setEncoding "utf8"
|
||||
chatter = new Chatter(connection, this)
|
||||
chatter.on "chat", @handleChat
|
||||
chatter.on "join", @handleJoin
|
||||
chatter.on "leave", @handleLeave
|
||||
|
||||
handleChat: (chatter, message) =>
|
||||
@sendToEveryChatterExcept chatter, chatter.nickname + ": " + message
|
||||
|
||||
handleJoin: (chatter) =>
|
||||
console.log chatter.nickname + " has joined the chat."
|
||||
@sendToEveryChatter chatter.nickname + " has joined the chat."
|
||||
@addChatter chatter
|
||||
|
||||
handleLeave: (chatter) =>
|
||||
console.log chatter.nickname + " has left the chat."
|
||||
@removeChatter chatter
|
||||
@sendToEveryChatter chatter.nickname + " has left the chat."
|
||||
|
||||
addChatter: (chatter) =>
|
||||
@chatters[chatter.nickname] = chatter
|
||||
|
||||
removeChatter: (chatter) =>
|
||||
delete @chatters[chatter.nickname]
|
||||
|
||||
sendToEveryChatter: (data) =>
|
||||
for nickname of @chatters
|
||||
@chatters[nickname].send data
|
||||
|
||||
sendToEveryChatterExcept: (chatter, data) =>
|
||||
for nickname of @chatters
|
||||
@chatters[nickname].send data unless nickname is chatter.nickname
|
||||
|
||||
|
||||
class Chatter extends EventEmitter
|
||||
constructor: (socket, server) ->
|
||||
EventEmitter.call this
|
||||
@socket = socket
|
||||
@server = server
|
||||
@nickname = ""
|
||||
@lineBuffer = new SocketLineBuffer(socket)
|
||||
@lineBuffer.on "line", @handleNickname
|
||||
@socket.on "close", @handleDisconnect
|
||||
@send "Welcome! What is your nickname?"
|
||||
|
||||
handleNickname: (nickname) =>
|
||||
if isNicknameLegal(nickname)
|
||||
@nickname = nickname
|
||||
@lineBuffer.removeAllListeners "line"
|
||||
@lineBuffer.on "line", @handleChat
|
||||
@send "Welcome to the chat, " + nickname + "!"
|
||||
@emit "join", this
|
||||
else
|
||||
@send "Sorry, but that nickname is not legal or is already in use!"
|
||||
@send "What is your nickname?"
|
||||
|
||||
handleChat: (line) =>
|
||||
@emit "chat", this, line
|
||||
|
||||
handleDisconnect: =>
|
||||
@emit "leave", this
|
||||
|
||||
send: (data) =>
|
||||
@socket.write data + "\r\n"
|
||||
|
||||
|
||||
class SocketLineBuffer extends EventEmitter
|
||||
constructor: (socket) ->
|
||||
EventEmitter.call this
|
||||
@socket = socket
|
||||
@buffer = ""
|
||||
@socket.on "data", @handleData
|
||||
|
||||
handleData: (data) =>
|
||||
console.log "Handling data", data
|
||||
i = 0
|
||||
|
||||
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++
|
||||
|
||||
server = new ChatServer()
|
||||
63
Task/Chat-server/Erlang/chat-server.erl
Normal file
63
Task/Chat-server/Erlang/chat-server.erl
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
-module(chat).
|
||||
|
||||
-export([start/0, start/1]).
|
||||
|
||||
-record(client, {name=none, socket=none}).
|
||||
|
||||
start() -> start(8080).
|
||||
start(Port) ->
|
||||
register(server, spawn(fun() -> server() end)),
|
||||
{ok, LSocket} = gen_tcp:listen(Port, [binary, {packet, 0}, {active, false}, {reuseaddr, true}]),
|
||||
accept(LSocket).
|
||||
|
||||
% main loop for message dispatcher
|
||||
server() -> server([]).
|
||||
server(Clients) ->
|
||||
receive
|
||||
{join, Client=#client{name = Name, socket = Socket}} ->
|
||||
self() ! {say, Socket, "has joined." ++ [10, 13]},
|
||||
server(Clients ++ [Client]);
|
||||
{leave, Socket} ->
|
||||
{value, #client{name = Name}, List} = lists:keytake(Socket, 3, Clients),
|
||||
self() ! {say, none, Message = "has left."},
|
||||
server(List);
|
||||
{say, Socket, Data} ->
|
||||
{value, #client{name = From}, List} = lists:keytake(Socket, 3, Clients),
|
||||
Message = From ++ " : " ++ Data,
|
||||
lists:map(fun(#client{socket = S}) ->
|
||||
gen_tcp:send(S, Message)
|
||||
end, List)
|
||||
end,
|
||||
server(Clients).
|
||||
|
||||
% accepts connections then spawns the client handler
|
||||
accept(LSocket) ->
|
||||
{ok, Socket} = gen_tcp:accept(LSocket),
|
||||
spawn(fun() -> connecting(Socket) end),
|
||||
accept(LSocket).
|
||||
|
||||
% when client is first connect send prompt for user name
|
||||
connecting(Socket) ->
|
||||
gen_tcp:send(Socket, "What is your name? "),
|
||||
case listen(Socket) of
|
||||
{ok, N} ->
|
||||
Name = binary_to_list(N),
|
||||
server ! {join, #client{name = lists:sublist(Name, 1, length(Name) - 2), socket = Socket} },
|
||||
client(Socket);
|
||||
_ -> ok
|
||||
end.
|
||||
|
||||
% main client loop that listens for data
|
||||
client(Socket) ->
|
||||
case listen(Socket) of
|
||||
{ok, Data} ->
|
||||
server ! {say, Socket, binary_to_list(Data)},
|
||||
client(Socket);
|
||||
_ -> server ! {leave, Socket}
|
||||
end.
|
||||
|
||||
% utility function that listens for data on a socket
|
||||
listen(Socket) ->
|
||||
case gen_tcp:recv(Socket, 0) of
|
||||
Response -> Response
|
||||
end.
|
||||
148
Task/Chat-server/Go/chat-server.go
Normal file
148
Task/Chat-server/Go/chat-server.go
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"fmt"
|
||||
"net"
|
||||
"flag"
|
||||
"bufio"
|
||||
"bytes"
|
||||
)
|
||||
|
||||
// Quick and dirty error handling.
|
||||
func error_(err error, r int) {
|
||||
fmt.Printf("Error: %v\n", err)
|
||||
|
||||
if r >= 0 {
|
||||
os.Exit(r)
|
||||
}
|
||||
}
|
||||
|
||||
// A type for storing the connections.
|
||||
type clientMap map[string]net.Conn
|
||||
|
||||
// A method that makes clientMap compatible with io.Writer, allowing it to be
|
||||
// used with fmt.Fprintf().
|
||||
func (cm clientMap) Write(buf []byte) (n int, err error) {
|
||||
for _, c := range cm {
|
||||
// Write to each client in a seperate goroutine.
|
||||
go c.Write(buf)
|
||||
}
|
||||
|
||||
n = len(buf)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Check if a name exists; if it doesn't, add it.
|
||||
func (cm clientMap) Add(name string, c net.Conn) bool {
|
||||
for k := range cm {
|
||||
if name == k {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
cm[name] = c
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// A clientMap variable.
|
||||
var clients clientMap
|
||||
|
||||
func init() {
|
||||
// Initialize the map.
|
||||
clients = make(clientMap)
|
||||
}
|
||||
|
||||
func client(c net.Conn) {
|
||||
// Close the connection when this function returns.
|
||||
defer c.Close()
|
||||
|
||||
br := bufio.NewReader(c)
|
||||
|
||||
fmt.Fprintf(c, "Please enter your name: ")
|
||||
|
||||
buf, err := br.ReadBytes('\n')
|
||||
if err != nil {
|
||||
error_(err, -1)
|
||||
return
|
||||
}
|
||||
name := string(bytes.Trim(buf, " \t\n\r\x00"))
|
||||
|
||||
if name == "" {
|
||||
fmt.Fprintf(c, "!!! %v is invalid !!!\n", name)
|
||||
}
|
||||
|
||||
// Try to add the connection to the map.
|
||||
if !clients.Add(name, c) {
|
||||
fmt.Fprintf(c, "!!! %v is not available !!!\n", name)
|
||||
return
|
||||
}
|
||||
|
||||
// Send a message telling the clients who connected.
|
||||
fmt.Fprintf(clients, "+++ %v connected +++\n", name)
|
||||
// Send a disconnected message when the function returns.
|
||||
defer fmt.Fprintf(clients, "--- %v disconnected ---\n", name)
|
||||
// Remove the client from the list.
|
||||
defer delete(clients, name)
|
||||
|
||||
for {
|
||||
buf, err = br.ReadBytes('\n')
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
buf = bytes.Trim(buf, " \t\n\r\x00")
|
||||
|
||||
// Ignore empty messages.
|
||||
if len(buf) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
switch {
|
||||
// Support for '/me' type messages.
|
||||
case string(buf[0:3]) == "/me":
|
||||
buf = append([]byte(name), buf[3:]...)
|
||||
default:
|
||||
// Prepend the user-name and '> '.
|
||||
buf = append([]byte(name+"> "), buf...)
|
||||
}
|
||||
|
||||
// Send the message to all the clients.
|
||||
fmt.Fprintf(clients, "%v\n", string(buf))
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Flags. Use -help for usage info.
|
||||
var (
|
||||
port int
|
||||
help bool
|
||||
)
|
||||
flag.IntVar(&port, "port", 23, "Port to listen on")
|
||||
flag.BoolVar(&help, "help", false, "Display this")
|
||||
flag.Parse()
|
||||
|
||||
if help {
|
||||
flag.Usage()
|
||||
return
|
||||
}
|
||||
|
||||
// Initialize a new listener.
|
||||
lis, err := net.Listen("tcp", fmt.Sprintf(":%v", port))
|
||||
if err != nil {
|
||||
error_(err, 1)
|
||||
}
|
||||
|
||||
// Begin the main loop.
|
||||
for {
|
||||
c, err := lis.Accept()
|
||||
if err != nil {
|
||||
error_(err, -1)
|
||||
continue
|
||||
}
|
||||
|
||||
// Launch a new goroutine to handle the connection.
|
||||
go client(c)
|
||||
}
|
||||
}
|
||||
152
Task/Chat-server/Java/chat-server.java
Normal file
152
Task/Chat-server/Java/chat-server.java
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.util.*;
|
||||
|
||||
public class ChatServer implements Runnable
|
||||
{
|
||||
private int port = 0;
|
||||
private List<Client> clients = new ArrayList<Client>();
|
||||
|
||||
public ChatServer(int port)
|
||||
{ this.port = port; }
|
||||
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
ServerSocket ss = new ServerSocket(port);
|
||||
while (true)
|
||||
{
|
||||
Socket s = ss.accept();
|
||||
new Thread(new Client(s)).start();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{ e.printStackTrace(); }
|
||||
}
|
||||
|
||||
private synchronized boolean registerClient(Client client)
|
||||
{
|
||||
for (Client otherClient : clients)
|
||||
if (otherClient.clientName.equalsIgnoreCase(client.clientName))
|
||||
return false;
|
||||
clients.add(client);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void deregisterClient(Client client)
|
||||
{
|
||||
boolean wasRegistered = false;
|
||||
synchronized (this)
|
||||
{ wasRegistered = clients.remove(client); }
|
||||
if (wasRegistered)
|
||||
broadcast(client, "--- " + client.clientName + " left ---");
|
||||
}
|
||||
|
||||
private synchronized String getOnlineListCSV()
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(clients.size()).append(" user(s) online: ");
|
||||
for (int i = 0; i < clients.size(); i++)
|
||||
sb.append((i > 0) ? ", " : "").append(clients.get(i).clientName);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void broadcast(Client fromClient, String msg)
|
||||
{
|
||||
// Copy client list (don't want to hold lock while doing IO)
|
||||
List<Client> clients = null;
|
||||
synchronized (this)
|
||||
{ clients = new ArrayList<Client>(this.clients); }
|
||||
for (Client client : clients)
|
||||
{
|
||||
if (client.equals(fromClient))
|
||||
continue;
|
||||
try
|
||||
{ client.write(msg + "\r\n"); }
|
||||
catch (Exception e)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
|
||||
public class Client implements Runnable
|
||||
{
|
||||
private Socket socket = null;
|
||||
private Writer output = null;
|
||||
private String clientName = null;
|
||||
|
||||
public Client(Socket socket)
|
||||
{
|
||||
this.socket = socket;
|
||||
}
|
||||
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
socket.setSendBufferSize(16384);
|
||||
socket.setTcpNoDelay(true);
|
||||
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
output = new OutputStreamWriter(socket.getOutputStream());
|
||||
write("Please enter your name: ");
|
||||
String line = null;
|
||||
while ((line = input.readLine()) != null)
|
||||
{
|
||||
if (clientName == null)
|
||||
{
|
||||
line = line.trim();
|
||||
if (line.isEmpty())
|
||||
{
|
||||
write("A name is required. Please enter your name: ");
|
||||
continue;
|
||||
}
|
||||
clientName = line;
|
||||
if (!registerClient(this))
|
||||
{
|
||||
clientName = null;
|
||||
write("Name already registered. Please enter your name: ");
|
||||
continue;
|
||||
}
|
||||
write(getOnlineListCSV() + "\r\n");
|
||||
broadcast(this, "+++ " + clientName + " arrived +++");
|
||||
continue;
|
||||
}
|
||||
if (line.equalsIgnoreCase("/quit"))
|
||||
return;
|
||||
broadcast(this, clientName + "> " + line);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{ }
|
||||
finally
|
||||
{
|
||||
deregisterClient(this);
|
||||
output = null;
|
||||
try
|
||||
{ socket.close(); }
|
||||
catch (Exception e)
|
||||
{ }
|
||||
socket = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void write(String msg) throws IOException
|
||||
{
|
||||
output.write(msg);
|
||||
output.flush();
|
||||
}
|
||||
|
||||
public boolean equals(Client client)
|
||||
{
|
||||
return (client != null) && (client instanceof Client) && (clientName != null) && (client.clientName != null) && clientName.equals(client.clientName);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args)
|
||||
{
|
||||
int port = 4004;
|
||||
if (args.length > 0)
|
||||
port = Integer.parseInt(args[0]);
|
||||
new ChatServer(port).run();
|
||||
}
|
||||
}
|
||||
158
Task/Chat-server/JavaScript/chat-server.js
Normal file
158
Task/Chat-server/JavaScript/chat-server.js
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
var net = require("net");
|
||||
var sys = require("sys");
|
||||
var EventEmitter = require("events").EventEmitter;
|
||||
|
||||
/*******************************************************************************
|
||||
* ChatServer
|
||||
*
|
||||
* Manages connections, users, and chat messages.
|
||||
******************************************************************************/
|
||||
|
||||
function ChatServer() {
|
||||
this.chatters = {};
|
||||
this.server = net.createServer(this.handleConnection.bind(this));
|
||||
this.server.listen(1212, "localhost");
|
||||
}
|
||||
|
||||
ChatServer.prototype.isNicknameLegal = function(nickname) {
|
||||
// A nickname may contain letters or numbers only,
|
||||
// and may only be used once.
|
||||
if(nickname.replace(/[A-Za-z0-9]*/, '') != "") {
|
||||
return false
|
||||
}
|
||||
for(used_nick in this.chatters) {
|
||||
if(used_nick == nickname) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
ChatServer.prototype.handleConnection = function(connection) {
|
||||
console.log("Incoming connection from " + connection.remoteAddress);
|
||||
connection.setEncoding("utf8");
|
||||
|
||||
var chatter = new Chatter(connection, this);
|
||||
chatter.on("chat", this.handleChat.bind(this));
|
||||
chatter.on("join", this.handleJoin.bind(this));
|
||||
chatter.on("leave", this.handleLeave.bind(this));
|
||||
};
|
||||
|
||||
ChatServer.prototype.handleChat = function(chatter, message) {
|
||||
this.sendToEveryChatterExcept(chatter, chatter.nickname + ": " + message);
|
||||
};
|
||||
|
||||
ChatServer.prototype.handleJoin = function(chatter) {
|
||||
console.log(chatter.nickname + " has joined the chat.");
|
||||
this.sendToEveryChatter(chatter.nickname + " has joined the chat.");
|
||||
this.addChatter(chatter);
|
||||
};
|
||||
|
||||
ChatServer.prototype.handleLeave = function(chatter) {
|
||||
console.log(chatter.nickname + " has left the chat.");
|
||||
this.removeChatter(chatter);
|
||||
this.sendToEveryChatter(chatter.nickname + " has left the chat.");
|
||||
};
|
||||
|
||||
ChatServer.prototype.addChatter = function(chatter) {
|
||||
this.chatters[chatter.nickname] = chatter;
|
||||
};
|
||||
|
||||
ChatServer.prototype.removeChatter = function(chatter) {
|
||||
delete this.chatters[chatter.nickname];
|
||||
};
|
||||
|
||||
ChatServer.prototype.sendToEveryChatter = function(data) {
|
||||
for(nickname in this.chatters) {
|
||||
this.chatters[nickname].send(data);
|
||||
}
|
||||
};
|
||||
|
||||
ChatServer.prototype.sendToEveryChatterExcept = function(chatter, data) {
|
||||
for(nickname in this.chatters) {
|
||||
if(nickname != chatter.nickname) {
|
||||
this.chatters[nickname].send(data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
* Chatter
|
||||
*
|
||||
* Represents a single user/connection in the chat server.
|
||||
******************************************************************************/
|
||||
|
||||
function Chatter(socket, server) {
|
||||
EventEmitter.call(this);
|
||||
|
||||
this.socket = socket;
|
||||
this.server = server;
|
||||
this.nickname = "";
|
||||
this.lineBuffer = new SocketLineBuffer(socket);
|
||||
|
||||
this.lineBuffer.on("line", this.handleNickname.bind(this));
|
||||
this.socket.on("close", this.handleDisconnect.bind(this));
|
||||
|
||||
this.send("Welcome! What is your nickname?");
|
||||
};
|
||||
|
||||
sys.inherits(Chatter, EventEmitter);
|
||||
|
||||
Chatter.prototype.handleNickname = function(nickname) {
|
||||
if(server.isNicknameLegal(nickname)) {
|
||||
this.nickname = nickname;
|
||||
this.lineBuffer.removeAllListeners("line");
|
||||
this.lineBuffer.on("line", this.handleChat.bind(this));
|
||||
this.send("Welcome to the chat, " + nickname + "!");
|
||||
this.emit("join", this);
|
||||
} else {
|
||||
this.send("Sorry, but that nickname is not legal or is already in use!");
|
||||
this.send("What is your nickname?");
|
||||
}
|
||||
};
|
||||
|
||||
Chatter.prototype.handleChat = function(line) {
|
||||
this.emit("chat", this, line);
|
||||
};
|
||||
|
||||
Chatter.prototype.handleDisconnect = function() {
|
||||
this.emit("leave", this);
|
||||
};
|
||||
|
||||
Chatter.prototype.send = function(data) {
|
||||
this.socket.write(data + "\r\n");
|
||||
};
|
||||
|
||||
/*******************************************************************************
|
||||
* SocketLineBuffer
|
||||
*
|
||||
* Listens for and buffers incoming data on a socket and emits a 'line' event
|
||||
* whenever a complete line is detected.
|
||||
******************************************************************************/
|
||||
|
||||
function SocketLineBuffer(socket) {
|
||||
EventEmitter.call(this);
|
||||
|
||||
this.socket = socket;
|
||||
this.buffer = "";
|
||||
|
||||
this.socket.on("data", this.handleData.bind(this));
|
||||
};
|
||||
|
||||
sys.inherits(SocketLineBuffer, EventEmitter);
|
||||
|
||||
SocketLineBuffer.prototype.handleData = function(data) {
|
||||
for(var i = 0; i < data.length; i++) {
|
||||
var char = data.charAt(i);
|
||||
this.buffer += char;
|
||||
if(char == "\n") {
|
||||
this.buffer = this.buffer.replace("\r\n", "");
|
||||
this.buffer = this.buffer.replace("\n", "");
|
||||
this.emit("line", this.buffer);
|
||||
this.buffer = "";
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Start the server!
|
||||
server = new ChatServer();
|
||||
28
Task/Chat-server/PicoLisp/chat-server.l
Normal file
28
Task/Chat-server/PicoLisp/chat-server.l
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
#!/usr/bin/picolisp /usr/lib/picolisp/lib.l
|
||||
|
||||
(de chat Lst
|
||||
(out *Sock
|
||||
(mapc prin Lst)
|
||||
(prinl) ) )
|
||||
|
||||
(setq *Port (port 4004))
|
||||
|
||||
(loop
|
||||
(setq *Sock (listen *Port))
|
||||
(NIL (fork) (close *Port))
|
||||
(close *Sock) )
|
||||
|
||||
(out *Sock
|
||||
(prin "Please enter your name: ")
|
||||
(flush) )
|
||||
(in *Sock (setq *Name (line T)))
|
||||
|
||||
(tell 'chat "+++ " *Name " arrived +++")
|
||||
|
||||
(task *Sock
|
||||
(in @
|
||||
(ifn (eof)
|
||||
(tell 'chat *Name "> " (line T))
|
||||
(tell 'chat "--- " *Name " left ---")
|
||||
(bye) ) ) )
|
||||
(wait)
|
||||
78
Task/Chat-server/Python/chat-server.py
Normal file
78
Task/Chat-server/Python/chat-server.py
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
import socket
|
||||
import thread
|
||||
import time
|
||||
|
||||
HOST = ""
|
||||
PORT = 4004
|
||||
|
||||
def accept(conn):
|
||||
"""
|
||||
Call the inner func in a thread so as not to block. Wait for a
|
||||
name to be entered from the given connection. Once a name is
|
||||
entered, set the connection to non-blocking and add the user to
|
||||
the users dict.
|
||||
"""
|
||||
def threaded():
|
||||
while True:
|
||||
conn.send("Please enter your name: ")
|
||||
try:
|
||||
name = conn.recv(1024).strip()
|
||||
except socket.error:
|
||||
continue
|
||||
if name in users:
|
||||
conn.send("Name entered is already in use.\n")
|
||||
elif name:
|
||||
conn.setblocking(False)
|
||||
users[name] = conn
|
||||
broadcast(name, "+++ %s arrived +++" % name)
|
||||
break
|
||||
thread.start_new_thread(threaded, ())
|
||||
|
||||
def broadcast(name, message):
|
||||
"""
|
||||
Send a message to all users from the given name.
|
||||
"""
|
||||
print message
|
||||
for to_name, conn in users.items():
|
||||
if to_name != name:
|
||||
try:
|
||||
conn.send(message + "\n")
|
||||
except socket.error:
|
||||
pass
|
||||
|
||||
# Set up the server socket.
|
||||
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
server.setblocking(False)
|
||||
server.bind((HOST, PORT))
|
||||
server.listen(1)
|
||||
print "Listening on %s" % ("%s:%s" % server.getsockname())
|
||||
|
||||
# Main event loop.
|
||||
users = {}
|
||||
while True:
|
||||
try:
|
||||
# Accept new connections.
|
||||
while True:
|
||||
try:
|
||||
conn, addr = server.accept()
|
||||
except socket.error:
|
||||
break
|
||||
accept(conn)
|
||||
# Read from connections.
|
||||
for name, conn in users.items():
|
||||
try:
|
||||
message = conn.recv(1024)
|
||||
except socket.error:
|
||||
continue
|
||||
if not message:
|
||||
# Empty string is given on disconnect.
|
||||
del users[name]
|
||||
broadcast(name, "--- %s leaves ---" % name)
|
||||
else:
|
||||
broadcast(name, "%s> %s" % (name, message.strip()))
|
||||
time.sleep(.1)
|
||||
except (SystemExit, KeyboardInterrupt):
|
||||
break
|
||||
68
Task/Chat-server/Ruby/chat-server.rb
Normal file
68
Task/Chat-server/Ruby/chat-server.rb
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
require 'gserver'
|
||||
|
||||
class ChatServer < GServer
|
||||
def initialize *args
|
||||
super
|
||||
|
||||
#Keep a list for broadcasting messages
|
||||
@chatters = []
|
||||
|
||||
#We'll need this for thread safety
|
||||
@mutex = Mutex.new
|
||||
end
|
||||
|
||||
#Send message out to everyone but sender
|
||||
def broadcast message, sender = nil
|
||||
#Need to use \r\n for our Windows friends
|
||||
message = message.strip << "\r\n"
|
||||
|
||||
#Mutex for safety - GServer uses threads
|
||||
@mutex.synchronize do
|
||||
@chatters.each do |chatter|
|
||||
begin
|
||||
chatter.print message unless chatter == sender
|
||||
rescue
|
||||
@chatters.delete chatter
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#Handle each connection
|
||||
def serve io
|
||||
io.print 'Name: '
|
||||
name = io.gets
|
||||
|
||||
#They might disconnect
|
||||
return if name.nil?
|
||||
|
||||
name.strip!
|
||||
|
||||
broadcast "--+ #{name} has joined +--"
|
||||
|
||||
#Add to our list of connections
|
||||
@mutex.synchronize do
|
||||
@chatters << io
|
||||
end
|
||||
|
||||
#Get and broadcast input until connection returns nil
|
||||
loop do
|
||||
message = io.gets
|
||||
|
||||
if message
|
||||
broadcast "#{name}> #{message}", io
|
||||
else
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
broadcast "--+ #{name} has left +--"
|
||||
end
|
||||
end
|
||||
|
||||
#Start up the server on port 7000
|
||||
#Accept connections for any IP address
|
||||
#Allow up to 100 connections
|
||||
#Send information to stderr
|
||||
#Turn on informational messages
|
||||
ChatServer.new(7000, '0.0.0.0', 100, $stderr, true).start.join
|
||||
61
Task/Chat-server/Tcl/chat-server.tcl
Normal file
61
Task/Chat-server/Tcl/chat-server.tcl
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
package require Tcl 8.6
|
||||
|
||||
# Write a message to everyone except the sender of the message
|
||||
proc writeEveryoneElse {sender message} {
|
||||
dict for {who ch} $::cmap {
|
||||
if {$who ne $sender} {
|
||||
puts $ch $message
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# How to read a line (up to 256 chars long) in a coroutine
|
||||
proc cgets {ch var} {
|
||||
upvar 1 $var v
|
||||
while {[gets $ch v] < 0} {
|
||||
if {[eof $ch] || [chan pending input $ch] > 256} {
|
||||
return false
|
||||
}
|
||||
yield
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
# The chatting, as seen by one user
|
||||
proc chat {ch addr port} {
|
||||
### CONNECTION CODE ###
|
||||
#Log "connection from ${addr}:${port} on channel $ch"
|
||||
fconfigure $ch -buffering none -blocking 0 -encoding utf-8
|
||||
fileevent $ch readable [info coroutine]
|
||||
global cmap
|
||||
try {
|
||||
|
||||
### GET THE NICKNAME OF THE USER ###
|
||||
puts -nonewline $ch "Please enter your name: "
|
||||
if {![cgets $ch name]} {
|
||||
return
|
||||
}
|
||||
#Log "Mapping ${addr}:${port} to ${name} on channel $ch"
|
||||
dict set cmap $name $ch
|
||||
writeEveryoneElse $name "+++ $name arrived +++"
|
||||
|
||||
### MAIN CHAT LOOP ###
|
||||
while {[cgets $ch line]} {
|
||||
writeEveryoneElse $name "$name> $line"
|
||||
}
|
||||
|
||||
} finally {
|
||||
### DISCONNECTION CODE ###
|
||||
if {[info exists name]} {
|
||||
writeEveryoneElse $name "--- $name left ---"
|
||||
dict unset cmap $name
|
||||
}
|
||||
close $ch
|
||||
#Log "disconnection from ${addr}:${port} on channel $ch"
|
||||
}
|
||||
}
|
||||
|
||||
# Service the socket by making corouines running [chat]
|
||||
socket -server {coroutine c[incr count] chat} 4004
|
||||
set ::cmap {}; # Dictionary mapping nicks to channels
|
||||
vwait forever; # Run event loop
|
||||
Loading…
Add table
Add a link
Reference in a new issue