---
name: ws_send
kind: function
lang: go
domain: infra
version: "1.0.0"
purity: impure
signature: "func WSSend(client *WSClient, msg []byte) error"
description: "Envia bytes al canal Send de un cliente WebSocket especifico de forma no bloqueante. Si el canal esta lleno o el cliente desconectado, retorna error sin bloquear al emisor. Para broadcast a todos los clientes del hub usar WSBroadcast en su lugar."
tags: [websocket, send, server, infra, realtime]
uses_functions: []
uses_types: [WSClient_go_infra]
returns: []
returns_optional: false
error_type: "error_go_core"
imports: [fmt]
params:
  - name: client
    desc: "*WSClient destinatario. Si es nil retorna error."
  - name: msg
    desc: "bytes a enviar. Tipicamente JSON serializado de un WSMessage. Se entregan tal cual al websocket.Conn."
output: "error si el canal Send esta lleno (cliente lento) o cerrado (cliente desconectado). Nil si el mensaje se encolo correctamente."
tested: true
tests: ["envia mensaje al canal Send del cliente", "retorna error si client es nil", "retorna error si el canal esta lleno"]
test_file_path: "functions/infra/ws_test.go"
file_path: "functions/infra/ws_send.go"
---

## Ejemplo

```go
// Enviar mensaje a un cliente especifico (unicast)
target := findClientByID(hub, "user-42")
err := WSSend(target, []byte(`{"type":"notification","text":"hola"}`))
if err != nil {
    log.Printf("send failed: %v", err)
}
```

## Notas

Funcion no bloqueante: si el cliente no consume su canal `Send`, este se llena y la funcion retorna error inmediatamente en vez de bloquear al emisor. Esto previene que un cliente lento bloquee a otros productores.

El mensaje se encola en `client.Send` — la goroutine writePump del cliente lo escribira al `websocket.Conn`. No hay garantia de orden estricto entre llamadas concurrentes a `WSSend` sobre el mismo cliente.
