1
0
mirror of https://github.com/AR2000AR/openComputers_codes.git synced 2025-09-07 22:21:14 +02:00

[osinetwork] TCP

This commit is contained in:
2023-11-30 21:02:08 +01:00
parent 699cb1d71d
commit a87ff086f4
6 changed files with 84 additions and 70 deletions

View File

@@ -7,20 +7,30 @@ local socket = require("socket")
local args, opts = shell.parse(...) local args, opts = shell.parse(...)
local udpSocket, reason = socket.udp() ---@type TCPSocket|UDPSocket
local localSocket
---@type TCPSocket|nil
local clientSocket
---@type thread
local listenerThread local listenerThread
opts.p = tonumber(opts.p) or 0 opts.p = tonumber(opts.p) or 0
opts.b = opts.b or "0.0.0.0" opts.b = opts.b or "0.0.0.0"
---@param listenedSocket UDPSocket ---@param listenedSocket UDPSocket|TCPSocket
local function listenSocket(listenedSocket) local function listenSocket(listenedSocket)
checkArg(1, listenedSocket, 'table') checkArg(1, listenedSocket, 'table')
while true do while true do
local datagram = listenedSocket:recieve() if (listenedSocket:instanceOf(socket.tcp)) then
if (datagram) then ---@cast listenedSocket TCPSocket
term.write(datagram) if (listenedSocket:getState() ~= "ESTABLISHED") then
return false
end
end
local data = listenedSocket:recieve()
if (data) then
term.write(data)
end end
os.sleep() os.sleep()
end end
@@ -37,8 +47,18 @@ local function help()
print("\t nc -u 192.168.1.1 9999") print("\t nc -u 192.168.1.1 9999")
end end
event.listen("interrupted", function(...) ---read stdin indefenitely and send what's read through the socket
if (udpSocket) then udpSocket:close() end ---@param outSocket TCPSocket|UDPSocket
local function readUserInput(outSocket)
repeat
local msg = term.read()
if (msg) then outSocket:send(msg .. "\n") end
until not msg or listenerThread:status() == "dead"
end
local function exit()
if (localSocket) then localSocket:close() end
if (clientSocket) then clientSocket:close() end
if (listenerThread) then if (listenerThread) then
if (not listenerThread:join(3)) then if (not listenerThread:join(3)) then
listenerThread:kill() listenerThread:kill()
@@ -46,63 +66,62 @@ event.listen("interrupted", function(...)
end end
return false return false
end end
)
event.listen("interrupted", exit)
if (opts.h or opts.help) then if (opts.h or opts.help) then
help() help()
os.exit() os.exit()
elseif (opts.l and opts.u and (tonumber(args[1]) or opts.p)) then --listen UDP elseif (opts.l and opts.u and (tonumber(args[1]) or opts.p)) then --listen UDP
assert(udpSocket:setsockname("*", tonumber(args[1]) or opts.p)) localSocket = socket.udp()
--udpSocket:setCallback(listenSocket) assert(localSocket:setsockname("*", tonumber(args[1]) or opts.p))
print(string.format("Listening on %s:%d", udpSocket:getsockname())) print(string.format("Listening on %s:%d", localSocket:getsockname()))
listenerThread = thread.create(listenSocket, udpSocket) listenerThread = thread.create(listenSocket, localSocket)
while true do while true do
--no remote addr/port. We cannot send msgs --no remote addr/port. We cannot send msgs
os.sleep() os.sleep()
end end
udpSocket:close() localSocket:close()
elseif (opts.u) then --connect UDP elseif (opts.u) then --connect UDP
assert(udpSocket:setsockname(opts.b, opts.p)) localSocket = socket.udp()
assert(localSocket:setsockname(opts.b, opts.p))
args[2] = assert(tonumber(args[2]), "Invalid port number") args[2] = assert(tonumber(args[2]), "Invalid port number")
assert(udpSocket:setpeername(args[1], args[2])) assert(localSocket:setpeername(args[1], args[2]))
--udpSocket:setCallback(listenSocket) print(string.format("Listening on %s:%d", localSocket:getsockname()))
print(string.format("Listening on %s:%d", udpSocket:getsockname())) listenerThread = thread.create(listenSocket, localSocket)
listenerThread = thread.create(listenSocket, udpSocket) readUserInput(localSocket)
repeat localSocket:close()
local msg = term.read() elseif (opts.l) then --listen tcp
if (msg) then udpSocket:send(msg .. "\n") end localSocket = socket.tcp()
until not msg args[1] = args[1] or opts.b
udpSocket:close() args[2] = assert(tonumber(args[2] or opts.p), "Invalid port number")
elseif (opts.l) then assert(localSocket:bind(args[1], args[2]))
local tcpsocket = socket.tcp() print(string.format("Listening on %s:%d", localSocket:getsockname()))
assert(tcpsocket:bind(opts.b, opts.p)) localSocket:listen(1)
args[2] = assert(tonumber(args[2]), "Invalid port number") local reason
assert(tcpsocket:bind(args[1], args[2])) clientSocket = localSocket:accept()
print(string.format("Listening on %s:%d", tcpsocket:getsockname())) localSocket:close() --client connected, we don't need the listening socket anymore
tcpsocket:listen(1) if (clientSocket) then
local client = tcpsocket:accept() print(string.format("Connected to : %s:%d", clientSocket:getpeername()))
if (client) then listenerThread = thread.create(listenSocket, clientSocket)
listenerThread = thread.create(listenSocket, client) readUserInput(clientSocket)
repeat clientSocket:close()
local msg = term.read() else
if (msg) then client:send(msg .. "\n") end print(reason)
until not msg
client:close()
end end
tcpsocket:close()
else --connect TCP else --connect TCP
args[2] = assert(tonumber(args[2]), "Invalid port number") args[2] = assert(tonumber(args[2]), "Invalid port number")
local tcpsocket = socket.tcp() localSocket = socket.tcp()
tcpsocket:settimeout(5) localSocket:settimeout(5)
local s = tcpsocket:connect(args[1], args[2]) local s = localSocket:connect(args[1], args[2])
if (s ~= 1) then if (s ~= 1) then
print("Timeout") print("Timeout")
os.exit(1) else
print(string.format("Connected to %s:%d", localSocket:getpeername()))
listenerThread = thread.create(listenSocket, localSocket)
readUserInput(localSocket)
end end
print(string.format("Connected to %s:%d", tcpsocket:getpeername())) localSocket:close()
listenerThread = thread.create(listenSocket, tcpsocket)
repeat
local msg = term.read()
if (msg) then tcpsocket:send(msg .. "\n") end
until not msg
tcpsocket:close()
end end
exit()

View File

@@ -30,7 +30,7 @@ end
---@package ---@package
function TCPLayer:tick() function TCPLayer:tick()
for _,s in pairs(utils.getTreeBottomValues(self._sockets)) do for _, s in pairs(utils.getTreeBottomValues(self._sockets)) do
---@cast s TCPSocket ---@cast s TCPSocket
s:_tick() s:_tick()
end end
@@ -42,7 +42,7 @@ function TCPLayer:payloadHandler(from, to, payload)
if (seg:flag(TCPSegment.Flags.SYN) and not seg:flag(TCPSegment.Flags.ACK)) then if (seg:flag(TCPSegment.Flags.SYN) and not seg:flag(TCPSegment.Flags.ACK)) then
--initial sync packet --initial sync packet
socket = self:getSocket(to, seg:dstPort(), 0, 0) socket = self:getSocket(to, seg:dstPort(), 0, 0)
if (not socket) then if (not socket) then --Send RST
local rstseg = TCPSegment(seg:dstPort(), seg:srcPort(), "") local rstseg = TCPSegment(seg:dstPort(), seg:srcPort(), "")
rstseg:flags(TCPSegment.Flags.RST|TCPSegment.Flags.ACK) rstseg:flags(TCPSegment.Flags.RST|TCPSegment.Flags.ACK)
rstseg:seq(seg:flag(TCPSegment.Flags.ACK) and seg:ack() or 0) rstseg:seq(seg:flag(TCPSegment.Flags.ACK) and seg:ack() or 0)

View File

@@ -28,7 +28,7 @@ local class = require("libClass2")
---@class Buffer:Object ---@class Buffer:Object
---@field private _data string ---@field private _data string
---@operator call:Buffer ---@operator call:Buffer
local Buffer = require('libClass2')() local Buffer = class()
---Comment ---Comment
---@return Buffer ---@return Buffer

View File

@@ -1,12 +1,9 @@
local TCPSocket = require("socket.tcp") local TCPSocket = require("socket.tcp")
local UDPSocket = require("socket.udp")
return { return {
udp = require("socket.udp"), udp = UDPSocket,
---Creates and returns an TCP master object. A master object can be transformed into a server object with the method listen (after a call to bind) or into a client object with the method connect. The only other method supported by a master object is the close method. tcp = TCPSocket,
---
---In case of success, a new master object is returned. In case of error, nil is returned, followed by an error message.
---@return TCPSocket
tcp = function() return TCPSocket() end,
---@type socketdns ---@type socketdns
dns = select(2, pcall(require, "socket.dns")) dns = select(2, pcall(require, "socket.dns"))
} }

View File

@@ -155,6 +155,7 @@ function TCPSocket:_makeDataSegment(data)
return seg return seg
end end
--set the segment `seg` acknowledgment number to acknowledge the `recived` segment
---@protected ---@protected
---@param seg TCPSegment ---@param seg TCPSegment
---@param received TCPSegment ---@param received TCPSegment
@@ -165,6 +166,7 @@ function TCPSocket:_setAck(seg, received)
return seg return seg
end end
---make a new acknowledgment segment for the `recived` segment
---@protected ---@protected
---@param received TCPSegment ---@param received TCPSegment
---@return TCPSegment ---@return TCPSegment
@@ -243,7 +245,7 @@ function TCPSocket:bind(address, port)
end end
function TCPSocket:close() function TCPSocket:close()
if (self._kind == "client") then if (self._kind == "client" and self._state ~= "CLOSED") then
local seg = self:_makeSegment() local seg = self:_makeSegment()
seg:flag(f.FIN, true) seg:flag(f.FIN, true)
seg:flag(f.ACK, true) seg:flag(f.ACK, true)
@@ -338,7 +340,7 @@ end
---Prefix is an optional string to be concatenated to the beginning of any received data before return.\ ---Prefix is an optional string to be concatenated to the beginning of any received data before return.\
--- ---
---If successful, the method returns the received pattern. In case of error, the method returns nil followed by an error message, followed by a (possibly empty) string containing the partial that was received. The error message can be the string 'closed' in case the connection was closed before the transmission was completed or the string 'timeout' in case there was a timeout during the operation. ---If successful, the method returns the received pattern. In case of error, the method returns nil followed by an error message, followed by a (possibly empty) string containing the partial that was received. The error message can be the string 'closed' in case the connection was closed before the transmission was completed or the string 'timeout' in case there was a timeout during the operation.
---@param pattern? string ---@param pattern? "*a"|"*l"|number
---@param prefix? string ---@param prefix? string
---@return string? data, string?reason ---@return string? data, string?reason
function TCPSocket:recieve(pattern, prefix) function TCPSocket:recieve(pattern, prefix)
@@ -349,6 +351,7 @@ function TCPSocket:recieve(pattern, prefix)
local data local data
repeat repeat
data = self._inBuffer:read(pattern) data = self._inBuffer:read(pattern)
os.sleep()
until data ~= nil or self:_hasTimedOut(t1) until data ~= nil or self:_hasTimedOut(t1)
if (not data) then if (not data) then
return nil, self:_hasTimedOut(t1) and "timeout" or "" return nil, self:_hasTimedOut(t1) and "timeout" or ""
@@ -473,6 +476,7 @@ function TCPSocket:payloadHandler(from, to, tcpSegment)
if (tcpSegment:offset() > 5) then if (tcpSegment:offset() > 5) then
self:handleOptions(tcpSegment) self:handleOptions(tcpSegment)
end end
if (self._state == "LISTEN") then if (self._state == "LISTEN") then
if (tcpSegment:flag(f.SYN)) then if (tcpSegment:flag(f.SYN)) then
if (#(self._backlog) < self._backlogLen) then if (#(self._backlog) < self._backlogLen) then

View File

@@ -173,10 +173,4 @@ function UDPSocket:payloadHandler(from, to, udpPacket)
table.insert(self._buffer, {udpPacket:payload(), ipv4Address.tostring(from), udpPacket:srcPort()}) table.insert(self._buffer, {udpPacket:payload(), ipv4Address.tostring(from), udpPacket:srcPort()})
end end
---Create and returns an unconnected UDP socket. return UDPSocket
---@return UDPSocket
local function udp()
return UDPSocket()
end
return udp