DEV Community

FaxriddinMaxmadiyorov
FaxriddinMaxmadiyorov

Posted on • Edited on

Chat Server in Ruby Sockets

I am going to build a chat server in Ruby's sockets library. Before this, let's clarify what sockets are. Sockets are the endpoints of the communication channels. There are two types of sockets:

  1. Stream sockets - These sockets use Transmission Control Protocol (TCP) for communication. They provide a reliable, connection-oriented communication channel. Web servers and clients (HTTP/HTTPS) use this kind of sockets.
  2. Datagram sockets - These sockets use User Datagram protocol for communication. They are less reliable than TCP sockets and are used in streaming.

Chat application contains two parts: server and client; We will organize server part first:
Create TCPServer, it accepts infinite signals, i.e it must be alive, exchange data.
Create Socket client, it connects to server, exchange data

Our first example shows synchronous chat (chat and clients sides have to wait each other to send a message, each side of the connection waits for the other to send a message before it proceeds, creating a sort of "deadlock" where both sides are waiting.)

# server.rb require 'socket' server = TCPServer.new('localhost', 3333) puts "TCPServer is running port on 3333 ..." client = server.accept puts "Client connected." loop do message = client.gets.chomp puts "Client: #{message}" break if message == 'exit' response = gets.chomp client.puts response break if response == 'exit' end client.close puts "Client disconnected." 
Enter fullscreen mode Exit fullscreen mode
# client.rb require 'socket' client = TCPSocket.new('localhost', 3333) puts "Connected to server..." loop do message = gets.chomp client.puts message # Exit if the message is 'exit' break if message == 'exit' # Receive and print the server's response response = client.gets.chomp puts "Server: #{response}" break if response == 'exit' end client.close puts "Disconnected from server." 
Enter fullscreen mode Exit fullscreen mode

To deliver messages in correct order, we have to use Threads to handle other tasks concurrently (the server accepts only one client).

require 'socket' server = TCPServer.new('localhost', 3333) puts "TCPServer is running port on 3333 ..." client = server.accept puts "Client connected." # Thread to handle reading messages from the client Thread.new do loop do message = client.gets.chomp puts "Client: #{message}" break if message == 'exit' end end # Thread to handle sending messages to the client loop do response = gets.chomp client.puts response break if response == 'exit' end client.close puts "Client disconnected." 
Enter fullscreen mode Exit fullscreen mode
require 'socket' client = TCPSocket.new('localhost', 3333) puts "Connected to server..." # Thread to reading messages from the server Thread.new do loop do message = client.gets.chomp puts "Server: #{message}" break if message == 'exit' end end loop do response = gets.chomp client.puts response break if response == 'exit' end client.close puts "Disconnected from server." 
Enter fullscreen mode Exit fullscreen mode

If we want our server can communicate to all clients at the same time, we should create 1 thread (to handle communication) for each client and 1 server thread (to handle server-side input and broadcasts). As to client side, we have to create one thread to handle incoming message.

# server.rb require 'socket' server = TCPServer.new('localhost', 3333) puts "TCPServer is running on port 3333 ..." clients = [] def broadcast(message, clients, sender = nil) clients.each do |client| client.puts message unless client == sender end end # Thread to handle server input server_input_thread = Thread.new do loop do response = gets break if response.nil? || response.chomp == 'exit' message = "Server: #{response.chomp}" broadcast(message, clients) end puts "Server shutting down." clients.each { |client| client.close } # Close all client connections server.close exit end loop do client = server.accept clients << client puts "Client connected." Thread.new(client) do |client_socket| begin loop do message = client_socket.gets break if message.nil? || message.chomp == 'exit' message = "Client: #{message.chomp}" puts message end rescue StandardError puts "Client disconnected." ensure clients.delete(client_socket) client_socket.close puts "Client disconnected." end end end 
Enter fullscreen mode Exit fullscreen mode
# client.rb require 'socket' client = TCPSocket.new('localhost', 3333) puts "Connected to server..." # Thread to reading messages from the server Thread.new do loop do message = client.gets&.chomp puts message break if message == 'exit' end end loop do response = gets.chomp client.puts response break if response == 'exit' end client.close puts "Disconnected from server." 
Enter fullscreen mode Exit fullscreen mode

Top comments (0)