September Morn Update

This commit is contained in:
Ingy döt Net 2019-09-12 10:33:56 -07:00
parent 4e2d22a71d
commit aac6731f2c
6856 changed files with 141342 additions and 21127 deletions

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,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,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,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