internal Task HandleBroadcastAsync(ControllerParameter parameter) { var packet = parameter.Packet; var data = packet.Data; var senderId = packet.SenderId; // if senderId == environment.ClientId then ... var address = parameter.Address; var tcpPort = data["tcpPort"].As <int>(); var udpPort = data["udpPort"].As <int>(); var name = data["name"].As <string>(); var text = data["text"].As <string>(); var imageHash = data["imageHash"].As <string>(); var instance = default(LinkProfile); var profile = Op.Lock(locker, () => profiles.GetOrAdd(senderId, key => (instance = new LinkProfile(key, LinkProfileType.Client)))); return(client.UpdateUIAsync(() => { profile.Name = name; profile.Text = text; profile.Address = address; profile.TcpPort = tcpPort; profile.UdpPort = udpPort; profile.LastOnlineDateTime = DateTime.Now; profile.ProfileStatus = LinkProfileStatus.Online; profile.RemoteImageHash = imageHash; if (instance != null) { client.ProfileCollection.Add(profile); } })); }
public async Task SendDirectoryAsync(LinkProfile profile, string directoryPath, DirectorySenderHandler handler) { if (profile == null || handler == null) { throw new ArgumentNullException(); } var directoryInfo = new DirectoryInfo(directoryPath); if (!directoryInfo.Exists) { throw new DirectoryNotFoundException("Directory not found!"); } var packet = new { name = directoryInfo.Name }; using (var tcp = await Network.ConnectAsync("link.share.directory", packet, profile.GetTcpEndPoint(), CancellationToken)) { var stream = tcp.GetStream(); var result = await stream.ReadBlockWithHeaderAsync(Environment.TcpBufferLimits, CancellationToken); var data = Generator.AsToken(result); if (data["status"].As <string>() != "wait") { throw new LinkException(LinkError.InvalidData); } using (var sender = new DirectorySender(this, profile, stream, directoryInfo.FullName)) { await UpdateUIAsync(() => handler.Invoke(sender)); await sender.LoopAsync(); } } }
/// <summary> /// 向目标用户发送图片消息哈希报文, 在文件 IO 出错时抛出异常, 在网络发送失败时不抛出异常 /// </summary> /// <param name="profile">目标用户</param> /// <param name="filePath">图片路径</param> public async Task SendImageAsync(LinkProfile profile, string filePath) { var result = await Cache.CacheFileAsync(filePath, cancellation.Token); var message = new ImageMessage() { ImageHash = result.Hash, ImagePath = result.FullPath }; var packetData = new { messageId = message.Id, imageHash = result.Hash, }; await Network.SendAsync(profile, message, "link.message.image-hash", packetData); }
/// <summary> /// 向目标用户发送文本消息, 失败时不抛出异常 /// </summary> /// <param name="profile">目标用户</param> /// <param name="text">文本信息内容</param> public async Task SendTextAsync(LinkProfile profile, string text) { if (profile == null) { throw new ArgumentNullException(nameof(profile)); } var message = new TextMessage() { Text = text, }; var packetData = new { messageId = message.Id, text, }; await Network.SendAsync(profile, message, "link.message.text", packetData); }
public LinkClient(LinkSettings settings, TaskScheduler scheduler) { this.scheduler = scheduler; var environment = settings.Environment; CancellationToken = cancellation.Token; void changed(object sender, PropertyChangedEventArgs e) { Debug.Assert(sender == Profile); if (e.PropertyName == nameof(LinkProfile.Name)) { environment.ClientName = Profile.Name; } else if (e.PropertyName == nameof(LinkProfile.Text)) { environment.ClientText = Profile.Text; } else if (e.PropertyName == nameof(LinkProfile.ImageHash)) { environment.ClientImageHash = Profile.ImageHash; } } Settings = settings; Environment = environment; Operation = new LinkOperation(this); Network = new LinkNetwork(this); Contracts = new LinkContracts(this); Cache = new LinkCache(this); var imageHash = environment.ClientImageHash; var imagePath = string.Empty; var exists = !string.IsNullOrEmpty(imageHash) && Cache.Exists(imageHash, out imagePath); Profile = new LinkProfile(environment.ClientId, LinkProfileType.Client) { Name = environment.ClientName, Text = environment.ClientText, ImageHash = exists ? imageHash : string.Empty, ImagePath = exists ? imagePath : string.Empty, Address = IPAddress.Loopback, }; Profile.PropertyChanged += changed; }
private async Task UpdateImageAsync(LinkProfile profile) { var cache = client.Cache; var imageHash = profile.RemoteImageHash; try { if (cache.Exists(imageHash, out var fullpath) == false) { fullpath = await cache.RequestAsync(imageHash, profile.GetTcpEndPoint(), client.CancellationToken); } profile.ImageHash = imageHash; await client.UpdateUIAsync(() => profile.ImagePath = fullpath); } finally { Op.Lock(locker, () => pending.Remove(profile.Id)); } }
internal async Task SendAsync(LinkProfile profile, Message message, string path, object packetData) { message.Status = MessageStatus.Pending; profile.MessageCollection.Add(message); bool handled(Token token) { var status = token["status"].As <string>(); var flag = status == "ok" ? MessageStatus.Success : status == "refused" ? MessageStatus.Refused : default; if (flag == default) { return(false); } message.Status = flag; return(true); } for (var i = 0; i < 2; i++) { try { var result = await RequestAsync(path, packetData, profile.GetUdpEndPoint(), environment.UdpTimeout); if (handled(result.Data)) { return; } } catch (LinkException ex) when(ex.ErrorCode == LinkError.UdpPacketTooLarge) { break; } catch (TimeoutException) { continue; } } var tcp = default(TcpClient); var stream = default(NetworkStream); var cancel = new CancellationTokenSource(); try { tcp = await ConnectAsync(path, packetData, profile.GetTcpEndPoint(), cancel.Token); stream = tcp.GetStream(); var buffer = await stream.ReadBlockWithHeaderAsync(environment.TcpBufferLimits, cancel.Token).TimeoutAfter(environment.TcpTimeout); var token = generator.AsToken(buffer); if (handled(token)) { return; } } catch (Exception) { // ignore } finally { cancel.Cancel(); cancel.Dispose(); stream?.Dispose(); tcp?.Dispose(); } message.Status = MessageStatus.Aborted; }