Data commit

This commit is contained in:
Ingy döt Net 2023-07-01 11:58:00 -04:00
parent 7387c8f97b
commit cb5bb5e222
199093 changed files with 3378972 additions and 0 deletions

View file

@ -0,0 +1,3 @@
---
from: http://rosettacode.org/wiki/Chat_server
note: Networking and Web Interaction

View file

@ -0,0 +1,5 @@
;Task:
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.
<br><br>

View 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;

View file

@ -0,0 +1,35 @@
DECLARE user$ ASSOC STRING
DECLARE connect ASSOC long
OPEN "localhost:51000" FOR SERVER AS mynet
WHILE TRUE
IF WAIT(mynet, 30) THEN
fd = ACCEPT(mynet)
connect(GETPEER$(fd)) = fd
SEND "Enter your name: " TO fd
ELSE
FOR con$ IN OBTAIN$(connect)
IF WAIT(connect(con$), 10) THEN
RECEIVE in$ FROM connect(con$)
IF user$(GETPEER$(connect(con$))) = "" THEN
user$(GETPEER$(connect(con$))) = CHOP$(in$)
chat$ = chat$ & user$(GETPEER$(connect(con$))) & " joined the chat." & NL$
SEND "Welcome, " & CHOP$(in$) & "!" & NL$ TO connect(con$)
ELIF LEFT$(in$, 4) = "quit" THEN
SEND "You're disconnected!" & NL$ TO connect(con$)
chat$ = chat$ & user$(GETPEER$(connect(con$))) & " left the chat." & NL$
FREE user$(GETPEER$(connect(con$)))
FREE connect(con$)
CLOSE SERVER connect(con$)
ELIF LEFT$(in$, 4) = "say " THEN
chat$ = chat$ & user$(GETPEER$(connect(con$))) & " said: " & MID$(in$, 5)
ENDIF
ENDIF
NEXT
IF LEN(chat$) > 0 THEN
FOR con$ IN OBTAIN$(connect)
IF user$(GETPEER$(connect(con$))) <> "" THEN SEND chat$ TO connect(con$)
NEXT
chat$ = ""
ENDIF
ENDIF
WEND

View file

@ -0,0 +1,144 @@
using System;
using System.Collections.Generic;
using System.Net.Sockets;
using System.Text;
using System.Threading;
namespace ChatServer {
class State {
private TcpClient client;
private StringBuilder sb = new StringBuilder();
public string Name { get; }
public State(string name, TcpClient client) {
Name = name;
this.client = client;
}
public void Add(byte b) {
sb.Append((char)b);
}
public void Send(string text) {
var bytes = Encoding.ASCII.GetBytes(string.Format("{0}\r\n", text));
client.GetStream().Write(bytes, 0, bytes.Length);
}
}
class Program {
static TcpListener listen;
static Thread serverthread;
static Dictionary<int, State> connections = new Dictionary<int, State>();
static void Main(string[] args) {
listen = new TcpListener(System.Net.IPAddress.Parse("127.0.0.1"), 4004);
serverthread = new Thread(new ThreadStart(DoListen));
serverthread.Start();
}
private static void DoListen() {
// Listen
listen.Start();
Console.WriteLine("Server: Started server");
while (true) {
Console.WriteLine("Server: Waiting...");
TcpClient client = listen.AcceptTcpClient();
Console.WriteLine("Server: Waited");
// New thread with client
Thread clientThread = new Thread(new ParameterizedThreadStart(DoClient));
clientThread.Start(client);
}
}
private static void DoClient(object client) {
// Read data
TcpClient tClient = (TcpClient)client;
Console.WriteLine("Client (Thread: {0}): Connected!", Thread.CurrentThread.ManagedThreadId);
byte[] bytes = Encoding.ASCII.GetBytes("Enter name: ");
tClient.GetStream().Write(bytes, 0, bytes.Length);
string name = string.Empty;
bool done = false;
do {
if (!tClient.Connected) {
Console.WriteLine("Client (Thread: {0}): Terminated!", Thread.CurrentThread.ManagedThreadId);
tClient.Close();
Thread.CurrentThread.Abort(); // Kill thread.
}
name = Receive(tClient);
done = true;
if (done) {
foreach (var cl in connections) {
var state = cl.Value;
if (state.Name == name) {
bytes = Encoding.ASCII.GetBytes("Name already registered. Please enter your name: ");
tClient.GetStream().Write(bytes, 0, bytes.Length);
done = false;
}
}
}
} while (!done);
connections.Add(Thread.CurrentThread.ManagedThreadId, new State(name, tClient));
Console.WriteLine("\tTotal connections: {0}", connections.Count);
Broadcast(string.Format("+++ {0} arrived +++", name));
do {
string text = Receive(tClient);
if (text == "/quit") {
Broadcast(string.Format("Connection from {0} closed.", name));
connections.Remove(Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("\tTotal connections: {0}", connections.Count);
break;
}
if (!tClient.Connected) {
break;
}
Broadcast(string.Format("{0}> {1}", name, text));
} while (true);
Console.WriteLine("Client (Thread: {0}): Terminated!", Thread.CurrentThread.ManagedThreadId);
tClient.Close();
Thread.CurrentThread.Abort();
}
private static string Receive(TcpClient client) {
StringBuilder sb = new StringBuilder();
do {
if (client.Available > 0) {
while (client.Available > 0) {
char ch = (char)client.GetStream().ReadByte();
if (ch == '\r') {
//ignore
continue;
}
if (ch == '\n') {
return sb.ToString();
}
sb.Append(ch);
}
}
// Pause
Thread.Sleep(100);
} while (true);
}
private static void Broadcast(string text) {
Console.WriteLine(text);
foreach (var oClient in connections) {
if (oClient.Key != Thread.CurrentThread.ManagedThreadId) {
State state = oClient.Value;
state.Send(text);
}
}
}
}
}

View 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, &current, NULL, NULL, NULL)==-1)
{
perror("Select");
return 0;
}
for (i = 0; i < FD_SETSIZE; ++i)
{
if (FD_ISSET(i, &current))
{
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;
}

View 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()

View file

@ -0,0 +1,175 @@
(ql:quickload '(:usocket :simple-actors :bordeaux-threads))
(defpackage :chat-server
(:use :common-lisp :usocket :simple-actors :bordeaux-threads)
(:export :accept-connections))
(in-package :chat-server)
(defvar *whitespace* '(#\Space #\Tab #\Page #\Vt #\Newline #\Return))
(defun send-message (users from-user message)
(loop for (nil . actor) in users
do (send actor :message from-user message)))
(defun socket-format (socket format-control &rest format-arguments)
(apply #'format (socket-stream socket) format-control format-arguments)
(finish-output (socket-stream socket)))
(defvar *log* *standard-output*)
(defmacro log-errors (&body body)
`(handler-case
(progn ,@body)
(t (err)
(format *log* "Error: ~a" err))))
(defparameter *user-manager*
(let ((users nil))
(actor (action &rest args)
(format *log* "Handling message ~s~%" (cons action args))
(ecase action
(:newuser
(destructuring-bind (username session-actor)
args
(cond ((assoc username users :test #'equalp)
(send session-actor :rejected
(format nil "Username ~a is already taken. Send /NICK new-nick with a valid name to enter the chat~%" username)))
((equalp username "Server")
(send session-actor :rejected
(format nil "Server is not a valid username. Send /NICK new-nick with a valid name to enter the chat~%")))
(t (send-message users "Server" (format nil "~a has joined the chat." username))
(send session-actor :accepted
(format nil "Welcome to the Rosetta Code chat server in Common Lisp. ~a users connected.~%"
(length users)))
(pushnew (cons username session-actor)
users
:key #'car
:test #'equalp)))))
(:who
(destructuring-bind (username) args
(let ((actor (cdr (assoc username users :test #'equalp))))
(send actor :message "Server"
"Users connected right now:")
(loop for (user . nil) in users
do (send actor :message "Server" user)))))
(:message
(apply #'send-message users args))
(:dropuser
(destructuring-bind (username) args
(let ((user-actor (cdr (assoc username users :test #'equalp))))
(send user-actor :close)
(send user-actor 'stop))
(setf users (remove username users
:key #'car
:test #'equalp))
(send-message users "Server" (format nil "~a has left." username))))))))
(defmacro drop-connection-on-error (&body body)
`(handler-case (progn ,@body)
(t (err)
(format *log* "Error: ~a; Closing connection" err)
(send self :close)
(send self 'stop)
(send *user-manager* :dropuser username))))
(defun parse-command (message)
(let* ((space-at (position #\Space message))
(after-space (and space-at
(position-if (lambda (ch)
(not (char= ch #\Space)))
message :start (1+ space-at)))))
(values (subseq message 0 space-at)
(and after-space
(string-trim *whitespace*
(subseq message after-space))))))
(defun help (socket)
(socket-format socket "/QUIT to quit, /WHO to list users.~%"))
(defun make-user (username socket)
(let* ((state :unregistered)
(actor
(actor (message &rest args)
(drop-connection-on-error
(ecase message
(:register
(send *user-manager* :newuser username self))
(:accepted
(destructuring-bind (message) args
(write-string message (socket-stream socket))
(finish-output (socket-stream socket))
(setf state :registered)))
(:rejected
(destructuring-bind (message) args
(write-string message (socket-stream socket))
(finish-output (socket-stream socket))
(setf state :unregistered)))
(:user-typed
(destructuring-bind (message) args
(when (> (length message) 0)
(if (char= (aref message 0) #\/)
(multiple-value-bind (cmd arg)
(parse-command message)
(cond ((equalp cmd "/nick")
(ecase state
(:unregistered
(setf username arg)
(send *user-manager* :newuser username self))
(:registered
(socket-format socket
"Can't change your name after successfully registering~%"))))
((equalp cmd "/help")
(help socket))
((equalp cmd "/who")
(send *user-manager* :who username))
((equalp cmd "/quit")
(socket-format socket
"Goodbye.~%")
(send *user-manager* :dropuser username))
(t
(socket-format socket
"Unknown command~%"))))
(send *user-manager* :message username message)))))
(:message
(destructuring-bind (from-user message) args
(socket-format socket "<~a> ~a~%" from-user message)))
(:close
(log-errors
(close (socket-stream socket)))))))))
(bt:make-thread (lambda ()
(handler-case
(loop for line = (read-line (socket-stream socket) nil :eof)
do (if (eq line :eof)
(send *user-manager* :dropuser username)
(send actor :user-typed (remove #\Return line))))
(t () (send *user-manager* :dropuser username))))
:name "Reader thread")
actor))
(defun initialize-user (socket)
(bt:make-thread
(lambda ()
(format *log* "Handling new connection ~s" socket)
(log-errors
(loop do
(socket-format socket "Your name: ")
(let ((name (string-trim *whitespace* (read-line (socket-stream socket)))))
(format *log* "Registering user ~a" name)
(cond ((equalp name "Server")
(socket-format socket
"Server is not a valid username.~%"))
(t (send *user-manager*
:newuser name (make-user name socket))
(return)))))))
:name "INITIALIZE-USER"))
(defun accept-connections ()
(let ((accepting-socket (socket-listen "0.0.0.0" 7070)))
(loop for new-connection = (socket-accept accepting-socket)
do (initialize-user new-connection))))
(make-thread #'accept-connections)

View file

@ -0,0 +1,159 @@
import std.getopt;
import std.socket;
import std.stdio;
import std.string;
struct client {
int pos;
char[] name;
char[] buffer;
Socket socket;
}
void broadcast(client[] connections, size_t self, const char[] message) {
writeln(message);
for (size_t i = 0; i < connections.length; i++) {
if (i == self) continue;
connections[i].socket.send(message);
connections[i].socket.send("\r\n");
}
}
bool registerClient(client[] connections, size_t self) {
for (size_t i = 0; i < connections.length; i++) {
if (i == self) continue;
if (icmp(connections[i].name, connections[self].name) == 0) {
return false;
}
}
return true;
}
void main(string[] args) {
ushort port = 4004;
auto helpInformation = getopt
(
args,
"port|p", "The port to listen to chat clients on [default is 4004]", &port
);
if (helpInformation.helpWanted) {
defaultGetoptPrinter("A simple chat server based on a task in rosettacode.", helpInformation.options);
return;
}
auto listener = new TcpSocket();
assert(listener.isAlive);
listener.blocking = false;
listener.bind(new InternetAddress(port));
listener.listen(10);
writeln("Listening on port: ", port);
enum MAX_CONNECTIONS = 60;
auto socketSet = new SocketSet(MAX_CONNECTIONS + 1);
client[] connections;
while(true) {
socketSet.add(listener);
foreach (con; connections) {
socketSet.add(con.socket);
}
Socket.select(socketSet, null, null);
for (size_t i = 0; i < connections.length; i++) {
if (socketSet.isSet(connections[i].socket)) {
char[1024] buf;
auto datLength = connections[i].socket.receive(buf[]);
if (datLength == Socket.ERROR) {
writeln("Connection error.");
} else if (datLength != 0) {
if (buf[0] == '\n' || buf[0] == '\r') {
if (connections[i].buffer == "/quit") {
connections[i].socket.close();
if (connections[i].name.length > 0) {
writeln("Connection from ", connections[i].name, " closed.");
} else {
writeln("Connection from ", connections[i].socket.remoteAddress(), " closed.");
}
connections[i] = connections[$-1];
connections.length--;
i--;
writeln("\tTotal connections: ", connections.length);
continue;
} else if (connections[i].name.length == 0) {
connections[i].buffer = strip(connections[i].buffer);
if (connections[i].buffer.length > 0) {
connections[i].name = connections[i].buffer;
if (registerClient(connections, i)) {
connections.broadcast(i, "+++ " ~ connections[i].name ~ " arrived +++");
} else {
connections[i].socket.send("Name already registered. Please enter your name: ");
connections[i].name.length = 0;
}
} else {
connections[i].socket.send("A name is required. Please enter your name: ");
}
} else {
connections.broadcast(i, connections[i].name ~ "> " ~ connections[i].buffer);
}
connections[i].buffer.length = 0;
} else {
connections[i].buffer ~= buf[0..datLength];
}
} else {
try {
if (connections[i].name.length > 0) {
writeln("Connection from ", connections[i].name, " closed.");
} else {
writeln("Connection from ", connections[i].socket.remoteAddress(), " closed.");
}
} catch (SocketException) {
writeln("Connection closed.");
}
}
}
}
if (socketSet.isSet(listener)) {
Socket sn = null;
scope(failure) {
writeln("Error accepting");
if (sn) {
sn.close();
}
}
sn = listener.accept();
assert(sn.isAlive);
assert(listener.isAlive);
if (connections.length < MAX_CONNECTIONS) {
client newclient;
writeln("Connection from ", sn.remoteAddress(), " established.");
sn.send("Enter name: ");
newclient.socket = sn;
connections ~= newclient;
writeln("\tTotal connections: ", connections.length);
} else {
writeln("Rejected connection from ", sn.remoteAddress(), "; too many connections.");
sn.close();
assert(!sn.isAlive);
assert(listener.isAlive);
}
}
socketSet.reset();
}
}

View 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.

View file

@ -0,0 +1,182 @@
package main
import (
"bufio"
"flag"
"fmt"
"log"
"net"
"strings"
"time"
)
func main() {
log.SetPrefix("chat: ")
addr := flag.String("addr", "localhost:4000", "listen address")
flag.Parse()
log.Fatal(ListenAndServe(*addr))
}
// A Server represents a chat server that accepts incoming connections.
type Server struct {
add chan *conn // To add a connection
rem chan string // To remove a connection by name
msg chan string // To send a message to all connections
stop chan bool // To stop early
}
// ListenAndServe listens on the TCP network address addr for
// new chat client connections.
func ListenAndServe(addr string) error {
ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}
log.Println("Listening for connections on", addr)
defer ln.Close()
s := &Server{
add: make(chan *conn),
rem: make(chan string),
msg: make(chan string),
stop: make(chan bool),
}
go s.handleConns()
for {
// TODO use AcceptTCP() so that we can get a TCPConn on which
// we can call SetKeepAlive() and SetKeepAlivePeriod()
rwc, err := ln.Accept()
if err != nil {
// TODO Could handle err.(net.Error).Temporary()
// here by adding a backoff delay.
close(s.stop)
return err
}
log.Println("New connection from", rwc.RemoteAddr())
go newConn(s, rwc).welcome()
}
}
// handleConns is run as a go routine to handle adding and removal of
// chat client connections as well as broadcasting messages to them.
func (s *Server) handleConns() {
// We define the `conns` map here rather than within Server,
// and we use local function literals rather than methods to be
// extra sure that the only place that touches this map is this
// method. In this way we forgo any explicit locking needed as
// we're the only go routine that can see or modify this.
conns := make(map[string]*conn)
var dropConn func(string)
writeAll := func(str string) {
log.Printf("Broadcast: %q", str)
// TODO handle blocked connections
for name, c := range conns {
c.SetWriteDeadline(time.Now().Add(500 * time.Millisecond))
_, err := c.Write([]byte(str))
if err != nil {
log.Printf("Error writing to %q: %v", name, err)
c.Close()
delete(conns, name)
// Defer all the disconnect messages until after
// we've closed all currently problematic conns.
defer dropConn(name)
}
}
}
dropConn = func(name string) {
if c, ok := conns[name]; ok {
log.Printf("Closing connection with %q from %v",
name, c.RemoteAddr())
c.Close()
delete(conns, name)
} else {
log.Printf("Dropped connection with %q", name)
}
str := fmt.Sprintf("--- %q disconnected ---\n", name)
writeAll(str)
}
defer func() {
writeAll("Server stopping!\n")
for _, c := range conns {
c.Close()
}
}()
for {
select {
case c := <-s.add:
if _, exists := conns[c.name]; exists {
fmt.Fprintf(c, "Name %q is not available\n", c.name)
go c.welcome()
continue
}
str := fmt.Sprintf("+++ %q connected +++\n", c.name)
writeAll(str)
conns[c.name] = c
go c.readloop()
case str := <-s.msg:
writeAll(str)
case name := <-s.rem:
dropConn(name)
case <-s.stop:
return
}
}
}
// A conn represents the server side of a single chat connection.
// Note we embed the bufio.Reader and net.Conn (and specifically in
// that order) so that a conn gets the appropriate methods from each
// to be a full io.ReadWriteCloser.
type conn struct {
*bufio.Reader // buffered input
net.Conn // raw connection
server *Server // the Server on which the connection arrived
name string
}
func newConn(s *Server, rwc net.Conn) *conn {
return &conn{
Reader: bufio.NewReader(rwc),
Conn: rwc,
server: s,
}
}
// welcome requests a name from the client before attempting to add the
// named connect to the set handled by the server.
func (c *conn) welcome() {
var err error
for c.name = ""; c.name == ""; {
fmt.Fprint(c, "Enter your name: ")
c.name, err = c.ReadString('\n')
if err != nil {
log.Printf("Reading name from %v: %v", c.RemoteAddr(), err)
c.Close()
return
}
c.name = strings.TrimSpace(c.name)
}
// The server will take this *conn and do a final check
// on the name, possibly starting c.welcome() again.
c.server.add <- c
}
// readloop is started as a go routine by the server once the initial
// welcome phase has completed successfully. It reads single lines from
// the client and passes them to the server for broadcast to all chat
// clients (including us).
// Once done, we ask the server to remove (and close) our connection.
func (c *conn) readloop() {
for {
msg, err := c.ReadString('\n')
if err != nil {
break
}
//msg = strings.TrimSpace(msg)
c.server.msg <- c.name + "> " + msg
}
c.server.rem <- c.name
}

View file

@ -0,0 +1,156 @@
class ChatServer implements Runnable {
private int port = 0
private List<Client> clientList = new ArrayList<>()
ChatServer(int port) {
this.port = port
}
@SuppressWarnings("GroovyInfiniteLoopStatement")
@Override
void run() {
try {
ServerSocket serverSocket = new ServerSocket(port)
while (true) {
Socket socket = serverSocket.accept()
new Thread(new Client(socket)).start()
}
} catch (Exception e) {
e.printStackTrace()
}
}
private synchronized boolean registerClient(Client client) {
for (Client other : clientList) {
if (other.clientName.equalsIgnoreCase(client.clientName)) {
return false
}
}
clientList.add(client)
return true
}
private void deRegisterClient(Client client) {
boolean wasRegistered
synchronized (this) {
wasRegistered = clientList.remove(client)
}
if (wasRegistered) {
broadcast(client, "--- " + client.clientName + " left ---")
}
}
private synchronized String getOnlineListCSV() {
StringBuilder sb = new StringBuilder()
sb.append(clientList.size()).append(" user(s) online: ")
def it = clientList.iterator()
if (it.hasNext()) {
sb.append(it.next().clientName)
}
while (it.hasNext()) {
sb.append(", ")
sb.append(it.next().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
synchronized (this) {
clients = new ArrayList<>(this.clientList)
}
for (Client client : clients) {
if (client == fromClient) {
continue
}
try {
client.write(msg + "\r\n")
} catch (Exception ignored) {
// empty
}
}
}
class Client implements Runnable {
private Socket socket = null
private Writer output = null
private String clientName = null
Client(Socket socket) {
this.socket = socket
}
@Override
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
while (null != (line = input.readLine())) {
if (null == clientName) {
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 ignored) {
// empty
} finally {
deRegisterClient(this)
output = null
try {
socket.close()
} catch (Exception ignored) {
// empty
}
socket = null
}
}
void write(String msg) {
output.write(msg)
output.flush()
}
@Override
boolean equals(client) {
return (null != client) && (client instanceof Client) && (null != clientName) && clientName == client.clientName
}
@Override
int hashCode() {
int result
result = (socket != null ? socket.hashCode() : 0)
result = 31 * result + (output != null ? output.hashCode() : 0)
result = 31 * result + (clientName != null ? clientName.hashCode() : 0)
return result
}
}
static void main(String[] args) {
int port = 4004
if (args.length > 0) {
port = Integer.parseInt(args[0])
}
new ChatServer(port).run()
}
}

View file

@ -0,0 +1,84 @@
{-# LANGUAGE OverloadedStrings #-}
import Network
import System.IO
import Control.Concurrent
import qualified Data.Text as T
import Data.Text (Text)
import qualified Data.Text.IO as T
import qualified Data.Map as M
import Data.Map (Map)
import Control.Monad.Reader
import Control.Monad.Error
import Control.Exception
import Data.Monoid
import Control.Applicative
type ServerApp = ReaderT ThreadData IO
data Speaker = Server | Client Text
data ThreadData = ThreadData { threadHandle :: Handle
, userTableMV :: MVar (Map Text Handle)}
echoLocal = liftIO . T.putStrLn
echoRemote = echoMessage . (">> "<>)
echoMessage msg = viewHandle >>= \h -> liftIO $ T.hPutStrLn h msg
getRemoteLine = viewHandle >>= liftIO . T.hGetLine
putMVarT = (liftIO.) . putMVar
takeMVarT = liftIO . takeMVar
readMVarT = liftIO . readMVar
modifyUserTable fn = viewUsers >>= \mv ->
liftIO $ modifyMVar_ mv (return . fn)
viewHandle = threadHandle <$> ask
viewUsers = userTableMV <$> ask
userChat :: ServerApp ()
userChat = do
name <- addUser
echoLocal name
h <- viewHandle
(flip catchError) (\_ -> removeUser name) $
do echoLocal $ "Accepted " <> name
forever $ getRemoteLine >>= broadcast (Client name)
removeUser :: Text -> ServerApp ()
removeUser name = do
echoLocal $ "Exception with " <> name <> ", removing from userTable"
broadcast Server $ name <> " has left the server"
modifyUserTable (M.delete name)
addUser :: ServerApp Text
addUser = do
h <- viewHandle
usersMV <- viewUsers
echoRemote "Enter username"
name <- T.filter (/='\r') <$> getRemoteLine
userTable <- takeMVarT usersMV
if name `M.member` userTable
then do echoRemote "Username already exists!"
putMVarT usersMV userTable
addUser
else do putMVarT usersMV (M.insert name h userTable)
broadcast Server $ name <> " has joined the server"
echoRemote "Welcome to the server!\n>> Other users:"
readMVarT usersMV >>=
mapM_ (echoRemote . ("*" <>) . fst)
. filter ((/=name). fst) . M.toList
return name
broadcast :: Speaker -> Text -> ServerApp ()
broadcast user msg =
viewUsers >>= readMVarT >>= mapM_ (f . snd) . fn . M.toList
where f h = liftIO $ T.hPutStrLn h $ nm <> msg
(fn, nm) = case user of
Server -> (id, ">> ")
Client t -> (filter ((/=t) . fst), t <> "> ")
clientLoop socket users = do
(h, _, _) <- accept socket
hSetBuffering h LineBuffering
forkIO $ runReaderT userChat (ThreadData h users)
clientLoop socket users
main = do
server <- listenOn $ PortNumber 5002
T.putStrLn "Server started"
newMVar (M.empty) >>= clientLoop server

View file

@ -0,0 +1,27 @@
global mlck, nCons, cons
procedure main()
mlck := mutex()
nCons := 0
cons := mutex(set())
while f := open(":12321","na") do {
handle_client(f)
critical mlck: if nCons <= 0 then close(f)
}
end
procedure handle_client(f)
critical mlck: nCons +:= 1
thread {
select(f,1000) & {
writes(f, "Name? ")
nick := (read(f) ? tab(upto('\n\r')))
every write(!cons, nick," has joined.")
insert(cons, f)
while s := read(f) do every write(!cons, nick,": ",s)
}
delete(cons, f)
every write(!cons, nick," has left.")
critical mlck: nCons -:= 1
}
end

View 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();
}
}

View file

@ -0,0 +1,148 @@
const net = require("net");
const EventEmitter = require("events").EventEmitter;
/*******************************************************************************
* ChatServer
*
* Manages connections, users, and chat messages.
******************************************************************************/
class ChatServer {
constructor() {
this.chatters = {};
this.server = net.createServer(this.handleConnection.bind(this));
this.server.listen(1212, "localhost");
}
isNicknameLegal(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 (const used_nick in this.chatters) {
if (used_nick === nickname) {
return false;
}
}
return true;
}
handleConnection(connection) {
console.log(`Incoming connection from ${connection.remoteAddress}`);
connection.setEncoding("utf8");
let 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));
}
handleChat(chatter, message) {
this.sendToEveryChatterExcept(chatter, chatter.nickname + ": " + message);
}
handleJoin(chatter) {
console.log(`${chatter.nickname} has joined the chat.`);
this.sendToEveryChatter(`${chatter.nickname} has joined the chat.`);
this.addChatter(chatter);
}
handleLeave(chatter) {
console.log(`${chatter.nickname} has left the chat.`);
this.removeChatter(chatter);
this.sendToEveryChatter(`${chatter.nickname} has left the chat.`);
}
addChatter(chatter) {
this.chatters[chatter.nickname] = chatter;
}
removeChatter(chatter) {
delete this.chatters[chatter.nickname];
}
sendToEveryChatter(data) {
for (const nickname in this.chatters) {
this.chatters[nickname].send(data);
}
}
sendToEveryChatterExcept(chatter, data) {
for (const nickname in this.chatters) {
if (nickname !== chatter.nickname) {
this.chatters[nickname].send(data);
}
}
}
}
/*******************************************************************************
* Chatter
*
* Represents a single user/connection in the chat server.
******************************************************************************/
class Chatter extends EventEmitter {
constructor(socket, server) {
super();
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?");
}
handleNickname(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?");
}
}
handleChat(line) {
this.emit("chat", this, line);
}
handleDisconnect() {
this.emit("leave", this);
}
send(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.
******************************************************************************/
class SocketLineBuffer extends EventEmitter {
constructor(socket) {
super();
this.socket = socket;
this.buffer = "";
this.socket.on("data", this.handleData.bind(this));
}
handleData(data) {
for (let i = 0; i < data.length; i++) {
const 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();

View file

@ -0,0 +1,82 @@
using HttpServer
using WebSockets
const connections = Dict{Int,WebSocket}()
const usernames = Dict{Int,String}()
function decodeMessage( msg )
String(copy(msg))
end
wsh = WebSocketHandler() do req, client
global connections
@show connections[client.id] = client
println("req is $req")
notifyonline = "Connection from user number $(client.id) is now online."
for (k,v) in connections
if k != client.id
try
write(v, notifyonline)
catch
continue
end
end
end
while true
try
msg = read(client)
catch
telloffline = "User $(usernames[client.id]) disconnected."
println(telloffline, "(The client id was $(client.id).)")
delete!(connections, client.id)
if haskey(usernames, client.id)
delete!(usernames, client.id)
end
for (k,v) in connections
try
write(v, telloffline)
catch
continue
end
end
return
end
msg = decodeMessage(msg)
if startswith(msg, "setusername:")
println("SETTING USERNAME: $msg")
usernames[client.id] = msg[13:end]
notifyusername = "User number $(client.id) chose $(usernames[client.id]) as name handle."
for (k,v) in connections
try
write(v, notifyusername)
catch
println("Caught exception writing to user $k")
continue
end
end
end
if startswith(msg, "say:")
println("EMITTING MESSAGE: $msg")
for (k,v) in connections
if k != client.id
try
write(v, (usernames[client.id] * ": " * msg[5:end]))
catch
println("Caught exception writing to user $k")
continue
end
end
end
end
end
end
onepage = readstring(Pkg.dir("WebSockets","examples","chat-client.html"))
httph = HttpHandler() do req::Request, res::Response
Response(onepage)
end
server = Server(httph, wsh)
println("Chat server listening on 8000...")
run(server,8000)

View file

@ -0,0 +1,153 @@
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader
import java.io.OutputStreamWriter
import java.io.Writer
import java.net.ServerSocket
import java.net.Socket
import java.util.ArrayList
import java.util.Collections
class ChatServer private constructor(private val port: Int) : Runnable {
private val clients = ArrayList<Client>()
private val onlineListCSV: String
@Synchronized get() {
val sb = StringBuilder()
sb.append(clients.size).append(" user(s) online: ")
for (i in clients.indices) {
sb.append(if (i > 0) ", " else "").append(clients[i].clientName)
}
return sb.toString()
}
override fun run() {
try {
val ss = ServerSocket(port)
while (true) {
val s = ss.accept()
Thread(Client(s)).start()
}
} catch (e: Exception) {
e.printStackTrace()
}
}
@Synchronized
private fun registerClient(client: Client): Boolean {
for (otherClient in clients) {
if (otherClient.clientName!!.equals(client.clientName!!, ignoreCase = true)) {
return false
}
}
clients.add(client)
return true
}
private fun deRegisterClient(client: Client) {
var wasRegistered = false
synchronized(this) {
wasRegistered = clients.remove(client)
}
if (wasRegistered) {
broadcast(client, "--- " + client.clientName + " left ---")
}
}
private fun broadcast(fromClient: Client, msg: String) {
// Copy client list (don't want to hold lock while doing IO)
var clients: List<Client> = Collections.emptyList()
synchronized(this) {
clients = ArrayList(this.clients)
}
for (client in clients) {
if (client.equals(fromClient)) {
continue
}
try {
client.write(msg + "\r\n")
} catch (e: Exception) {
e.printStackTrace()
}
}
}
inner class Client internal constructor(private var socket: Socket?) : Runnable {
private var output: Writer? = null
var clientName: String? = null
override fun run() {
try {
socket!!.sendBufferSize = 16384
socket!!.tcpNoDelay = true
val input = BufferedReader(InputStreamReader(socket!!.getInputStream()))
output = OutputStreamWriter(socket!!.getOutputStream())
write("Please enter your name: ")
var line: String
while (true) {
line = input.readLine()
if (null == line) {
break
}
if (clientName == null) {
line = line.trim { it <= ' ' }
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(onlineListCSV + "\r\n")
broadcast(this, "+++ $clientName arrived +++")
continue
}
if (line.equals("/quit", ignoreCase = true)) {
return
}
broadcast(this, "$clientName> $line")
}
} catch (e: Exception) {
e.printStackTrace()
} finally {
deRegisterClient(this)
output = null
try {
socket!!.close()
} catch (e: Exception) {
e.printStackTrace()
}
socket = null
}
}
@Throws(IOException::class)
internal fun write(msg: String) {
output!!.write(msg)
output!!.flush()
}
internal fun equals(client: Client?): Boolean {
return (client != null
&& clientName != null
&& client.clientName != null
&& clientName == client.clientName)
}
}
companion object {
@JvmStatic
fun main(args: Array<String>) {
var port = 4004
if (args.isNotEmpty()) {
port = Integer.parseInt(args[0])
}
ChatServer(port).run()
}
}
}

View file

@ -0,0 +1,42 @@
import asyncnet, asyncdispatch
type
Client = tuple
socket: AsyncSocket
name: string
connected: bool
var clients {.threadvar.}: seq[Client]
proc sendOthers(client: Client, line: string) {.async.} =
for c in clients:
if c != client and c.connected:
await c.socket.send(line & "\c\L")
proc processClient(socket: AsyncSocket) {.async.} =
await socket.send("Please enter your name: ")
var client: Client = (socket, await socket.recvLine(), true)
clients.add(client)
asyncCheck client.sendOthers("+++ " & client.name & " arrived +++")
while true:
let line = await client.socket.recvLine()
if line == "":
asyncCheck client.sendOthers("--- " & client.name & " leaves ---")
client.connected = false
return
asyncCheck client.sendOthers(client.name & "> " & line)
proc serve() {.async.} =
clients = @[]
var server = newAsyncSocket()
server.bindAddr(Port(4004))
server.listen()
while true:
let socket = await server.accept()
asyncCheck processClient(socket)
asyncCheck serve()
runForever()

View file

@ -0,0 +1,109 @@
use System.IO.Net;
use System.Concurrency;
use Collection;
bundle Default {
class ChatServer {
@clients : StringMap;
@clients_mutex : ThreadMutex;
New() {
@clients := StringMap->New();
@clients_mutex := ThreadMutex->New("clients_mutex");
}
method : ValidLogin(login_name : String, clients : StringMap) ~ Bool {
if(clients->Has(login_name)) {
return false;
};
return true;
}
function : Main(args : String[]) ~ Nil {
chat_server := ChatServer->New();
chat_server->Run();
}
method : public : Broadcast(message : String, sender : Client) ~ Nil {
client_array : Vector;
critical(@clients_mutex) {
client_array := @clients->GetValues();
};
each(i : client_array) {
client := client_array->Get(i)->As(Client);
if(client <> sender) {
client->Send(message);
};
};
}
method : public : Disconnect(sender : Client) ~ Nil {
send_name := sender->GetName();
Broadcast("+++ {$send_name} has left +++", sender);
critical(@clients_mutex) {
@clients->Remove(sender->GetName());
};
sender->Close();
}
method : public : Run() ~ Nil {
server := TCPSocketServer->New(4661);
if(server->Listen(5)) {
while(true) {
client_sock := server->Accept();
critical(@clients_mutex) {
client_sock->WriteString("login: ");
login_name := client_sock->ReadString();
if(ValidLogin(login_name, @clients)) {
client := Client->New(login_name, client_sock, @self);
@clients->Insert(client->GetName(), client);
client->Execute(Nil);
}
else {
client_sock->WriteString("+++ login in use +++\r\n");
client_sock->Close();
};
};
};
};
server->Close();
}
}
class Client from Thread {
@client_sock : TCPSocket;
@server : ChatServer;
New(login_name : String, client_sock : TCPSocket, server : ChatServer) {
Parent(login_name);
@client_sock := client_sock;
@server := server;
}
method : public : Close() ~ Nil {
@client_sock->Close();
}
method : public : Send(message : String) ~ Nil {
if(@client_sock->IsOpen() & message->Size() > 0) {
@client_sock->WriteString("{$message}\r\n");
}
else {
@server->Disconnect(@self);
};
}
method : public : Run(param : Base) ~ Nil {
client_name := GetName();
@server->Broadcast("+++ {$client_name} has arrived +++", @self);
message := @client_sock->ReadString();
while(message->Size() > 0 & message->Equals("/quit") = false) {
@server->Broadcast("{$client_name}> {$message}", @self);
message := @client_sock->ReadString();
};
@server->Disconnect(@self);
}
}
}

View file

@ -0,0 +1,68 @@
(define (timestamp) (syscall 201 "%c"))
(fork-server 'chat-room (lambda ()
(let this ((visitors #empty))
(let* ((envelope (wait-mail))
(sender msg envelope))
(case msg
(['join who name]
(let ((visitors (put visitors who name)))
(for-each (lambda (who)
(print-to (car who) name " joined to as"))
(ff->alist visitors))
(this visitors)))
(['talk message]
(for-each (lambda (who)
(print-to (car who) (cdr who) ": " message))
(ff->alist visitors))
(this visitors))
(['part who]
(for-each (lambda (who)
(print-to (car who) (visitors (car who) "unknown") " leaved"))
(ff->alist visitors))
(let ((visitors (del visitors who)))
(this visitors))))))))
(define (on-accept name fd)
(lambda ()
(print "# " (timestamp) "> we got new visitor: " name)
(mail 'chat-room ['join fd name])
(let*((ss1 ms1 (clock)))
(let loop ((str #null) (stream (force (port->bytestream fd))))
(cond
((null? stream)
#false)
((function? stream)
(mail 'chat-room ['talk (list->string (reverse str))])
(loop #null (force stream)))
(else
(loop (cons (car stream) str) (cdr stream)))))
(syscall 3 fd)
(let*((ss2 ms2 (clock)))
(print "# " (timestamp) "> visitor leave us. It takes " (+ (* (- ss2 ss1) 1000) (- ms2 ms1)) "ms.")))
(mail 'chat-room ['part fd])
))
(define (run port)
(let ((socket (syscall 41)))
; bind
(let loop ((port port))
(if (not (syscall 49 socket port)) ; bind
(loop (+ port 2))
(print "Server binded to " port)))
; listen
(if (not (syscall 50 socket)) ; listen
(shutdown (print "Can't listen")))
; accept
(let loop ()
(if (syscall 23 socket) ; select
(let ((fd (syscall 43 socket))) ; accept
;(print "\n# " (timestamp) ": new request from " (syscall 51 fd))
(fork (on-accept (syscall 51 fd) fd))))
(sleep 0)
(loop))))
(run 8080)

View file

@ -0,0 +1,108 @@
use 5.010;
use strict;
use warnings;
use threads;
use threads::shared;
use IO::Socket::INET;
use Time::HiRes qw(sleep ualarm);
my $HOST = "localhost";
my $PORT = 4004;
my @open;
my %users : shared;
sub broadcast {
my ($id, $message) = @_;
print "$message\n";
foreach my $i (keys %users) {
if ($i != $id) {
$open[$i]->send("$message\n");
}
}
}
sub sign_in {
my ($conn) = @_;
state $id = 0;
threads->new(
sub {
while (1) {
$conn->send("Please enter your name: ");
$conn->recv(my $name, 1024, 0);
if (defined $name) {
$name = unpack('A*', $name);
if (exists $users{$name}) {
$conn->send("Name entered is already in use.\n");
}
elsif ($name ne '') {
$users{$id} = $name;
broadcast($id, "+++ $name arrived +++");
last;
}
}
}
}
);
++$id;
push @open, $conn;
}
my $server = IO::Socket::INET->new(
Timeout => 0,
LocalPort => $PORT,
Proto => "tcp",
LocalAddr => $HOST,
Blocking => 0,
Listen => 1,
Reuse => 1,
);
local $| = 1;
print "Listening on $HOST:$PORT\n";
while (1) {
my ($conn) = $server->accept;
if (defined($conn)) {
sign_in($conn);
}
foreach my $i (keys %users) {
my $conn = $open[$i];
my $message;
eval {
local $SIG{ALRM} = sub { die "alarm\n" };
ualarm(500);
$conn->recv($message, 1024, 0);
ualarm(0);
};
if ($@ eq "alarm\n") {
next;
}
if (defined($message)) {
if ($message ne '') {
$message = unpack('A*', $message);
broadcast($i, "$users{$i}> $message");
}
else {
broadcast($i, "--- $users{$i} leaves ---");
delete $users{$i};
undef $open[$i];
}
}
}
sleep(0.1);
}

View file

@ -0,0 +1,80 @@
#!/usr/bin/perl
use strict; # http://www.rosettacode.org/wiki/Chat_server
use warnings;
use IO::Socket;
use IO::Select; # with write queueing
my $port = shift // 6666;
my (%nicks, @users, %data);
my $listen = IO::Socket::INET->new(LocalPort => $port, Listen => 9,
Reuse => 1) or die "$@ opening socket on port $port";
my $rsel = IO::Select->new($listen);
my $wsel = IO::Select->new();
print "ready on $port...\n";
sub to
{
my $text = pop;
for ( @_ )
{
length $data{$_}{out} or $wsel->add( $_ );
length( $data{$_}{out} .= $text ) > 1e4 and left( $_ );
}
return $text;
}
sub left
{
my $h = shift;
@users = grep $h != $_, @users;
if( defined( my $nick = delete $nicks{$h} ) )
{
print to @users, "$nick has left\n";
}
delete $data{$h};
$rsel->remove($h);
}
while( 1 )
{
my ($reads, $writes) = IO::Select->select($rsel, $wsel, undef, 5);
for my $h ( @{ $writes // [] } )
{
my $len = syswrite $h, $data{$h}{out};
$len and substr $data{$h}{out}, 0, $len, '';
length $data{$h}{out} or $wsel->remove( $h );
}
for my $h ( @{ $reads // [] } )
{
if( $h == $listen ) # new connection
{
$rsel->add( my $client = $h->accept );
$data{$client} = { h => $client, out => "enter nick: ", in => '' };
$wsel->add( $client );
}
elsif( not sysread $h, $data{$h}{in}, 4096, length $data{$h}{in} ) # closed
{
left $h;
}
elsif( exists $nicks{$h} ) # user is signed in
{
my @others = grep $h != $_, @users;
to @others, "$nicks{$h}> $&" while $data{$h}{in} =~ s/.*\n//;
}
elsif( $data{$h}{in} =~ s/^(\w+)\r?\n.*//s and
not grep lc $1 eq lc, values %nicks )
{ # user has joined
my $all = join ' ', sort values %nicks;
$nicks{$h} = $1;
push @users, $h;
print to @users, "$nicks{$h} has joined $all\n";
}
else # bad nick
{
to $h, "nick invalid or in use, enter nick: ";
$data{$h}{in} = '';
}
}
}

View file

@ -0,0 +1,171 @@
(notonline)-->
<span style="color: #000080;font-style:italic;">--
-- demo\rosetta\ChatServer.exw
-- ========================
--
-- translation of (qchat) chatServ.exw
--
-- Run this first, then ChatClient.exw, see also IRC_Gateway.exw
--
-- Note that I've only got a 32-bit windows eulibnet.dll, but it should not
-- be dificult to use a "real" libnet.dll/so, or something a little newer.
--</span>
<span style="color: #008080;">without</span> <span style="color: #008080;">js</span>
<span style="color: #008080;">include</span> <span style="color: #000000;">pGUI</span><span style="color: #0000FF;">.</span><span style="color: #000000;">e</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">dl</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">`Download rosetta\eulibnet\ from http://phix.x10.mx/pmwiki/pmwiki.php?n=Main.Eulibnet`</span>
<span style="color: #7060A8;">assert</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">get_file_type</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"eulibnet"</span><span style="color: #0000FF;">)=</span><span style="color: #004600;">FILETYPE_DIRECTORY</span><span style="color: #0000FF;">,</span><span style="color: #000000;">dl</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">include</span> <span style="color: #000000;">eulibnet</span><span style="color: #0000FF;">/</span><span style="color: #000000;">eulibnet</span><span style="color: #0000FF;">.</span><span style="color: #000000;">ew</span>
<span style="color: #004080;">Ihandle</span> <span style="color: #000000;">log_list</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">log_window</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">statusbar</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">timer</span>
<span style="color: #004080;">atom</span> <span style="color: #000000;">listconn</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">IP</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"127.0.0.1"</span><span style="color: #0000FF;">,</span>
<span style="color: #000000;">port</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"29029"</span><span style="color: #0000FF;">,</span>
<span style="color: #000000;">IPaddress</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">IP</span> <span style="color: #0000FF;">&</span> <span style="color: #008000;">":"</span> <span style="color: #0000FF;">&</span> <span style="color: #000000;">port</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">MAX_MSG</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">550</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">connections</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{},</span>
<span style="color: #000000;">nicknames</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{}</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">max_log_len</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">300</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">log_to_window</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">txt</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">count</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">to_number</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">IupGetAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">log_list</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"COUNT"</span><span style="color: #0000FF;">))</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">count</span> <span style="color: #0000FF;">></span> <span style="color: #000000;">max_log_len</span> <span style="color: #008080;">then</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">t</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #000000;">count</span><span style="color: #0000FF;">-</span><span style="color: #000000;">max_log_len</span> <span style="color: #008080;">do</span>
<span style="color: #7060A8;">IupSetAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">log_list</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"REMOVEITEM"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"1"</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #7060A8;">IupSetAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">log_list</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"APPENDITEM"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">txt</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">IupSetAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">log_list</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"TOPITEM"</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">IupGetAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">log_list</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"COUNT"</span><span style="color: #0000FF;">))</span>
<span style="color: #7060A8;">IupUpdate</span><span style="color: #0000FF;">(</span><span style="color: #000000;">log_list</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">msg</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">sequence</span> <span style="color: #000000;">args</span><span style="color: #0000FF;">={})</span>
<span style="color: #008080;">if</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">args</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span> <span style="color: #000000;">msg</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">sprintf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">msg</span><span style="color: #0000FF;">,</span><span style="color: #000000;">args</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #7060A8;">IupSetStrAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">statusbar</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"TITLE"</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">msg</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">log_to_window</span><span style="color: #0000FF;">(</span><span style="color: #000000;">msg</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">shutDown</span><span style="color: #0000FF;">()</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Shutting down euLibnet..."</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">connections</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">net_closeconn</span><span style="color: #0000FF;">(</span><span style="color: #000000;">connections</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">])</span> <span style="color: #008080;">then</span> <span style="color: #7060A8;">crash</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Error closing connection!"</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">net_closeconn</span><span style="color: #0000FF;">(</span><span style="color: #000000;">listconn</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span> <span style="color: #7060A8;">crash</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Error closing listconn!"</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">net_shutdown</span><span style="color: #0000FF;">()</span> <span style="color: #008080;">then</span> <span style="color: #7060A8;">crash</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Error shutting down euLibnet!"</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">sendToAll</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">msg</span><span style="color: #0000FF;">)</span>
<span style="color: #000080;font-style:italic;">-- Send msg to all clients</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">connections</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #004080;">atom</span> <span style="color: #000000;">ci</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">connections</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Sending to connection %d (%s)"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">ci</span><span style="color: #0000FF;">,</span><span style="color: #000000;">nicknames</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]})</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">net_send_rdm</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ci</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">msg</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Error sending to connection %d"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">ci</span><span style="color: #0000FF;">})</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">mainWindow_onOpen</span><span style="color: #0000FF;">()</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Initializing euLibnet..."</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">net_init</span><span style="color: #0000FF;">()</span> <span style="color: #008080;">then</span> <span style="color: #7060A8;">crash</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Error initializing euLibnet!"</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"done."</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Initializing driver..."</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">net_initdriver</span><span style="color: #0000FF;">(</span><span style="color: #000000;">NET_DRIVER_WSOCK_WIN</span><span style="color: #0000FF;">)</span> <span style="color: #0000FF;">!=</span> <span style="color: #000000;">1</span> <span style="color: #008080;">then</span>
<span style="color: #7060A8;">crash</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Error initializing WinSock driver!"</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"done."</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Opening port "</span> <span style="color: #0000FF;">&</span> <span style="color: #000000;">IPaddress</span> <span style="color: #0000FF;">&</span> <span style="color: #008000;">"..."</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">listconn</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">net_openconn</span><span style="color: #0000FF;">(</span><span style="color: #000000;">NET_DRIVER_WSOCK_WIN</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">IPaddress</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">listconn</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">NULL</span> <span style="color: #008080;">then</span>
<span style="color: #7060A8;">crash</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Couldn't open connection (server already running?)"</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"done."</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">net_listen</span><span style="color: #0000FF;">(</span><span style="color: #000000;">listconn</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
<span style="color: #7060A8;">crash</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Error trying to listen to port"</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Listening on port "</span> <span style="color: #0000FF;">&</span> <span style="color: #000000;">IPaddress</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">timer_cb</span><span style="color: #0000FF;">(</span><span style="color: #004080;">Ihandle</span> <span style="color: #000080;font-style:italic;">/*timer*/</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">conn</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">net_poll_listen</span><span style="color: #0000FF;">(</span><span style="color: #000000;">listconn</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">conn</span> <span style="color: #0000FF;">!=</span> <span style="color: #004600;">NULL</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">connections</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">append</span><span style="color: #0000FF;">(</span><span style="color: #000000;">connections</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">conn</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">nicknames</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">append</span><span style="color: #0000FF;">(</span><span style="color: #000000;">nicknames</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">""</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"New connection open from "</span> <span style="color: #0000FF;">&</span> <span style="color: #000000;">net_getpeer</span><span style="color: #0000FF;">(</span><span style="color: #000000;">conn</span><span style="color: #0000FF;">))</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000080;font-style:italic;">-- Check for messages from clients</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">i</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">connections</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">ci</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">connections</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">net_query_rdm</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ci</span><span style="color: #0000FF;">)</span> <span style="color: #0000FF;">></span> <span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">ni</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">nicknames</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span>
<span style="color: #000080;font-style:italic;">--Get the message</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">msg</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">net_receive_rdm</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ci</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">MAX_MSG</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"received msg \"%s\" of length %d from %d aka %s"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">msg</span><span style="color: #0000FF;">[</span><span style="color: #000000;">2</span><span style="color: #0000FF;">],</span><span style="color: #000000;">msg</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">],</span><span style="color: #000000;">ci</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ni</span><span style="color: #0000FF;">})</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">msg</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;"><</span> <span style="color: #000000;">0</span> <span style="color: #008080;">then</span> <span style="color: #000080;font-style:italic;">--Exit on error</span>
<span style="color: #0000FF;">{}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">net_ignore_rdm</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ci</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">sendToAll</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Server error: some data may be lost"</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">exit</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">msg</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">msg</span><span style="color: #0000FF;">[</span><span style="color: #000000;">2</span><span style="color: #0000FF;">]</span>
<span style="color: #008080;">if</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">msg</span><span style="color: #0000FF;">)</span> <span style="color: #0000FF;">></span> <span style="color: #000000;">4</span> <span style="color: #008080;">and</span> <span style="color: #7060A8;">equal</span><span style="color: #0000FF;">(</span><span style="color: #000000;">msg</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">..</span><span style="color: #000000;">3</span><span style="color: #0000FF;">],</span> <span style="color: #008000;">"/n:"</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
<span style="color: #008080;">if</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">msg</span><span style="color: #0000FF;">[</span><span style="color: #000000;">4</span><span style="color: #0000FF;">..</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">msg</span><span style="color: #0000FF;">)],</span> <span style="color: #000000;">nicknames</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">net_send_rdm</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ci</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"/nt"</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Error sending to %d"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">ci</span><span style="color: #0000FF;">})</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">else</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">prevname</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">ni</span>
<span style="color: #000000;">ni</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">msg</span><span style="color: #0000FF;">[</span><span style="color: #000000;">4</span><span style="color: #0000FF;">..</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">msg</span><span style="color: #0000FF;">)]</span>
<span style="color: #000000;">nicknames</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">ni</span>
<span style="color: #008080;">if</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">prevname</span><span style="color: #0000FF;">)</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">sendToAll</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"/j:"</span> <span style="color: #0000FF;">&</span> <span style="color: #000000;">ni</span> <span style="color: #0000FF;">&</span> <span style="color: #008000;">" has joined"</span><span style="color: #0000FF;">)</span>
<span style="color: #000080;font-style:italic;">-- send fake "has joined"s to the new joiner,
-- so that it can build a full members list (see allj)</span>
<span style="color: #008080;">for</span> <span style="color: #000000;">n</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">nicknames</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">do</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">n</span><span style="color: #0000FF;">!=</span><span style="color: #000000;">i</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">msg</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"/j:"</span> <span style="color: #0000FF;">&</span> <span style="color: #000000;">nicknames</span><span style="color: #0000FF;">[</span><span style="color: #000000;">n</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">&</span> <span style="color: #008000;">" has joined"</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">net_send_rdm</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ci</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">msg</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Error sending to connection %d"</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">ci</span><span style="color: #0000FF;">})</span>
<span style="color: #008080;">exit</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #008080;">else</span>
<span style="color: #000000;">sendToAll</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"/c:"</span> <span style="color: #0000FF;">&</span> <span style="color: #000000;">prevname</span> <span style="color: #0000FF;">&</span> <span style="color: #008000;">" has changed name to "</span> <span style="color: #0000FF;">&</span> <span style="color: #000000;">ni</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">elsif</span> <span style="color: #7060A8;">equal</span><span style="color: #0000FF;">(</span><span style="color: #000000;">msg</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"/d"</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">msg</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"/l:"</span><span style="color: #0000FF;">&</span> <span style="color: #000000;">ni</span> <span style="color: #0000FF;">&</span> <span style="color: #008000;">" has left"</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #000000;">msg</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">nicknames</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">..</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{}</span>
<span style="color: #000000;">connections</span><span style="color: #0000FF;">[</span><span style="color: #000000;">i</span><span style="color: #0000FF;">..</span><span style="color: #000000;">i</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{}</span>
<span style="color: #000000;">sendToAll</span><span style="color: #0000FF;">(</span><span style="color: #000000;">msg</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">exit</span>
<span style="color: #000080;font-style:italic;">-- Aside: bunch of popup and file transfer stuff was here in qchat.exw,
-- all ripped out in the name of keeping this short and sweet.</span>
<span style="color: #008080;">else</span>
<span style="color: #000000;">sendToAll</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ni</span> <span style="color: #0000FF;">&</span> <span style="color: #008000;">": "</span> <span style="color: #0000FF;">&</span> <span style="color: #000000;">msg</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- (Add nickname to message)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span>
<span style="color: #008080;">return</span> <span style="color: #004600;">IUP_IGNORE</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">close_cb</span><span style="color: #0000FF;">(</span><span style="color: #004080;">Ihandle</span> <span style="color: #000080;font-style:italic;">/*ih*/</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">shutDown</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">return</span> <span style="color: #004600;">IUP_DEFAULT</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #7060A8;">IupOpen</span><span style="color: #0000FF;">()</span>
<span style="color: #000000;">log_list</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupList</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"EXPAND=YES, CANFOCUS=NO, MULTIPLE=YES"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">statusbar</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupLabel</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Loading..."</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"EXPAND=HORIZONTAL"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">log_window</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupDialog</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">IupVbox</span><span style="color: #0000FF;">({</span><span style="color: #000000;">log_list</span><span style="color: #0000FF;">,</span><span style="color: #000000;">statusbar</span><span style="color: #0000FF;">}),</span>
<span style="color: #008000;">`TITLE="Chat Server", RASTERSIZE=400x400`</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">IupSetCallback</span><span style="color: #0000FF;">(</span><span style="color: #000000;">log_window</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"CLOSE_CB"</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">Icallback</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"close_cb"</span><span style="color: #0000FF;">))</span>
<span style="color: #7060A8;">IupShow</span><span style="color: #0000FF;">(</span><span style="color: #000000;">log_window</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">mainWindow_onOpen</span><span style="color: #0000FF;">()</span>
<span style="color: #000000;">timer</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupTimer</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">Icallback</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"timer_cb"</span><span style="color: #0000FF;">),</span> <span style="color: #000000;">500</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">IupMainLoop</span><span style="color: #0000FF;">()</span>
<span style="color: #7060A8;">IupClose</span><span style="color: #0000FF;">()</span>
<!--

View file

@ -0,0 +1,243 @@
(notonline)-->
<span style="color: #000080;font-style:italic;">--
-- demo\rosetta\ChatClient.exw
-- ===========================
--
-- translation of (qchat) QChat.exw
--
-- Probably best to run ChatServer.exw before this.
--</span>
<span style="color: #008080;">without</span> <span style="color: #008080;">js</span>
<span style="color: #008080;">constant</span> <span style="color: #004080;">bool</span> <span style="color: #000000;">bViaGateway</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">false</span>
<span style="color: #000080;font-style:italic;">--constant bool bViaGateway = true -- see IRC_Gateway.exw</span>
<span style="color: #008080;">include</span> <span style="color: #000000;">pGUI</span><span style="color: #0000FF;">.</span><span style="color: #000000;">e</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">dl</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">`Download rosetta\eulibnet\ from http://phix.x10.mx/pmwiki/pmwiki.php?n=Main.Eulibnet`</span>
<span style="color: #7060A8;">assert</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">get_file_type</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"eulibnet"</span><span style="color: #0000FF;">)=</span><span style="color: #004600;">FILETYPE_DIRECTORY</span><span style="color: #0000FF;">,</span><span style="color: #000000;">dl</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">include</span> <span style="color: #000000;">eulibnet</span><span style="color: #0000FF;">/</span><span style="color: #000000;">eulibnet</span><span style="color: #0000FF;">.</span><span style="color: #000000;">ew</span>
<span style="color: #004080;">Ihandle</span> <span style="color: #000000;">about</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">help</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">quit</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">nickle</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">nickname</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">login</span><span style="color: #0000FF;">,</span>
<span style="color: #000000;">memble</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">members</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">logle</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">log_list</span><span style="color: #0000FF;">,</span>
<span style="color: #000000;">messle</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">input</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">statusbar</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">chat_window</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">timer</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">conn</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span>
<span style="color: #000000;">conFlag</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span>
<span style="color: #004080;">bool</span> <span style="color: #000000;">allj</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">true</span> <span style="color: #000080;font-style:italic;">-- (suppress initial flurry of fake "has joined" messages)</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">nick</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">""</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">conTextBack</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">""</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">IP</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"127.0.0.1"</span><span style="color: #0000FF;">,</span>
<span style="color: #000000;">port</span> <span style="color: #0000FF;">=</span> <span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">bViaGateway</span><span style="color: #0000FF;">?</span><span style="color: #008000;">"29030"</span><span style="color: #0000FF;">:</span><span style="color: #008000;">"29029"</span><span style="color: #0000FF;">),</span> <span style="color: #000080;font-style:italic;">-- See IRC_Gateway.exw</span>
<span style="color: #000000;">timeout</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">20</span><span style="color: #0000FF;">,</span>
<span style="color: #000000;">IPaddress</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">IP</span> <span style="color: #0000FF;">&</span> <span style="color: #008000;">":"</span> <span style="color: #0000FF;">&</span> <span style="color: #000000;">port</span><span style="color: #0000FF;">,</span>
<span style="color: #000000;">cr</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"\r\n"</span><span style="color: #0000FF;">,</span>
<span style="color: #000000;">MAX_MSG</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">80</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">about_text</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"""
Translated by Pete Lomax from qchat by Andrew/Norman (and win32lib ==&gt; pGUI).
Uses Libnet by George Foot and Chad Catlet as wrapped by Ray Smith."
"""</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">about_cb</span><span style="color: #0000FF;">(</span><span style="color: #004080;">Ihandle</span> <span style="color: #000080;font-style:italic;">/*ih*/</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">IupMessage</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"About"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">about_text</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">return</span> <span style="color: #004600;">IUP_DEFAULT</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #008080;">constant</span> <span style="color: #000000;">help_text</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">"""
Make sure ChatServer.exw is running first...
Enter a nickname and press the Connect Button (or Return), then
enter your messages in the message area.
"""</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">help_cb</span><span style="color: #0000FF;">(</span><span style="color: #004080;">Ihandln</span> <span style="color: #000080;font-style:italic;">/*ih*/</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">IupMessage</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Chat client"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">help_text</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">return</span> <span style="color: #004600;">IUP_DEFAULT</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">message</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">IupSetStrAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">statusbar</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"TITLE"</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">message</span><span style="color: #0000FF;">)</span>
<span style="color: #000080;font-style:italic;">-- This should probably be cropped once it gets too long...</span>
<span style="color: #000000;">conTextBack</span> <span style="color: #0000FF;">&=</span> <span style="color: #000000;">message</span> <span style="color: #0000FF;">&</span> <span style="color: #000000;">cr</span>
<span style="color: #7060A8;">IupSetAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">log_list</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"VALUE"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">conTextBack</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">IupSetInt</span><span style="color: #0000FF;">(</span><span style="color: #000000;">log_list</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"SCROLLTOPOS"</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">conTextBack</span><span style="color: #0000FF;">))</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">sendMsg</span><span style="color: #0000FF;">(</span><span style="color: #004080;">sequence</span> <span style="color: #000000;">msg</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">conFlag</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"You must be connected to do this"</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">return</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">net_send_rdm</span><span style="color: #0000FF;">(</span><span style="color: #000000;">conn</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">msg</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Error sending message \'"</span> <span style="color: #0000FF;">&</span> <span style="color: #000000;">msg</span> <span style="color: #0000FF;">&</span> <span style="color: #008000;">"\'"</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">shutDown</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">if</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">nick</span><span style="color: #0000FF;">)</span> <span style="color: #0000FF;">></span> <span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Disconnecting from server..."</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">net_send_rdm</span><span style="color: #0000FF;">(</span><span style="color: #000000;">conn</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"/d"</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Error disconnecting from server!"</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Shutting down euLibnet..."</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">conn</span> <span style="color: #0000FF;">></span> <span style="color: #0000FF;">-</span><span style="color: #000000;">1</span> <span style="color: #008080;">then</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">net_closeconn</span><span style="color: #0000FF;">(</span><span style="color: #000000;">conn</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
<span style="color: #7060A8;">crash</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Error closing connection!"</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">net_shutdown</span><span style="color: #0000FF;">()</span> <span style="color: #008080;">then</span>
<span style="color: #7060A8;">crash</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Error shutting down euLibnet!"</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">conFlag</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">0</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">quit_cb</span><span style="color: #0000FF;">(</span><span style="color: #004080;">Ihandle</span> <span style="color: #000080;font-style:italic;">/*ih*/</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">shutDown</span><span style="color: #0000FF;">()</span>
<span style="color: #008080;">return</span> <span style="color: #004600;">IUP_CLOSE</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">connect_cb</span><span style="color: #0000FF;">(</span><span style="color: #004080;">Ihandle</span> <span style="color: #000080;font-style:italic;">/*ih*/</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">nick</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupGetAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">nickname</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"VALUE"</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">nick</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Opening connection..."</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">conn</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">net_openconn</span><span style="color: #0000FF;">(</span><span style="color: #000000;">NET_DRIVER_WSOCK_WIN</span><span style="color: #0000FF;">,</span> <span style="color: #004600;">NULL</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">conn</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">NULL</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Couldn't open connection."</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">return</span> <span style="color: #004600;">IUP_DEFAULT</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"done."</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Attempting to connect to chat server..."</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">ret</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">net_connect_wait_time</span><span style="color: #0000FF;">(</span><span style="color: #000000;">conn</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">IPaddress</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">timeout</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">ret</span> <span style="color: #0000FF;"><</span> <span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Error trying to establish connection."</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">return</span> <span style="color: #004600;">IUP_DEFAULT</span>
<span style="color: #008080;">elsif</span> <span style="color: #000000;">ret</span> <span style="color: #0000FF;">></span> <span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Timeout trying to establish connection."</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">return</span> <span style="color: #004600;">IUP_DEFAULT</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"done."</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">conFlag</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">1</span>
<span style="color: #7060A8;">IupSetStrAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">nickname</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"VALUE"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"Chat Client - "</span> <span style="color: #0000FF;">&</span> <span style="color: #000000;">nick</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">IupSetInt</span><span style="color: #0000FF;">({</span><span style="color: #000000;">nickname</span><span style="color: #0000FF;">,</span><span style="color: #000000;">login</span><span style="color: #0000FF;">},</span><span style="color: #008000;">"ACTIVE"</span><span style="color: #0000FF;">,</span><span style="color: #004600;">false</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">sendMsg</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"/n:"</span> <span style="color: #0000FF;">&</span> <span style="color: #000000;">nick</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">IupSetInt</span><span style="color: #0000FF;">(</span><span style="color: #000000;">timer</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"RUN"</span><span style="color: #0000FF;">,</span><span style="color: #004600;">true</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">IupSetAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">members</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"VALUE"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">nick</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">""</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">IupSetInt</span><span style="color: #0000FF;">(</span><span style="color: #000000;">input</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"ACTIVE"</span><span style="color: #0000FF;">,</span><span style="color: #004600;">true</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">IupSetFocus</span><span style="color: #0000FF;">(</span><span style="color: #000000;">input</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">return</span> <span style="color: #004600;">IUP_DEFAULT</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">mainWindow_onOpen</span><span style="color: #0000FF;">()</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Initializing euLibnet..."</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">net_init</span><span style="color: #0000FF;">()</span> <span style="color: #008080;">then</span> <span style="color: #7060A8;">crash</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Error initializing euLibnet!"</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"done."</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Initializing driver..."</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">net_initdriver</span><span style="color: #0000FF;">(</span><span style="color: #000000;">NET_DRIVER_WSOCK_WIN</span><span style="color: #0000FF;">)</span> <span style="color: #0000FF;">!=</span> <span style="color: #000000;">1</span> <span style="color: #008080;">then</span>
<span style="color: #7060A8;">crash</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Error initializing WinSock driver!"</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"done."</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">IupSetFocus</span><span style="color: #0000FF;">(</span><span style="color: #000000;">nickname</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">member_has_joined</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">joiner</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">mt</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupGetAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">members</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"VALUE"</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">ml</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">split</span><span style="color: #0000FF;">(</span><span style="color: #000000;">mt</span><span style="color: #0000FF;">,</span><span style="color: #008000;">'\n'</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">mt</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">join</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">unique</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">append</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ml</span><span style="color: #0000FF;">,</span><span style="color: #000000;">joiner</span><span style="color: #0000FF;">)),</span><span style="color: #008000;">'\n'</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">IupSetStrAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">members</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"VALUE"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">mt</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">procedure</span> <span style="color: #000000;">member_has_left</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">leaver</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">mt</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupGetAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">members</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"VALUE"</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">ml</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">split</span><span style="color: #0000FF;">(</span><span style="color: #000000;">mt</span><span style="color: #0000FF;">,</span><span style="color: #008000;">'\n'</span><span style="color: #0000FF;">)</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">k</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">find</span><span style="color: #0000FF;">(</span><span style="color: #000000;">leaver</span><span style="color: #0000FF;">,</span><span style="color: #000000;">ml</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">ml</span><span style="color: #0000FF;">[</span><span style="color: #000000;">k</span><span style="color: #0000FF;">..</span><span style="color: #000000;">k</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{}</span>
<span style="color: #7060A8;">IupSetStrAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">members</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"VALUE"</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">join</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ml</span><span style="color: #0000FF;">,</span><span style="color: #008000;">'\n'</span><span style="color: #0000FF;">))</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">timer_cb</span><span style="color: #0000FF;">(</span><span style="color: #004080;">Ihandle</span> <span style="color: #000080;font-style:italic;">/*timer*/</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">net_query_rdm</span><span style="color: #0000FF;">(</span><span style="color: #000000;">conn</span><span style="color: #0000FF;">)</span> <span style="color: #0000FF;">></span> <span style="color: #000000;">0</span> <span style="color: #008080;">then</span> <span style="color: #000080;font-style:italic;">--Check for message</span>
<span style="color: #004080;">sequence</span> <span style="color: #000000;">msg</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">net_receive_rdm</span><span style="color: #0000FF;">(</span><span style="color: #000000;">conn</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">MAX_MSG</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">msg</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;"><</span> <span style="color: #000000;">0</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Error receiving message!"</span><span style="color: #0000FF;">)</span>
<span style="color: #0000FF;">{}</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">net_ignore_rdm</span><span style="color: #0000FF;">(</span><span style="color: #000000;">conn</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">msg</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">msg</span><span style="color: #0000FF;">[</span><span style="color: #000000;">2</span><span style="color: #0000FF;">]</span>
<span style="color: #008080;">if</span> <span style="color: #7060A8;">equal</span><span style="color: #0000FF;">(</span><span style="color: #000000;">msg</span><span style="color: #0000FF;">,</span> <span style="color: #008000;">"/nt"</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Nickname "</span> <span style="color: #0000FF;">&</span> <span style="color: #000000;">nick</span> <span style="color: #0000FF;">&</span> <span style="color: #008000;">" already taken"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">nick</span> <span style="color: #0000FF;">&=</span> <span style="color: #008000;">"b"</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Trying "</span> <span style="color: #0000FF;">&</span> <span style="color: #000000;">nick</span> <span style="color: #0000FF;">&</span> <span style="color: #008000;">" instead"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Type /n:nickname to try a different one"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">sendMsg</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"/n:"</span> <span style="color: #0000FF;">&</span> <span style="color: #000000;">nick</span><span style="color: #0000FF;">)</span>
<span style="color: #000080;font-style:italic;">-- As per ChatServer, bunch of popup and file transfer stuff was
-- all ripped out in the name of keeping this short and sweet.</span>
<span style="color: #008080;">else</span>
<span style="color: #008080;">if</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">msg</span><span style="color: #0000FF;">)></span><span style="color: #000000;">3</span> <span style="color: #008080;">and</span> <span style="color: #7060A8;">equal</span><span style="color: #0000FF;">(</span><span style="color: #000000;">msg</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">..</span><span style="color: #000000;">3</span><span style="color: #0000FF;">],</span> <span style="color: #008000;">"/j:"</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">member_has_joined</span><span style="color: #0000FF;">(</span><span style="color: #000000;">msg</span><span style="color: #0000FF;">[</span><span style="color: #000000;">4</span><span style="color: #0000FF;">..</span><span style="color: #7060A8;">match</span><span style="color: #0000FF;">(</span><span style="color: #008000;">" has joined"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">msg</span><span style="color: #0000FF;">)-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">])</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">allj</span> <span style="color: #008080;">then</span> <span style="color: #008080;">return</span> <span style="color: #004600;">IUP_DEFAULT</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">msg</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">msg</span><span style="color: #0000FF;">[</span><span style="color: #000000;">4</span><span style="color: #0000FF;">..$]</span>
<span style="color: #008080;">elsif</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">msg</span><span style="color: #0000FF;">)></span><span style="color: #000000;">3</span> <span style="color: #008080;">and</span> <span style="color: #7060A8;">equal</span><span style="color: #0000FF;">(</span><span style="color: #000000;">msg</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">..</span><span style="color: #000000;">3</span><span style="color: #0000FF;">],</span> <span style="color: #008000;">"/l:"</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">member_has_left</span><span style="color: #0000FF;">(</span><span style="color: #000000;">msg</span><span style="color: #0000FF;">[</span><span style="color: #000000;">4</span><span style="color: #0000FF;">..</span><span style="color: #7060A8;">match</span><span style="color: #0000FF;">(</span><span style="color: #008000;">" has left"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">msg</span><span style="color: #0000FF;">)-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">])</span>
<span style="color: #000000;">msg</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">msg</span><span style="color: #0000FF;">[</span><span style="color: #000000;">4</span><span style="color: #0000FF;">..$]</span>
<span style="color: #008080;">elsif</span> <span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">msg</span><span style="color: #0000FF;">)></span><span style="color: #000000;">3</span> <span style="color: #008080;">and</span> <span style="color: #7060A8;">equal</span><span style="color: #0000FF;">(</span><span style="color: #000000;">msg</span><span style="color: #0000FF;">[</span><span style="color: #000000;">1</span><span style="color: #0000FF;">..</span><span style="color: #000000;">3</span><span style="color: #0000FF;">],</span> <span style="color: #008000;">"/c:"</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">then</span>
<span style="color: #004080;">string</span> <span style="color: #000000;">shcnts</span> <span style="color: #0000FF;">=</span> <span style="color: #008000;">" has changed name to "</span>
<span style="color: #004080;">integer</span> <span style="color: #000000;">k</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">match</span><span style="color: #0000FF;">(</span><span style="color: #000000;">shcnts</span><span style="color: #0000FF;">,</span><span style="color: #000000;">msg</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">member_has_left</span><span style="color: #0000FF;">(</span><span style="color: #000000;">msg</span><span style="color: #0000FF;">[</span><span style="color: #000000;">4</span><span style="color: #0000FF;">..</span><span style="color: #000000;">k</span><span style="color: #0000FF;">-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">])</span>
<span style="color: #000000;">member_has_joined</span><span style="color: #0000FF;">(</span><span style="color: #000000;">msg</span><span style="color: #0000FF;">[</span><span style="color: #000000;">k</span><span style="color: #0000FF;">+</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">shcnts</span><span style="color: #0000FF;">)-</span><span style="color: #000000;">1</span><span style="color: #0000FF;">..$])</span>
<span style="color: #000000;">msg</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">msg</span><span style="color: #0000FF;">[</span><span style="color: #000000;">4</span><span style="color: #0000FF;">..$]</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">message</span><span style="color: #0000FF;">(</span><span style="color: #000000;">msg</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #000000;">allj</span> <span style="color: #0000FF;">=</span> <span style="color: #004600;">false</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">return</span> <span style="color: #004600;">IUP_DEFAULT</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #008080;">function</span> <span style="color: #000000;">key_cb</span><span style="color: #0000FF;">(</span><span style="color: #004080;">Ihandle</span> <span style="color: #000000;">ih</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">atom</span> <span style="color: #000000;">c</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">c</span><span style="color: #0000FF;">=</span><span style="color: #004600;">K_ESC</span> <span style="color: #008080;">then</span> <span style="color: #000000;">shutDown</span><span style="color: #0000FF;">()</span> <span style="color: #008080;">return</span> <span style="color: #004600;">IUP_CLOSE</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">c</span><span style="color: #0000FF;">=</span><span style="color: #004600;">K_F1</span> <span style="color: #008080;">then</span> <span style="color: #008080;">return</span> <span style="color: #000000;">help_cb</span><span style="color: #0000FF;">(</span><span style="color: #004600;">NULL</span><span style="color: #0000FF;">)</span> <span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">c</span><span style="color: #0000FF;">=</span><span style="color: #008000;">'\r'</span> <span style="color: #008080;">then</span>
<span style="color: #008080;">if</span> <span style="color: #000000;">ih</span><span style="color: #0000FF;">=</span><span style="color: #000000;">input</span> <span style="color: #008080;">then</span>
<span style="color: #000000;">sendMsg</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">IupGetAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">input</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"VALUE"</span><span style="color: #0000FF;">))</span>
<span style="color: #7060A8;">IupSetAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">input</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"VALUE"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">""</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">elsif</span> <span style="color: #000000;">ih</span><span style="color: #0000FF;">=</span><span style="color: #000000;">nickname</span> <span style="color: #008080;">then</span>
<span style="color: #008080;">return</span> <span style="color: #000000;">connect_cb</span><span style="color: #0000FF;">(</span><span style="color: #000000;">ih</span><span style="color: #0000FF;">)</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span>
<span style="color: #008080;">return</span> <span style="color: #004600;">IUP_DEFAULT</span>
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span>
<span style="color: #7060A8;">IupOpen</span><span style="color: #0000FF;">()</span>
<span style="color: #000000;">about</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupButton</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"About"</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">Icallback</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"about_cb"</span><span style="color: #0000FF;">))</span>
<span style="color: #000000;">help</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupButton</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Help"</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">Icallback</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"help_cb"</span><span style="color: #0000FF;">))</span>
<span style="color: #000000;">quit</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupButton</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Exit"</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">Icallback</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"quit_cb"</span><span style="color: #0000FF;">))</span>
<span style="color: #000000;">nickle</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupLabel</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Nickname"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">nickname</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupText</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"EXPAND=HORIZONTAL"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">login</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupButton</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Connect"</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">Icallback</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"connect_cb"</span><span style="color: #0000FF;">))</span>
<span style="color: #000000;">memble</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupLabel</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Members online"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">members</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupText</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"EXPAND=VERTICAL, CANFOCUS=NO, MULTILINE=YES, SIZE=70x"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">logle</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupLabel</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Incoming text"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">log_list</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupText</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"EXPAND=YES, CANFOCUS=NO, MULTILINE=YES"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">messle</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupLabel</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Message"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">input</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupText</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"EXPAND=HORIZONTAL"</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">IupSetCallback</span><span style="color: #0000FF;">({</span><span style="color: #000000;">nickname</span><span style="color: #0000FF;">,</span><span style="color: #000000;">input</span><span style="color: #0000FF;">},</span><span style="color: #008000;">"KEY_CB"</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">Icallback</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"key_cb"</span><span style="color: #0000FF;">))</span>
<span style="color: #7060A8;">IupSetAttribute</span><span style="color: #0000FF;">(</span><span style="color: #000000;">input</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"CUEBANNER"</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"This Is Where You Type"</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">IupSetInt</span><span style="color: #0000FF;">(</span><span style="color: #000000;">input</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"NC"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">72</span><span style="color: #0000FF;">)</span> <span style="color: #000080;font-style:italic;">-- Max 72 characters allowed</span>
<span style="color: #7060A8;">IupSetInt</span><span style="color: #0000FF;">(</span><span style="color: #000000;">input</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"ACTIVE"</span><span style="color: #0000FF;">,</span><span style="color: #004600;">false</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">statusbar</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupLabel</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Loading..."</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"EXPAND=HORIZONTAL"</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">chat_window</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupDialog</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">IupVbox</span><span style="color: #0000FF;">({</span><span style="color: #7060A8;">IupHbox</span><span style="color: #0000FF;">({</span><span style="color: #000000;">about</span><span style="color: #0000FF;">,</span><span style="color: #000000;">help</span><span style="color: #0000FF;">,</span><span style="color: #000000;">quit</span><span style="color: #0000FF;">,</span><span style="color: #000000;">nickle</span><span style="color: #0000FF;">,</span><span style="color: #000000;">nickname</span><span style="color: #0000FF;">,</span><span style="color: #000000;">login</span><span style="color: #0000FF;">},</span>
<span style="color: #008000;">"NORMALIZESIZE=VERTICAL,GAP=10,MARGIN=5x5"</span><span style="color: #0000FF;">),</span>
<span style="color: #7060A8;">IupHbox</span><span style="color: #0000FF;">({</span><span style="color: #7060A8;">IupVbox</span><span style="color: #0000FF;">({</span><span style="color: #000000;">memble</span><span style="color: #0000FF;">,</span><span style="color: #000000;">members</span><span style="color: #0000FF;">}),</span>
<span style="color: #7060A8;">IupVbox</span><span style="color: #0000FF;">({</span><span style="color: #000000;">logle</span><span style="color: #0000FF;">,</span><span style="color: #000000;">log_list</span><span style="color: #0000FF;">})},</span>
<span style="color: #008000;">"NGAP=10,NMARGIN=5x5"</span><span style="color: #0000FF;">),</span>
<span style="color: #7060A8;">IupHbox</span><span style="color: #0000FF;">({</span><span style="color: #000000;">messle</span><span style="color: #0000FF;">,</span><span style="color: #000000;">input</span><span style="color: #0000FF;">},</span><span style="color: #008000;">"GAP=10,MARGIN=5x5"</span><span style="color: #0000FF;">),</span>
<span style="color: #000000;">statusbar</span><span style="color: #0000FF;">}),</span>
<span style="color: #008000;">`TITLE="Chat Client", RASTERSIZE=400x400`</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">IupSetCallback</span><span style="color: #0000FF;">(</span><span style="color: #000000;">chat_window</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"CLOSE_CB"</span><span style="color: #0000FF;">,</span><span style="color: #7060A8;">Icallback</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"quit_cb"</span><span style="color: #0000FF;">))</span>
<span style="color: #7060A8;">IupSetAttributeHandle</span><span style="color: #0000FF;">(</span><span style="color: #004600;">NULL</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"PARENTDIALOG"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">chat_window</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">IupShow</span><span style="color: #0000FF;">(</span><span style="color: #000000;">chat_window</span><span style="color: #0000FF;">)</span>
<span style="color: #000000;">mainWindow_onOpen</span><span style="color: #0000FF;">()</span>
<span style="color: #000000;">timer</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">IupTimer</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">Icallback</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"timer_cb"</span><span style="color: #0000FF;">),</span> <span style="color: #000000;">500</span><span style="color: #0000FF;">,</span> <span style="color: #004600;">false</span><span style="color: #0000FF;">)</span>
<span style="color: #7060A8;">IupMainLoop</span><span style="color: #0000FF;">()</span>
<span style="color: #7060A8;">IupClose</span><span style="color: #0000FF;">()</span>
<!--

View 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)

View file

@ -0,0 +1,81 @@
:- initialization chat_server(5000).
chat_server(Port) :-
tcp_socket(Socket),
tcp_bind(Socket, Port),
tcp_listen(Socket, 5),
tcp_open_socket(Socket, AcceptFd, _),
dispatch(AcceptFd).
dispatch(AcceptFd) :-
tcp_accept(AcceptFd, Socket, _),
thread_create(process_client(Socket, _), _, [detached(true)]),
dispatch(AcceptFd).
process_client(Socket, _) :-
setup_call_cleanup(
tcp_open_socket(Socket, Str),
handle_connection(Str),
close(Str)).
% a connection was made, get the username and add the streams so the
% client can be broadcast to.
handle_connection(Str) :-
send_msg(Str, msg_welcome, []),
repeat,
send_msg(Str, msg_username, []),
read_line_to_string(Str, Name),
connect_user(Name, Str), !.
% connections are stored here
:- dynamic(connected/2).
connect_user(Name, Str) :-
connected(Name, _),
send_msg(Str, msg_username_taken, []),
fail.
connect_user(Name, Str) :-
\+ connected(Name, _),
send_msg(Str, msg_welcome_name, Name),
% make sure that the connection is removed when the client leaves.
setup_call_cleanup(
assert(connected(Name, Str)),
(
broadcast(Name, msg_joined, Name),
chat_loop(Name, Str), !,
broadcast(Name, msg_left, Name)
),
retractall(connected(Name, _))
).
% wait for a line to be sent then broadcast to the rest of the clients
% finish this goal when the client disconnects (end of stream)
chat_loop(Name, Str) :-
read_line_to_string(Str, S),
dif(S, end_of_file),
broadcast(Name, msg_by_user, [Name, S]),
chat_loop(Name, Str).
chat_loop(_, Str) :- at_end_of_stream(Str).
% send a message to all connected clients except Name (the sender)
broadcast(Name, Msg, Params) :-
forall(
(connected(N, Str), dif(N, Name)),
(send_msg(Str, Msg, Params), send_msg(Str, msg_new_line, []))
).
send_msg(St, MsgConst, Params) :-
call(MsgConst, Msg),
format(St, Msg, Params),
flush_output(St).
% constants for the various message types that are sent
msg_welcome('Welcome to Chatalot\n\r').
msg_username('Please enter your nickname: ').
msg_welcome_name('Welcome ~p\n\r').
msg_joined(' -- "~w" has joined the chat --').
msg_left(' -- "~w" has left the chat. --').
msg_username_taken('That username is already taken, choose another\n\r').
msg_new_line('\n\r').
msg_by_user('~w> ~w').

View 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

View file

@ -0,0 +1,146 @@
chat_loop <- function(server, sockets, delay = 0.5) {
repeat {
Sys.sleep(delay) # Saves CPU resources
## Exhausts queue each iteration
while (in_queue(server))
sockets <- new_socket_entry(server, sockets)
## Update which sockets have sent messages
sockets <- check_messages(sockets)
## No sockets, nothing to do
if (nrow(sockets) == 0)
next
## No new messages, nothing to do
if (all(!sockets$message_ready))
next
sockets <- read_messages(sockets) # Messages are stored until sent
sockets <- drop_dead(sockets) # Dead = ready to read, but no data
## In case all sockets were dropped
if (nrow(sockets) == 0)
next
sockets <- update_nicknames(sockets)
sockets <- send_messages(sockets) # Only to users with nicknames
}
}
check_messages <- function(sockets) {
if (nrow(sockets) != 0)
sockets$message_ready <- socketSelect(sockets$conn, timeout = 0)
sockets
}
drop_dead <- function(sockets) {
lapply(with(sockets, conn[!alive]), close)
dropped <- with(sockets, nickname[nickname_exists(sockets) & !alive])
sockets <- sockets[sockets$alive, ]
if (length(dropped) != 0) {
send_named(sockets, paste0(dropped, " has disconnected."))
}
sockets
}
in_queue <- function(server) socketSelect(list(server), timeout = 0)
is_valid_name <- function(nicks) gsub("[A-Za-z0-9]*", "", nicks) == ""
message_exists <- function(sockets) !is.na(sockets$message)
new_row <- function(df) {
df[nrow(df) + 1, ] <- NA
df
}
new_socket_entry <- function(server, sockets) {
sockets <- new_row(sockets)
n <- nrow(sockets)
within(sockets, {
conn[[n]] <- new_user(server)
alive[n] <- TRUE
message_ready[n] <- FALSE
})
}
new_user <- function(server) {
conn <- socketAccept(server)
writeLines("Hello! Please enter a nickname.", conn)
conn
}
nickname_exists <- function(sockets) !is.na(sockets$nickname)
read_messages <- function(sockets) {
if (all(!sockets$message_ready))
return(sockets)
msgs <- lapply(with(sockets, conn[message_ready]), readLines, n = 1)
empty_msgs <- sapply(msgs, identical, character(0))
sockets <- within(sockets, alive[message_ready & empty_msgs] <- FALSE)
msgs <- unlist(ifelse(empty_msgs, NA, msgs))
within(sockets, message[message_ready] <- msgs)
}
send_messages <- function(sockets) {
named_message <- message_exists(sockets) & nickname_exists(sockets)
if (all(!named_message))
return(sockets)
rows <- which(named_message)
socksub <- sockets[rows, ]
time <- format(Sys.time(), "[%H:%M:%S] ")
with(socksub, send_named(sockets, paste0(time, nickname, ": ", message)))
within(sockets, message[rows] <- NA)
}
send_named <- function(sockets, msg) {
has_nickname <- nickname_exists(sockets)
invisible(lapply(sockets$conn[has_nickname], writeLines, text = msg))
}
start_chat_server <- function(port = 50525) {
server <- serverSocket(port) # Start listening
on.exit(closeAllConnections()) # Cleanup connections
## All socket data is stored and passed using this object
sockets <- data.frame(conn = I(list()), nickname = character(),
message = character(), alive = logical(),
message_ready = logical())
## Main event loop
chat_loop(server, sockets)
}
update_nicknames <- function(sockets) {
sent_nickname <- message_exists(sockets) & !nickname_exists(sockets)
nickname_valid <- is_valid_name(sockets$message)
if (all(!sent_nickname))
return(sockets)
is_taken <- with(sockets, (tolower(message) %in% tolower(sockets$nickname)) &
!is.na(message))
sent_ok <- sent_nickname & nickname_valid & !is_taken
sockets <- within(sockets, {
nickname[sent_ok] <- message[sent_ok]
message[sent_nickname] <- NA
lapply(conn[sent_nickname & !nickname_valid], writeLines,
text = "Alphanumeric characters only. Try again.")
lapply(conn[is_taken], writeLines,
text = "Name already taken. Try again.")
})
if (any(sent_ok))
send_named(sockets, paste0(sockets$nickname[sent_ok], " has connected."))
sockets
}
start_chat_server()

View file

@ -0,0 +1,21 @@
#lang racket
(define outs (list (current-output-port)))
(define ((tell-all who o) line)
(for ([c outs] #:unless (eq? o c)) (displayln (~a who ": " line) c)))
(define ((client i o))
(define nick (begin (display "Nick: " o) (read-line i)))
(define tell (tell-all nick o))
(let loop ([line "(joined)"])
(if (eof-object? line)
(begin (tell "(left)") (set! outs (remq o outs)) (close-output-port o))
(begin (tell line) (loop (read-line i))))))
(define (chat-server listener)
(define-values [i o] (tcp-accept listener))
(for ([p (list i o)]) (file-stream-buffer-mode p 'none))
(thread (client i o)) (set! outs (cons o outs)) (chat-server listener))
(void (thread (λ() (chat-server (tcp-listen 12321)))))
((client (current-input-port) (current-output-port)))

View file

@ -0,0 +1,43 @@
react {
my %connections;
whenever IO::Socket::Async.listen('localhost', 4004) -> $conn {
my $name;
$conn.print: "Please enter your name: ";
whenever $conn.Supply.lines -> $message {
if !$name {
if %connections{$message} {
$conn.print: "Name already taken, choose another one: ";
}
else {
$name = $message;
%connections{$name} = $conn;
broadcast "+++ %s arrived +++", $name;
}
}
else {
broadcast "%s> %s", $name, $message;
}
LAST {
broadcast "--- %s left ---", $name;
%connections{$name}:delete;
$conn.close ;
}
QUIT {
default {
say "oh no, $_";
}
}
}
}
sub broadcast ($format, $from, *@message) {
my $text = sprintf $format, $from, |@message;
say $text;
for %connections.kv -> $name, $conn {
$conn.print: "$text\n" if $name ne $from;
}
}
}

View 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

View file

@ -0,0 +1,96 @@
use std::collections::HashMap;
use std::io;
use std::io::prelude::*;
use std::io::BufReader;
use std::net::{TcpListener, TcpStream};
use std::sync::{Arc, RwLock};
use std::thread;
type Username = String;
/// Sends a message to all clients except the sending client.
fn broadcast_message(
user: &str,
clients: &mut HashMap<String, TcpStream>,
message: &str,
) -> io::Result<()> {
for (client, stream) in clients.iter_mut() {
if client != user {
writeln!(stream, "{}", message)?;
}
}
Ok(())
}
fn chat_loop(listener: &TcpListener) -> io::Result<()> {
let local_clients: Arc<RwLock<HashMap<Username, TcpStream>>> =
Arc::new(RwLock::new(HashMap::new()));
println!("Accepting connections on {}", listener.local_addr()?.port());
for stream in listener.incoming() {
match stream {
Ok(stream) => {
let client_clients = Arc::clone(&local_clients);
thread::spawn(move || -> io::Result<()> {
let mut reader = BufReader::new(stream.try_clone()?);
let mut writer = stream;
let mut name = String::new();
loop {
write!(writer, "Please enter a username: ")?;
reader.read_line(&mut name)?;
name = name.trim().to_owned();
let clients = client_clients.read().unwrap();
if !clients.contains_key(&name) {
writeln!(writer, "Welcome, {}!", &name)?;
break;
}
writeln!(writer, "That username is taken.")?;
name.clear();
}
{
let mut clients = client_clients.write().unwrap();
clients.insert(name.clone(), writer);
broadcast_message(
&name,
&mut *clients,
&format!("{} has joined the chat room.", &name),
)?;
}
for line in reader.lines() {
let mut clients = client_clients.write().unwrap();
broadcast_message(&name, &mut *clients, &format!("{}: {}", &name, line?))?;
}
{
let mut clients = client_clients.write().unwrap();
clients.remove(&name);
broadcast_message(
&name,
&mut *clients,
&format!("{} has left the chat room.", &name),
)?;
}
Ok(())
});
}
Err(e) => {
println!("Connection failed: {}", e);
}
}
}
Ok(())
}
fn main() {
let listener = TcpListener::bind(("localhost", 7000)).unwrap();
chat_loop(&listener).unwrap();
}

View 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

View file

@ -0,0 +1,134 @@
Imports System.Net.Sockets
Imports System.Text
Imports System.Threading
Module Module1
Class State
Private ReadOnly client As TcpClient
Private ReadOnly sb As New StringBuilder
Public Sub New(name As String, client As TcpClient)
Me.Name = name
Me.client = client
End Sub
Public ReadOnly Property Name As String
Public Sub Send(text As String)
Dim bytes = Encoding.ASCII.GetBytes(String.Format("{0}" & vbCrLf, text))
client.GetStream().Write(bytes, 0, bytes.Length)
End Sub
End Class
ReadOnly connections As New Dictionary(Of Integer, State)
Dim listen As TcpListener
Dim serverThread As Thread
Sub Main()
listen = New TcpListener(Net.IPAddress.Parse("127.0.0.1"), 4004)
serverThread = New Thread(New ThreadStart(AddressOf DoListen))
serverThread.Start()
End Sub
Private Sub DoListen()
listen.Start()
Console.WriteLine("Server: Started server")
Do
Console.Write("Server: Waiting...")
Dim client = listen.AcceptTcpClient()
Console.WriteLine(" Connected")
' New thread with client
Dim clientThread As New Thread(New ParameterizedThreadStart(AddressOf DoClient))
clientThread.Start(client)
Loop
End Sub
Private Sub DoClient(client As TcpClient)
Console.WriteLine("Client (Thread: {0}): Connected!", Thread.CurrentThread.ManagedThreadId)
Dim bytes = Encoding.ASCII.GetBytes("Enter name: ")
client.GetStream().Write(bytes, 0, bytes.Length)
Dim done As Boolean
Dim name As String
Do
If Not client.Connected Then
Console.WriteLine("Client (Thread: {0}): Terminated!", Thread.CurrentThread.ManagedThreadId)
client.Close()
Thread.CurrentThread.Abort() ' Kill thread
End If
name = Receive(client)
done = True
For Each cl In connections
Dim state = cl.Value
If state.Name = name Then
bytes = Encoding.ASCII.GetBytes("Name already registered. Please enter your name: ")
client.GetStream().Write(bytes, 0, bytes.Length)
done = False
End If
Next
Loop While Not done
connections.Add(Thread.CurrentThread.ManagedThreadId, New State(name, client))
Console.WriteLine(vbTab & "Total connections: {0}", connections.Count)
Broadcast(String.Format("+++ {0} arrived +++", name))
Do
Dim text = Receive(client)
If text = "/quit" Then
Broadcast(String.Format("Connection from {0} closed.", name))
connections.Remove(Thread.CurrentThread.ManagedThreadId)
Console.WriteLine(vbTab & "Total connections: {0}", connections.Count)
Exit Do
End If
If Not client.Connected Then
Exit Do
End If
Broadcast(String.Format("{0}> {1}", name, text))
Loop
Console.WriteLine("Client (Thread: {0}): Terminated!", Thread.CurrentThread.ManagedThreadId)
client.Close()
Thread.CurrentThread.Abort()
End Sub
Private Function Receive(client As TcpClient) As String
Dim sb As New StringBuilder
Do
If client.Available > 0 Then
While client.Available > 0
Dim ch = Chr(client.GetStream.ReadByte())
If ch = vbCr Then
' ignore
Continue While
End If
If ch = vbLf Then
Return sb.ToString()
End If
sb.Append(ch)
End While
' pause
Thread.Sleep(100)
End If
Loop
End Function
Private Sub Broadcast(text As String)
Console.WriteLine(text)
For Each client In connections
If client.Key <> Thread.CurrentThread.ManagedThreadId Then
Dim state = client.Value
state.Send(text)
End If
Next
End Sub
End Module

View file

@ -0,0 +1,199 @@
/* chat_server.wren */
class Clients {
foreign static max
foreign static count
foreign static isActive(vmi)
foreign static connfd(vmi)
foreign static uid(vmi)
foreign static name(vmi)
foreign static setName(vmi, s)
foreign static printAddr(vmi)
foreign static delete(vmi)
}
class Mutex {
foreign static clientsLock()
foreign static clientsUnlock()
foreign static topicLock()
foreign static topicUnlock()
}
class Chat {
// send message to all clients but the sender
static sendMessage(s, uid) {
Mutex.clientsLock()
for (i in 0...Clients.max) {
if (Clients.isActive(i) && Clients.uid(i) != uid) {
if (write(Clients.connfd(i), s, s.bytes.count) < 0) {
System.print("Write to descriptor %(Clients.connfd(i)) failed.")
break
}
}
}
Mutex.clientsUnlock()
}
// send message to all clients
static sendMessageAll(s) {
Mutex.clientsLock()
for (i in 0...Clients.max) {
if (Clients.isActive(i)) {
if (write(Clients.connfd(i), s, s.bytes.count) < 0) {
System.print("Write to descriptor %(Clients.connfd(i)) failed.")
break
}
}
}
Mutex.clientsUnlock()
}
// send message to sender
static sendMessageSelf(s, connfd) {
if (write(connfd, s, s.bytes.count) < 0) {
Fiber.abort("Write to descriptor %(connfd) failed.")
}
}
// send message to client
static sendMessageClient(s, uid) {
Mutex.clientsLock()
for (i in 0...Clients.max) {
if (Clients.isActive(i) && Clients.uid(i) == uid) {
if (write(Clients.connfd(i), s, s.bytes.count) < 0) {
System.print("Write to descriptor %(Clients.connfd(i)) failed.")
break
}
}
}
Mutex.clientsUnlock()
}
// send list of active clients
static sendActiveClients(connfd) {
Mutex.clientsLock()
for (i in 0...Clients.max) {
if (Clients.isActive(i)) {
var s = "<< [%(Clients.uid(i))] %(Clients.name(i))\r\n"
sendMessageSelf(s, connfd)
}
}
Mutex.clientsUnlock()
}
// handle all communication with the client
static handleClient(vmi) {
if (!Clients.isActive(vmi)) {
Fiber.abort("The client handled by VM[%(vmi)] is inactive.")
}
var connfd = Clients.connfd(vmi)
var uid = Clients.uid(vmi)
var name = Clients.name(vmi)
System.write("<< accept ")
Clients.printAddr(vmi)
System.print(" referenced by %(uid)")
var buffOut = "<< %(name) has joined\r\n"
sendMessageAll(buffOut)
Mutex.topicLock()
if (topic != "") {
buffOut = "<< topic: %(topic)\r\n"
sendMessageSelf(buffOut, connfd)
}
Mutex.topicUnlock()
sendMessageSelf("<< see /help for assistance\r\n", connfd)
/* receive input from client */
var buffIn = ""
while ((buffIn = read(connfd, bufferSize/2 - 1)) && buffIn.bytes.count > 0) {
buffOut = ""
buffIn = buffIn.trimEnd("\r\n")
/* ignore empty buffer */
if (buffIn == "") continue
/* special options */
if (buffIn[0] == "/") {
var split = buffIn.split(" ")
var command = split[0]
if (command == "/quit") {
break
} else if (command == "/ping") {
sendMessageSelf("<< pong\r\n", connfd)
} else if (command == "/topic") {
if (split.count > 0) {
Mutex.topicLock()
topic = split[1..-1].join(" ")
Mutex.topicUnlock()
buffOut = "<< topic changed to: %(topic)\r\n"
sendMessageAll(buffOut)
} else {
sendMessageSelf("<< message cannot be null\r\n", connfd)
}
} else if (command == "/nick") {
if (split.count > 0) {
var newName = split[1..-1].join(" ")
buffOut = "<< %(name) is now known as %(newName)\r\n"
Clients.setName(vmi, newName)
name = newName
sendMessageAll(buffOut)
} else {
sendMessageSelf("<< name cannot be null\r\n", connfd)
}
} else if (command == "/msg") {
if (split.count > 0) {
var toUid = Num.fromString(split[1])
if (split.count > 1) {
buffOut = "[PM][%(name)] "
buffOut = buffOut + split[2..-1].join(" ") + "\r\n"
sendMessageClient(buffOut, toUid)
} else {
sendMessageSelf("<< message cannot be null\r\n", connfd)
}
} else {
sendMessageSelf("<< reference cannot be null\r\n", connfd)
}
} else if (command == "/list") {
buffOut = "<< clients %(Clients.count)\r\n"
sendMessageSelf(buffOut, connfd)
sendActiveClients(connfd)
} else if (command == "/help") {
buffOut = ""
buffOut = buffOut + "<< /quit Quit chatroom\r\n"
buffOut = buffOut + "<< /ping Server test\r\n"
buffOut = buffOut + "<< /topic <message> Set chat topic\r\n"
buffOut = buffOut + "<< /nick <name> Change nickname\r\n"
buffOut = buffOut + "<< /msg <reference> <message> Send private message\r\n"
buffOut = buffOut + "<< /list Show active clients\r\n"
buffOut = buffOut + "<< /help Show help\r\n"
sendMessageSelf(buffOut, connfd)
} else {
sendMessageSelf("<< unknown command\r\n", connfd)
}
} else {
/* send message */
buffOut = "[%(name)] %(buffIn)\r\n"
sendMessage(buffOut, uid)
}
}
/* close connection */
buffOut = "<< [%(name)] has left\r\n"
sendMessageAll(buffOut)
close(connfd)
/* delete client from queue and yield thread (from C side) */
System.write("<< quit ")
Clients.printAddr(vmi)
System.print(" referenced by %(uid)")
Clients.delete(vmi)
}
foreign static topic
foreign static topic=(s)
foreign static bufferSize
foreign static write(connfd, buf, count)
foreign static read(connfd, count)
foreign static close(connfd)
}

View file

@ -0,0 +1,353 @@
/* gcc chat_server.c -o chat_server -lpthread -lwren -lm */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <pthread.h>
#include <sys/types.h>
#include <signal.h>
#include <wren.h>
#define MAX_CLIENTS 10
#define BUFFER_SZ 2048
static _Atomic unsigned int cli_count = 0;
static int listenfd = 0, uid = 10;
char *script = NULL;
/* Client structure */
typedef struct {
struct sockaddr_in addr; /* Client remote address */
int connfd; /* Connection file descriptor */
int uid; /* Client unique identifier */
int vmi; /* The index of the VM which handles this client */
char name[32]; /* Client name */
} client_t;
client_t *clients[MAX_CLIENTS];
pthread_mutex_t clients_mutex = PTHREAD_MUTEX_INITIALIZER;
static char topic[BUFFER_SZ/2];
pthread_mutex_t topic_mutex = PTHREAD_MUTEX_INITIALIZER;
WrenVM* vms[MAX_CLIENTS]; // array of VMs
/* add client to queue */
void queue_add(client_t *cl){
pthread_mutex_lock(&clients_mutex);
for (int i = 0; i < MAX_CLIENTS; ++i) {
if (!clients[i]) {
cl->vmi = i;
clients[i] = cl;
break;
}
}
pthread_mutex_unlock(&clients_mutex);
}
/* Delete client from queue */
void queue_delete(int uid){
pthread_mutex_lock(&clients_mutex);
for (int i = 0; i < MAX_CLIENTS; ++i) {
if (clients[i]) {
if (clients[i]->uid == uid) {
clients[i] = NULL;
break;
}
}
}
pthread_mutex_unlock(&clients_mutex);
}
/* print ip address */
void print_client_addr(struct sockaddr_in addr){
printf("%d.%d.%d.%d",
addr.sin_addr.s_addr & 0xff,
(addr.sin_addr.s_addr & 0xff00) >> 8,
(addr.sin_addr.s_addr & 0xff0000) >> 16,
(addr.sin_addr.s_addr & 0xff000000) >> 24);
}
/* enable Wren to handle all client communication */
void *handle_client(void *arg) {
client_t *cli = (client_t *)arg;
cli_count++;
int vmi = cli->vmi;
WrenHandle *callHandle = wrenMakeCallHandle(vms[vmi], "handleClient(_)");
wrenEnsureSlots(vms[vmi], 2);
wrenGetVariable(vms[vmi], "main", "Chat", 0);
wrenSetSlotDouble(vms[vmi], 1, (double)vmi);
wrenCall(vms[vmi], callHandle);
}
/* C <= Wren interface functions */
void C_max(WrenVM* vm) {
wrenSetSlotDouble(vm, 0, (double)MAX_CLIENTS);
}
void C_count(WrenVM* vm) {
wrenSetSlotDouble(vm, 0, (double)cli_count);
}
void C_isActive(WrenVM* vm) {
int vmi = (int)wrenGetSlotDouble(vm, 1);
bool res = clients[vmi] != NULL;
wrenSetSlotBool(vm, 0, res);
}
void C_connfd(WrenVM* vm) {
int vmi = (int)wrenGetSlotDouble(vm, 1);
wrenSetSlotDouble(vm, 0, (double)clients[vmi]->connfd);
}
void C_uid(WrenVM* vm) {
int vmi = (int)wrenGetSlotDouble(vm, 1);
wrenSetSlotDouble(vm, 0, (double)clients[vmi]->uid);
}
void C_name(WrenVM* vm) {
int vmi = (int)wrenGetSlotDouble(vm, 1);
wrenSetSlotString(vm, 0, (const char *)clients[vmi]->name);
}
void C_setName(WrenVM* vm) {
int vmi = (int)wrenGetSlotDouble(vm, 1);
const char *name = wrenGetSlotString(vm, 2);
size_t size = sizeof(clients[vmi]->name);
strncpy(clients[vmi]->name, name, size);
clients[vmi]->name[size-1] = '\0';
}
void C_clientsLock(WrenVM* vm) {
pthread_mutex_lock(&clients_mutex);
}
void C_clientsUnlock(WrenVM* vm) {
pthread_mutex_unlock(&clients_mutex);
}
void C_topicLock(WrenVM* vm) {
pthread_mutex_lock(&topic_mutex);
}
void C_topicUnlock(WrenVM* vm) {
pthread_mutex_unlock(&topic_mutex);
}
void C_printAddr(WrenVM* vm) {
int vmi = (int)wrenGetSlotDouble(vm, 1);
print_client_addr(clients[vmi]->addr);
}
void C_topic(WrenVM* vm) {
wrenSetSlotString(vm, 0, (const char *)topic);
}
void C_setTopic(WrenVM* vm) {
const char *t = wrenGetSlotString(vm, 1);
strncpy(topic, t, sizeof(topic));
topic[sizeof(topic)-1] = '\0';
}
void C_bufferSize(WrenVM* vm) {
wrenSetSlotDouble(vm, 0, (double)BUFFER_SZ);
}
void C_write(WrenVM* vm) {
int fd = (int)wrenGetSlotDouble(vm, 1);
const void *buf = (const void *)wrenGetSlotString(vm, 2);
size_t count = (size_t)wrenGetSlotDouble(vm, 3);
ssize_t res = write(fd, buf, count);
wrenSetSlotDouble(vm, 0, (double)res);
}
void C_read(WrenVM* vm) {
char buf[BUFFER_SZ / 2];
int fd = (int)wrenGetSlotDouble(vm, 1);
size_t count = (size_t)wrenGetSlotDouble(vm, 2);
ssize_t rlen = read(fd, buf, count);
buf[rlen] = '\0';
wrenSetSlotString(vm, 0, (const char *)buf);
}
void C_close(WrenVM* vm) {
int connfd = (int)wrenGetSlotDouble(vm, 1);
close(connfd);
}
void C_delete(WrenVM* vm) {
int vmi = (int)wrenGetSlotDouble(vm, 1);
client_t *cli = clients[vmi];
queue_delete(cli->uid);
free(cli);
cli_count--;
pthread_detach(pthread_self());
}
WrenForeignMethodFn bindForeignMethod(
WrenVM* vm,
const char* module,
const char* className,
bool isStatic,
const char* signature) {
if (strcmp(module, "main") == 0) {
if (strcmp(className, "Clients") == 0) {
if (isStatic && strcmp(signature, "max") == 0) return C_max;
if (isStatic && strcmp(signature, "count") == 0) return C_count;
if (isStatic && strcmp(signature, "isActive(_)") == 0) return C_isActive;
if (isStatic && strcmp(signature, "connfd(_)") == 0) return C_connfd;
if (isStatic && strcmp(signature, "uid(_)") == 0) return C_uid;
if (isStatic && strcmp(signature, "name(_)") == 0) return C_name;
if (isStatic && strcmp(signature, "setName(_,_)") == 0) return C_setName;
if (isStatic && strcmp(signature, "printAddr(_)") == 0) return C_printAddr;
if (isStatic && strcmp(signature, "delete(_)") == 0) return C_delete;
} else if (strcmp(className, "Mutex") == 0) {
if (isStatic && strcmp(signature, "clientsLock()") == 0) return C_clientsLock;
if (isStatic && strcmp(signature, "clientsUnlock()") == 0) return C_clientsUnlock;
if (isStatic && strcmp(signature, "topicLock()") == 0) return C_topicLock;
if (isStatic && strcmp(signature, "topicUnlock()") == 0) return C_topicUnlock;
} else if (strcmp(className, "Chat") == 0) {
if (isStatic && strcmp(signature, "topic") == 0) return C_topic;
if (isStatic && strcmp(signature, "topic=(_)") == 0) return C_setTopic;
if (isStatic && strcmp(signature, "bufferSize") == 0) return C_bufferSize;
if (isStatic && strcmp(signature, "write(_,_,_)") == 0) return C_write;
if (isStatic && strcmp(signature, "read(_,_)") == 0) return C_read;
if (isStatic && strcmp(signature, "close(_)") == 0) return C_close;
}
}
return NULL;
}
static void writeFn(WrenVM* vm, const char* text) {
printf("%s", text);
}
void errorFn(WrenVM* vm, WrenErrorType errorType, const char* module, const int line, const char* msg) {
switch (errorType) {
case WREN_ERROR_COMPILE:
printf("[%s line %d] [Error] %s\n", module, line, msg);
break;
case WREN_ERROR_STACK_TRACE:
printf("[%s line %d] in %s\n", module, line, msg);
break;
case WREN_ERROR_RUNTIME:
printf("[Runtime Error] %s\n", msg);
break;
}
}
char *readFile(const char *fileName) {
FILE *f = fopen(fileName, "r");
fseek(f, 0, SEEK_END);
long fsize = ftell(f);
rewind(f);
char *script = malloc(fsize + 1);
fread(script, 1, fsize, f);
fclose(f);
script[fsize] = 0;
return script;
}
void catch_ctrl_c(int sig) {
/* clean up and exit */
for (int i = 0; i < MAX_CLIENTS; ++i) {
wrenFreeVM(vms[i]);
if (clients[i]) {
close(clients[i]->connfd);
free(clients[i]);
}
}
close(listenfd);
free(script);
printf("\n<[ SERVER ENDED ]>\n");
exit(EXIT_SUCCESS);
}
int main(int argc, char **argv) {
WrenConfiguration config;
wrenInitConfiguration(&config);
config.writeFn = &writeFn;
config.errorFn = &errorFn;
config.bindForeignMethodFn = &bindForeignMethod;
const char* module = "main";
const char* fileName = "chat_server.wren";
script = readFile(fileName);
/* config the VMs and interpret the script */
for (int i = 0; i < MAX_CLIENTS; ++i) {
vms[i] = wrenNewVM(&config);
wrenInterpret(vms[i], module, script);
}
/* prepare to start the server */
int connfd = 0;
struct sockaddr_in serv_addr;
struct sockaddr_in cli_addr;
pthread_t tid;
/* socket settings */
listenfd = socket(AF_INET, SOCK_STREAM, 0);
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(5000);
/* ignore pipe signals */
signal(SIGPIPE, SIG_IGN);
/* catch ctrl-c being pressed */
signal(SIGINT, catch_ctrl_c);
/* bind */
if (bind(listenfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0) {
perror("Socket binding failed");
return EXIT_FAILURE;
}
/* listen */
if (listen(listenfd, 10) < 0) {
perror("Socket listening failed");
return EXIT_FAILURE;
}
printf("<[ SERVER STARTED ]>\n");
/* accept clients */
while (1) {
socklen_t clilen = sizeof(cli_addr);
connfd = accept(listenfd, (struct sockaddr*)&cli_addr, &clilen);
/* check if max clients is reached */
if ((cli_count + 1) == MAX_CLIENTS) {
printf("<< max clients reached\n");
printf("<< reject ");
print_client_addr(cli_addr);
printf("\n");
close(connfd);
continue;
}
/* client settings */
client_t *cli = (client_t *)malloc(sizeof(client_t));
cli->addr = cli_addr;
cli->connfd = connfd;
cli->uid = uid++;
sprintf(cli->name, "%d", cli->uid);
/* add client to the queue and fork thread */
queue_add(cli);
pthread_create(&tid, NULL, &handle_client, (void*)cli);
/* reduce CPU usage */
sleep(1);
}
return 0;
}

View file

@ -0,0 +1,49 @@
const PORT=23;
var users=Dictionary(); // ( handle:socket, ...)
pipe:=Thread.Pipe(); // how server tells thread to connect to user
fcn accept(pipe){ // a thread waiting for the server to send a socket
while(socket:=pipe.read()){
println("Somebody is connecting ...");
socket.read(); // telnet stuff
while(True){ // get credentials
reg name;
socket.write("Your handle: "); // bottle neck
try{ name = socket.read().text.strip() } catch(IOError){ continue }
if(users.holds(name)) socket.write("Handle is already in use.\n");
else if(name){
users[name] = socket;
chat.launch(name,socket); // thread
broadcast(name, "+++ %s arrived +++".fmt(name));
break; // wait for next connection
}
}//while
}//while
}.launch(pipe); // thread
fcn chat(name,socket){ // a thread, one per user
try{
socket.write("^D to disconnect\n");
while(True){
message:=socket.read().text.strip();
if(message=="\xff\xec") break; // ^D to disconnect.
broadcast(name, "%s> %s".fmt(name,message));
}
}catch{} // eg socket pukes
users.del(name); socket.close();
broadcast(name, "--- %s leaves ---".fmt(name));
}
// Send a message to all users from the given name.
fcn broadcast(name, message){ // called from user thread
println(message); // log message to server console
users.pump(Void,'wrap([(toName,socket)]){
if(toName != name) try{ socket.write(message + "\n") } catch(IOError){}
});
}
// Set up the server socket.
server:=Network.TCPServerSocket.open(PORT);
println("Listening on %s:%s".fmt(server.hostname,server.port));
server.listen(pipe); // Main event loop