Merge pull request #215 from albertosottile/master

IPv6 support
This commit is contained in:
Alberto Sottile 2019-01-30 22:26:22 +01:00 committed by GitHub
commit bbc5ae0b7f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 94 additions and 51 deletions

View File

@ -783,8 +783,8 @@ info = dict(
'py2exe': {
'dist_dir': OUT_DIR,
'packages': 'PySide2',
'includes': 'twisted, sys, encodings, datetime, os, time, math, liburl, ast, unicodedata, _ssl',
'excludes': 'venv, doctest, pdb, unittest, win32clipboard, win32file, win32pdh, win32security, win32trace, win32ui, winxpgui, win32pipe, win32process, Tkinter',
'includes': 'twisted, sys, encodings, datetime, os, time, math, liburl, ast, unicodedata, _ssl, win32pipe, win32file',
'excludes': 'venv, doctest, pdb, unittest, win32clipboard, win32pdh, win32security, win32trace, win32ui, winxpgui, win32process, Tkinter',
'dll_excludes': 'msvcr71.dll, MSVCP90.dll, POWRPROF.dll',
'optimize': 2,
'compressed': 1

View File

@ -1,5 +1,5 @@
version = '1.6.2'
revision = ''
version = '1.6.3'
revision = ' beta'
milestone = 'Yoitsu'
release_number = '71'
release_number = '72'
projectURL = 'https://syncplay.pl/'

View File

@ -11,8 +11,10 @@ import time
from copy import deepcopy
from functools import wraps
from twisted.internet.endpoints import HostnameEndpoint
from twisted.internet.protocol import ClientFactory
from twisted.internet import reactor, task, defer, threads
from twisted.application.internet import ClientService
from syncplay import utils, constants, version
from syncplay.constants import PRIVACY_SENDHASHED_MODE, PRIVACY_DONTSEND_MODE, \
@ -27,41 +29,14 @@ class SyncClientFactory(ClientFactory):
self._client = client
self.retry = retry
self._timesTried = 0
self.reconnecting = False
def buildProtocol(self, addr):
self._timesTried = 0
return SyncClientProtocol(self._client)
def startedConnecting(self, connector):
destination = connector.getDestination()
message = getMessage("connection-attempt-notification").format(destination.host, destination.port)
self._client.ui.showMessage(message)
def clientConnectionLost(self, connector, reason):
if self._timesTried == 0:
self._client.onDisconnect()
if self._timesTried < self.retry:
self._timesTried += 1
self._client.ui.showMessage(getMessage("reconnection-attempt-notification"))
self.reconnecting = True
reactor.callLater(0.1 * (2 ** min(self._timesTried, 5)), connector.connect)
else:
message = getMessage("disconnection-notification")
self._client.ui.showErrorMessage(message)
def clientConnectionFailed(self, connector, reason):
if not self.reconnecting:
reactor.callLater(0.1, self._client.ui.showErrorMessage, getMessage("connection-failed-notification"), True)
reactor.callLater(0.1, self._client.stop, True)
else:
self.clientConnectionLost(connector, reason)
def resetRetrying(self):
self._timesTried = 0
def stopRetrying(self):
self._timesTried = self.retry
self._client._reconnectingService.stopService()
self._client.ui.showErrorMessage(getMessage("disconnection-notification"))
class SyncplayClient(object):
@ -725,16 +700,47 @@ class SyncplayClient(object):
reactor.callLater(0.1, self._playerClass.run, self, self._config['playerPath'], self._config['file'], self._config['playerArgs'], )
self._playerClass = None
self.protocolFactory = SyncClientFactory(self)
if '[' in host:
host = host.strip('[]')
port = int(port)
reactor.connectTCP(host, port, self.protocolFactory)
self._endpoint = HostnameEndpoint(reactor, host, port)
def retry(retries):
self._lastGlobalUpdate = None
if retries == 0:
self.onDisconnect()
if retries > constants.RECONNECT_RETRIES:
reactor.callLater(0.1, self.ui.showErrorMessage, getMessage("connection-failed-notification"),
True)
reactor.callLater(0.1, self.stop, True)
return None
self.ui.showMessage(getMessage("reconnection-attempt-notification"))
self.reconnecting = True
return(0.1 * (2 ** min(retries, 5)))
self._reconnectingService = ClientService(self._endpoint, self.protocolFactory , retryPolicy=retry)
waitForConnection = self._reconnectingService.whenConnected(failAfterFailures=1)
self._reconnectingService.startService()
def connectedNow(f):
hostIP = connectionHandle.result.transport.addr[0]
self.ui.showMessage(getMessage("handshake-successful-notification").format(host, hostIP))
return
def failed(f):
reactor.callLater(0.1, self.ui.showErrorMessage, getMessage("connection-failed-notification"), True)
reactor.callLater(0.1, self.stop, True)
connectionHandle = waitForConnection.addCallbacks(connectedNow, failed)
message = getMessage("connection-attempt-notification").format(host, port)
self.ui.showMessage(message)
reactor.run()
def stop(self, promptForAction=False):
if not self._running:
return
self._running = False
if self.protocolFactory:
self.protocolFactory.stopRetrying()
self.destroyProtocol()
if self._player:
self._player.drop()

View File

@ -16,6 +16,7 @@ de = {
"connection-failed-notification": "Verbindung zum Server fehlgeschlagen",
"connected-successful-notification": "Erfolgreich mit Server verbunden",
"retrying-notification": "%s, versuche erneut in %d Sekunden...", # Seconds
"handshake-successful-notification": "Connection established with {} ({})", # TODO: Translate
"rewind-notification": "Zurückgespult wegen Zeitdifferenz mit {}", # User
"fastforward-notification": "Vorgespult wegen Zeitdifferenz mit {}", # User

View File

@ -16,6 +16,7 @@ en = {
"connection-failed-notification": "Connection with server failed",
"connected-successful-notification": "Successfully connected to server",
"retrying-notification": "%s, Retrying in %d seconds...", # Seconds
"handshake-successful-notification": "Connection established with {} ({})",
"rewind-notification": "Rewinded due to time difference with {}", # User
"fastforward-notification": "Fast-forwarded due to time difference with {}", # User

View File

@ -16,6 +16,7 @@ it = {
"connection-failed-notification": "Connessione col server fallita",
"connected-successful-notification": "Connessione al server effettuata con successo",
"retrying-notification": "%s, Nuovo tentativo in %d secondi...", # Seconds
"handshake-successful-notification": "Connessione stabilita con {} ({})",
"rewind-notification": "Riavvolgo a causa della differenza temporale con {}", # User
"fastforward-notification": "Avanzamento rapido a causa della differenza temporale con {}", # User

View File

@ -16,6 +16,7 @@ ru = {
"connection-failed-notification": "Не удалось подключиться к серверу",
"connected-successful-notification": "Соединение с сервером установлено",
"retrying-notification": "%s, следующая попытка через %d секунд(ы)...", # Seconds
"handshake-successful-notification": "Connection established with {} ({})", # TODO: Translate
"rewind-notification": "Перемотано из-за разницы во времени с {}", # User
"fastforward-notification": "Ускорено из-за разницы во времени с {}", # User

View File

@ -313,14 +313,32 @@ class ConfigurationGetter(object):
port = constants.DEFAULT_PORT if not self._config["port"] else self._config["port"]
if host:
if ':' in host:
host, port = host.split(':', 1)
try:
port = int(port)
except ValueError:
if host.count(':') == 1:
#IPv4 address or hostname, with port
host, port = host.rsplit(':', 1)
try:
port = port.encode('ascii', 'ignore')
except:
port = ""
port = int(port)
except ValueError:
try:
port = port.encode('ascii', 'ignore')
except:
port = ""
else:
#IPv6 address
if ']' in host:
#IPv6 address in brackets
endBracket = host.index(']')
try:
#port explicitely indicated
port = int(host[endBracket+2:])
except ValueError:
#no port after the bracket
pass
host = host[:endBracket+1]
else:
#IPv6 address with no port and no brackets
#add brackets to correctly store IPv6 addresses in configs
host = '[' + host + ']'
return host, port
def _checkForPortableFile(self):

View File

@ -556,7 +556,7 @@ class ConfigDialog(QtWidgets.QDialog):
self.error = error
if config['host'] is None:
host = ""
elif ":" in config['host']:
elif ":" in config['host'] and '[' not in config['host']:
host = config['host']
else:
host = config['host'] + ":" + str(config['port'])
@ -580,7 +580,7 @@ class ConfigDialog(QtWidgets.QDialog):
i += 1
self.hostCombobox.setEditable(True)
self.hostCombobox.setEditText(host)
self.hostCombobox.setFixedWidth(165)
self.hostCombobox.setFixedWidth(250)
self.hostLabel = QLabel(getMessage("host-label"), self)
self.findServerButton = QtWidgets.QPushButton(QtGui.QIcon(resourcespath + 'arrow_refresh.png'), getMessage("update-server-list-label"))
self.findServerButton.clicked.connect(self.updateServerList)
@ -634,7 +634,7 @@ class ConfigDialog(QtWidgets.QDialog):
self.executablepathCombobox.setEditable(True)
self.executablepathCombobox.currentIndexChanged.connect(self.updateExecutableIcon)
self.executablepathCombobox.setEditText(self._tryToFillPlayerPath(config['playerPath'], playerpaths))
self.executablepathCombobox.setFixedWidth(250)
self.executablepathCombobox.setFixedWidth(330)
self.executablepathCombobox.editTextChanged.connect(self.updateExecutableIcon)
self.executablepathLabel = QLabel(getMessage("executable-path-label"), self)

View File

@ -1,6 +1,7 @@
#!/usr/bin/env python3
#coding:utf8
import socket
import sys
# libpath
@ -12,15 +13,27 @@ except AttributeError:
import warnings
warnings.warn("You must run Syncplay with Python 3.4 or newer!")
from twisted.internet import reactor
from twisted.internet import reactor, tcp
from syncplay.server import SyncFactory, ConfigurationGetter
class DualStackPort(tcp.Port):
def __init__(self, port, factory, backlog=50, interface='', reactor=None):
tcp.Port.__init__(self, port, factory, backlog, interface, reactor)
def createInternetSocket(self):
s = tcp.Port.createInternetSocket(self)
try:
s.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0)
except:
pass
return s
if __name__ == '__main__':
argsGetter = ConfigurationGetter()
args = argsGetter.getConfiguration()
reactor.listenTCP(
int(args.port),
dsp = DualStackPort(int(args.port),
SyncFactory(
args.port,
args.password,
@ -31,5 +44,7 @@ if __name__ == '__main__':
args.disable_chat,
args.max_chat_message_length,
args.max_username_length,
args.stats_db_file))
args.stats_db_file),
interface='::')
dsp.startListening()
reactor.run()