I need to send and then receive some data(string) from/to UDP client.
I can send data to client with code below but how can I implement read data as well? I have read thousands pages and examples but I just can not figure it out. Can someone please modify my code so it will be capable sent and than received data from UDP device?
My code is from here:
How to implement UDP client and send data in Swift on iPhone?
Details:
I am sending string to ESP32 device with specific IP and port.
then this device will do some calculations and return some data(string) back. So I will need receive that data after I sent data string to it.
I am able to sent string with provided code below but I do not know how to modify for receiving data from my ESP32 device as well via UDP.
import Foundation
func udpSend(textToSend: String, address: in_addr, port: CUnsignedShort) {
func htons(value: CUnsignedShort) -> CUnsignedShort {
return (value << 8) + (value >> 8);
}
let fd = socket(AF_INET, SOCK_DGRAM, 0) // DGRAM makes it UDP
let addr = sockaddr_in(
sin_len: __uint8_t(MemoryLayout<sockaddr_in>.size),
sin_family: sa_family_t(AF_INET),
sin_port: htons(value: port),
sin_addr: address,
sin_zero: ( 0, 0, 0, 0, 0, 0, 0, 0 )
)
let sent = textToSend.withCString { cstr -> Int in
var localCopy = addr
let sent = withUnsafePointer(to: &localCopy) { pointer -> Int in
let memory = UnsafeRawPointer(pointer).bindMemory(to: sockaddr.self, capacity: 1)
let sent = sendto(fd, cstr, strlen(cstr), 0, memory, socklen_t(addr.sin_len))
return sent
}
return sent
}
close(fd)
}
Related
I am creating a windows UDP tunnel using wintun driver. I have written a code to receive data from the server and send it to the windows tunnel using the function send_packet and allocatesendpacket. The error I get is that I can read data only as bytes from the UDP server but it causes a mismatch when I pass it to the send_packet function. The error is as follows:
mismatched types
expected struct `wintun::Packet`, found `&[u8]`
Code for send_packet and allocatesendpacket
https://github.com/nulldotblack/wintun/blob/main/src/session.rs
Code for packet struct data type
https://github.com/nulldotblack/wintun/blob/main/src/packet.rs
Link for other dependencies
https://github.com/nulldotblack/wintun
How do I convert the bytes (variable buffer below) to Packet struct data type?. The error of mismatch occurs when I declare the value of a variable packet with the value of the received buffer variable, so that packet variable can be sent to tunnel.
My code:
let socket = UdpSocket::bind("6.0.0.1:8000") //create client socket object with ip and port
.expect("Could not bind client socket");
socket.connect("6.0.0.10:8888") //SERVER IP /PORT
let writer_session = session.clone();
let writer =
std::thread::spawn(move || {
info!("Starting writer");
while RUNNING.load(Ordering::Relaxed) {
let mut buffer = [0u8; 20000];
socket.recv_from(&mut buffer) //get message from server
.expect("Could not read into buffer");
let leng1 = buffer.len();
let mut packet = writer_session.allocate_send_packet(leng1.try_into().unwrap()).unwrap();
packet = &buffer[0..leng1]; //ERROR occurs here
writer_session.send_packet(packet);
}
});
You have two errors the first caused by trying to assign a slice to packet directly which is solved by copying the buffer into packet instead:
packet.bytes_mut().copy_from_slice(&buffer);
The second is that you are not using the length returned by recv_from but instead using the length of the whole buffer which may lead to reading past the end of the read data although memory address is valid. You should do this instead:
let mut buffer = [0u8; 20000];
let (leng1, src_addr) = socket.recv_from(&mut buffer) //get message from server
.expect("Could not read into buffer");
let buffer = &mut buffer[..leng1];
To copy the contents of buffer into packet, you can use slice::copy_from_slice:
packet.bytes_mut().copy_from_slice(&buffer);
I have a small program that looks like the following:
let sock = UdpSocket::bind("0.0.0.0:1024").unwrap();
let mut buf = [0; 10000];
loop {
let (len, src) = sock.recv_from(&mut buf).unwrap();
let buf = &mut buf[..len];
// do stuff
}
Let's say someone sends data to example.com:1024, is there any way to know they sent the data to example.com and not just the source IP address?
I am new in Swift.
I am studying to write a pure swift app to be socket server.
code is as below
server = CFSocketCreate(kCFAllocatorDefault, AF_INET,
SOCK_STREAM, IPPROTO_TCP, callbackOpts.rawValue, {
(socket, type, address, data, info) in
print("type ->\(type)")
}, nil)
let size = MemoryLayout<sockaddr_in>.size
addr.sin_len = UInt8(MemoryLayout<sockaddr_in>.size)
addr.sin_family = sa_family_t(AF_INET); /* Address family */
addr.sin_port = in_port_t(10001); /* Or a specific port */
addr.sin_addr.s_addr = in_addr_t(INADDR_ANY);
let addrInPtr = withUnsafeMutablePointer(to: &addr) { ptr in
return ptr.withMemoryRebound(to: UInt8.self, capacity: size){
return $0
}
}
let sincfd = CFDataCreate(
kCFAllocatorDefault,
addrInPtr,
size)
let result = CFSocketSetAddress(server, sincfd)
switch result {
case .success:
print("xx sucess")
case .timeout:
print("xx timeout")
case .error:
print("xx error")
}
let runLoopSourceRef = CFSocketCreateRunLoopSource(
kCFAllocatorDefault, server, 0)
CFRunLoopAddSource(CFRunLoopGetCurrent(),
runLoopSourceRef, CFRunLoopMode.defaultMode)
I got "xx success" when binder.
I can not connect this server from client.
Does someone give me a hint what problem is and how to do a server?
I know there are some 3rd party library can do that,
but I am studying Swift and try to understand how to use CFNetwork.
I just found my problem.
It was caused by the byte order of port number.
Code should be
addr.sin_port = in_port_t(CFSwapInt16HostToBig(10001))
currently I am using GCDAsyncUdpSocket to send a udp broadcast. Currently, the code sends it to a hardcoded 255.255.255.255 address. Is that the correct broadcast address on all networks? Will it fail on certain networks? What is the correct swift code?
socket = GCDAsyncUdpSocket(delegate: self, delegateQueue: DispatchQueue.main)
socket!.send(ipRequestData!, toHost: "255.255.255.255", port: discoveryPort, withTimeout: 10000, tag: 0)
This question has an answer but it is in Objective-C
Calculating the Broadcast Address in Objective-C
Your broadcast address depends on your host address and the subnet mask. You can find the formula to calculate the broadcast address on Wikipedia:
broadcastAddress = ipAddress | ~subnetMask
You can calculate it with the following function:
func calculateBroadcastAddress(ipAddress: String, subnetMask: String) -> String {
let ipAdressArray = ipAddress.split(separator: ".")
let subnetMaskArray = subnetMask.split(separator: ".")
guard ipAdressArray.count == 4 && subnetMaskArray.count == 4 else {
return "255.255.255.255"
}
var broadcastAddressArray = [String]()
for i in 0..<4 {
let ipAddressByte = UInt8(ipAdressArray[i]) ?? 0
let subnetMaskbyte = UInt8(subnetMaskArray[i]) ?? 0
let broadcastAddressByte = ipAddressByte | ~subnetMaskbyte
broadcastAddressArray.append(String(broadcastAddressByte))
}
return broadcastAddressArray.joined(separator: ".")
}
Example:
let broadcastAddress = calculateBroadcastAddress(ipAddress: "172.16.0.0", subnetMask: "255.240.0.0")
Result:
"172.31.255.255"
In general, you should never choose the main queue for network operations because it will lead to lags in the user interface (which is drawn in the main queue). Better use
let udpSocket = GCDAsyncUdpSocket(delegate: self, delegateQueue: DispatchQueue.global())
Do not forget to explicitly enable broadcasts for this socket
do {
try udpSocket?.enableBroadcast(true)
} catch let error {
print(error)
}
Also consider that Apple enforces an iOS app to work with IPv6, which does not support broadcasts but multicasts. So maybe you should switch to multicasts at least if your device is inside a pure IPv6 network.
I have problem to read lines. When server (server code in java) sent more than one line code, my secket can not read all lines.
I used this library https://github.com/swiftsocket/SwiftSocket
and socket read method code:
private func sendRequest(data: String, client: TCPClient?) -> (String?) {
// It use ufter connection
if client != nil {
// Send data (WE MUST ADD TO SENDING MESSAGE '\n' )
let (isSuccess, errorMessage) = client!.send(str: "\(data)\n")
if isSuccess {
// Read response data
let data = client!.read(1024*10)
if let d = data {
// Create String from response data
if let str = NSString(bytes: d, length: d.count, encoding: NSUTF8StringEncoding) as? String {
return (data: str)
} else {
return (data: nil)
}
} else {
return (data: nil)
}
} else {
print(errorMessage)
return (data: nil)
}
} else {
return (data: nil)
}
}
I have changed like this : let data = client!.read(1024*40) but again can not read (received data large). it read some received data like this:
338C6EAAD0740ED101860EFBA286A653F42793CF1FC7FB55D244C010941BDE3DCA540223B291639D1CD7285B4240B330EBC7C002003F957790749256D54EAC4DB7FC0AD5E2970FE951DFA0A1635A93DFCB031DBA642BA928B8327B661F9F4F22CA657AE803B25A208C23F72D6F934B95558108C1187F90E6D8DE13F9367534E7EC28DBA5AC6C8597800154033B63A3B2185DF68ABF67BB76AC6B593C52E5D7B0A5D7674BEC7C6AA9B4C57343C32C944FEB5D1C2D6C2A400D454196A0029C23DB30F1B6049423DC5BEE728E03C275F207639C25E226A38A23EAE04AD673132336D9E113FC6CA32DAD1D75191BCE40A281D40549C1D6FDD23BC5A38B472ED1EEB6BA1D80D00EF2A08F4729FD05329993A6AE58C34253660A77C20139C73FE0E4A68D9857E02C3F8589A61B22C4E26B3DA00098158FB6CE0C43F271ABC823BF9AD7DBEB32A4E9BE4E90C284E1FD633956F82EB5387DE2E8D7626C88900D183C7E1F683C27B8A654EE75017E897D11F2431A9AB4C0662CC91B3897D52E630A788A1A8D552ABB04916B5E52A1382DB2803A796A96ACC50C00913C650393AA919100E6477B7D88066FEB6C78D4F5853122AB6D7309540053B9DB98BEC0D518CD8BF41620506E1224FB0F8B240B7E9FD60649E703FA9E6A21E785BB0F646DB028F5C5E64697E41A857F6A2A459B2C1B070C244F7B6FD252FFF016CC0A4F0457711213C025E6165706DF6C35C6C38F53610373C3DE99DFE426102860E2D4CD1BF5F6B97346655F0E26103CD6BF37AFCE705D4D2F78F7F40C5316143725D0D7FB6647A92F98A42570F423D646DC2CA726ABB16ED6C62C
but not all. How to fix this. Please, advise me. Thanks
there is your responsibility to check what did you received and how to process all data received from your socket. try to check this simple example how to read line delimited text from network. you can easily adopt this code for use with your library, or use it as is if you have working socket handle ...
import Darwin
private var line = [UInt8]()
private var excess = [UInt8]()
private func readLine(inout lineBufer: [UInt8], inout excessBufer: [UInt8], var sockHandle: Int32, wait: Bool = true) -> Int{
// (1) received bytes in one cycle and numbers of cycles to receive
// whole line (line delimited text)
var received = 0
var i = 0
// (2) clear the buffer for the extra bytes readed after the \n
excessBufer.removeAll()
var buffer = [UInt8](count: 64, repeatedValue: 0)
// (3) read from network until at least one \n is found
repeat {
received = read(sockHandle, &buffer, buffer.count)
if received == 0 {
close(sockHandle)
sockHandle = -1
print("connection lost")
return -1
}
if received < 0 {
print("\treceived failed:", String.fromCString(strerror(errno))!)
return -1
}
i = 0
while i < received && buffer[i] != UInt8(ascii: "\n") { i++ }
lineBufer.appendContentsOf(buffer[0..<i])
// (4) now consume '\n'
i++
// (5) we have extra bytes for next line received -i > 0
if i < received {
excessBufer.appendContentsOf(buffer[i..<received])
break // from loop
}
// (6) if no extra bytes and no whole line, than received - i < 0 (-1)
// if whole line and no extra bytes, than received - i = 0
} while i != received
return received
}
// (1) prepare buffers
line.removeAll()
line.appendContentsOf(excess)
// (2) read line delimited text ( socket is you uderlying socket handle )
readLine(&line, excessBufer: &excess, sockHandle: socket)
// now your line buffer consist of one line ot text
// do something with the line and repeate the proces as you need .....
// (1) prepare buffers
line.removeAll()
line.appendContentsOf(excess)
// (2) read line delimited text
readLine(&line, excessBufer: &excess, sockHandle: socket)
Looking at the GitHub repository you mentioned, I'm under the impression that this is a relatively new and not well-supported project. Yes, it may provide a somewhat simplified interface to sockets, but it is unknown how buggy it is and what its future is. Instead of using it, one might be better off using well-supported Apple Objective-C frameworks. Please see
https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/UsingSocketsandSocketStreams.html
https://developer.apple.com/library/ios/documentation/NetworkingInternetWeb/Conceptual/NetworkingOverview/CommonPitfalls/CommonPitfalls.html
It is fairly easy to use the Foundation framework with Swift. Yes, you can definitely call POSIX C networking API from Swift, but that is more work and is not recommended in iOS, as it doesn't activate the cellular radio in the device, as explained in the aforementioned Apple articles.