I'm trying to set up a UDP socket on iOS to listen for datagrams coming over a multicast socket:
#import <CoreFoundation/CoreFoundation.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
void getSocketDataCallBack (CFSocketRef cfSocketRef, CFSocketCallBackType cbType, CFDataRef address, const void *data, void *info) {
if (cbType == kCFSocketDataCallBack) {
cout << "o";
} else {
cout << "x";
}
}
void main () {
CFSocketError cfErr;
CFSocketContext udpSocketContext = {0, NULL, NULL, NULL, NULL};
udpSocketContext.info = &cbData;
CFSocketRef udpSocketRef = CFSocketCreate(kCFAllocatorDefault,
PF_INET,
SOCK_DGRAM,
IPPROTO_UDP,
kCFSocketDataCallBack,
&getSocketDataCallBack,
&udpSocketContext);
if ( udpSocketRef == NULL) {
cout << "CFSocketCreate failed\n";
} else {
cout << "UDP socket created\n";
CFRunLoopSourceRef source = CFSocketCreateRunLoopSource( kCFAllocatorDefault, udpSocketRef, 0 );
CFRunLoopAddSource( CFRunLoopGetMain(), source, kCFRunLoopCommonModes );
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_len = sizeof(addr);
addr.sin_family = AF_INET;
addr.sin_port = htons(MC_PORT); //4194
inet_aton(MC_ADDR, &addr.sin_addr); //239.0.123.45
//Tell socket to listen on this address
CFDataRef cfDataRef = CFDataCreate(NULL, (const UInt8 *)&addr, sizeof(addr));
cfErr = CFSocketSetAddress(udpSocketRef, cfDataRef);
}
}
All the socket calls succeed, but I don't get any callbacks (I am sending UDP datagrams to the MC address from a separate macOS application).
What am I doing wrong?
Thanks for any an all assistance!
Cheers.
The problem is that CFSocket does not in itself enable receipt of datagrams sent to an IPv4 (nor IPv6) multicast address. But all is not hopeless!
At https://justanapplication.wordpress.com/category/posix/posix-system-calls/posix-socket/ I found this: "Fortunately the function CFSocketCreateWithNative can turn a theoretical POSIX socket into a theoretical CFSocket." The author of this, Simon Lewis, also says that this actually works, too, "at least on an iPad running iOS 7.0.4," and he is nice enough to provide some code to try it out with. Good Luck!
Related
I am learning freertos and lwip on a Arty Z7.
I have managed to launch several tasks without problem but when I try to setup a lwip server to receive TCP packets, the server works perfectly but the other tasks won't continue their work.
So when I run the following code, the xil_printf of the "dumb_task" is writing correctly its message until the PHY autonegation is complete. Then, Nothing will happen from the dumb_task but the connection from the tcp port are accepted by the fpga. (I have commented by purpose the receive packet thread as it's changing nothing).
Do you have any ideas of what could be the problem?
Thank you!
Here is my code:
what is in my main file:
static sys_thread_t g_server_th_handle;
void dumb_task(void *p){
while(1){
xil_printf("dummy!\n");
vTaskDelay(10);
}
}
int main()
{
xTaskCreate (dumb_task, "TestTask", 512, NULL, 4, NULL);
g_server_th_handle = create_server_thread(2);
vTaskStartScheduler();
while(1);
return 0;
}
what is in a .cpp/.h file for the server:
static sys_thread_t g_server_thread_handle;
static int complete_nw_thread;
struct netif server_netif;
int g_server_tasks_priority = DEFAULT_THREAD_PRIO;
void setting_thread(void *p)
{
/* the mac address of the board. this should be unique per board */
u8_t mac_ethernet_address[] = { 0x00, 0x0a, 0x35, 0x00, 0x01, 0x02 };
/* Add network interface to the netif_list, and set it as default */
if (!xemac_add(&server_netif, NULL, NULL, NULL, mac_ethernet_address,
PLATFORM_EMAC_BASEADDR)) {
xil_printf("Error adding N/W interface\r\n");
return;
}
netif_set_default(&server_netif);
/* specify that the network if is up */
netif_set_up(&server_netif);
/* start packet receive thread - required for lwIP operation */
sys_thread_new("xemacif_input_thread",
(void(*)(void*))xemacif_input_thread, &server_netif,
THREAD_STACKSIZE, g_server_tasks_priority);
complete_nw_thread = 1;
vTaskResume(g_server_thread_handle);
vTaskDelete(NULL);
}
void accept_loop()
{
int sock, new_sd;
int opt=1;
struct sockaddr_in address, remote;
int size;
// set up address to connect to
memset(&address, 0, sizeof(address));
if ((sock = lwip_socket(AF_INET, SOCK_STREAM, 0)) < 0) {
xil_printf("TCP server: Error creating Socket\r\n");
return;
}
address.sin_family = AF_INET;
address.sin_port = htons(TCP_CONN_PORT);
address.sin_addr.s_addr = INADDR_ANY;
if (bind(sock, (struct sockaddr *)&address, sizeof (address)) < 0) {
xil_printf("TCP server: Unable to bind to port %d\r\n",
TCP_CONN_PORT);
close(sock);
return;
}
ioctl(sock,FIONBIO,&opt);
if (listen(sock, 0) < 0) {
xil_printf("TCP server: tcp_listen failed\r\n");
close(sock);
return;
}
size = sizeof(remote);
xil_printf("Server set and listening\n\r");
for(;;) {
if ((new_sd = accept(sock, (struct sockaddr *)&remote,
(socklen_t *)&size)) > 0){
char *ip = inet_ntoa(((struct sockaddr_in*) &remote)->sin_addr);
gTCP_connection_count +=1;
xil_printf("New connection from %s. Number of client : %d\n\r",
ip,gTCP_connection_count);
/*sys_thread_new("TCP_recv thread",
tcp_recv_traffic, (void*)&new_sd,
TCP_SERVER_THREAD_STACKSIZE,
g_server_tasks_priority);*/
}
vTaskDelay(pdMS_TO_TICKS( 1UL ));
}
}
void server_thread(void *p)
{
// /* initialize lwIP before calling sys_thread_new */
lwip_init();
/* any thread using lwIP should be created using sys_thread_new */
sys_thread_new("nw_thread", setting_thread, NULL,
THREAD_STACKSIZE, g_server_tasks_priority);
/* Suspend Task until auto-negotiation is completed */
if (!complete_nw_thread){
vTaskSuspend(NULL);
}
assign_default_ip(&(server_netif.ip_addr), &(server_netif.netmask),
&(server_netif.gw));
print_ip_settings(&(server_netif.ip_addr), &(server_netif.netmask),
&(server_netif.gw));
/* start the application*/
accept_loop();
vTaskDelete(NULL);
return;
}
sys_thread_t create_server_thread(int priority){
g_server_tasks_priority = priority;
g_server_thread_handle = sys_thread_new("server_thread", server_thread, 0,
THREAD_STACKSIZE, priority );
return g_server_thread_handle;
}
I'm currently using a Esp32 which presents, in addition to wifi, an ethernet interface.
I'm using the esp-idf v3.3 with FreeRTOS.
To use it I included the "esp_eth.h" library (https://docs.espressif.com/projects/esp-idf/en/release-v3.1/api-reference/ethernet/esp_eth.html#api-reference-phy-lan8720).
I'd like to use both Wifi and ethernet interfaces basing on what I want to do but selecting which one to use, is there a way?
The practical use is to receive a command through the ethernet interface (for example a site to ping), ping the server through the wifi interface and answer back to the ethernet interface the ping result.
How can I select which interface to use (i dont want that the ping is made through the ethernet or that the response go through the wifi).
#edit: i found the method netif_set_default(struct netif * netif), but i dont know if it is the best way to select the interface to use for the specific action(i should swap from one interface to another)
Just get the IP of the Ethernet interface, then bind a socket to that IP address.
struct in_addr iaddr = {0};
#if USE_ANY_IF
// Bind the socket to any address
iaddr.s_addr = htonl(INADDR_ANY);
#else
// bind only to the Ethernet interface
tcpip_adapter_ip_info_t ip_info = {0};
tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_ETH, &ip_info);
inet_addr_from_ip4addr(&iaddr, &ip_info.ip);
#endif
Then use it to bind a socket, for example:
static int create_multicast_ipv4_socket(struct in_addr bind_iaddr)
{
struct sockaddr_in saddr = {0};
int sock = -1;
int err = 0;
char addrbuf[32] = {0};
sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP);
if (sock < 0)
{
ESP_LOGE(V4TAG, "Failed to create socket. Error %d", errno);
return -1;
}
saddr.sin_addr.s_addr = bind_iaddr.s_addr; // what interface IP to bind to. Can be htonl(INADDR_ANY)
saddr.sin_family = PF_INET;
saddr.sin_port = htons(UDP_PORT);
inet_ntoa_r(saddr.sin_addr.s_addr, addrbuf, sizeof(addrbuf) - 1);
ESP_LOGI(TAG, "Binding to interface %s...", addrbuf);
err = bind(sock, (struct sockaddr *)&saddr, sizeof(struct sockaddr_in));
if (err < 0)
{
ESP_LOGE(V4TAG, "Failed to bind socket. Error %d", errno);
goto err;
}
// Assign multicast TTL (set separately from normal interface TTL)
uint8_t ttl = MULTICAST_TTL;
setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(uint8_t));
if (err < 0)
{
ESP_LOGE(V4TAG, "Failed to set IP_MULTICAST_TTL. Error %d", errno);
goto err;
}
// All set, socket is configured for sending
return sock;
err:
close(sock);
return -1;
}
I am using a CFSocket configured for UDP to send data through wifi. The problem is that when I receive an empty UDP packet, the wifi suddenly stops working. I cant receive or send wnything anymore after that. Does anyone know what seems to be the problem? Here is my configuration and callback code:
void socketCallback(CFSocketRef cfSocket, CFSocketCallBackType type, CFDataRef address, const void *data, void *userInfo)
{
NSData * inputData = (__bridge NSData*)data;
NSUInteger inputDataCapacity = inputData.length * 2;
if(inputDataCapacity > minimalFrameSize*2){
//DO STH
}
}
-(void) initNetworkCommunication
{
CFSocketContext socketContext = {0, (__bridge void *)(self), NULL, NULL, NULL};
_cfSocket = CFSocketCreate(kCFAllocatorDefault, AF_INET, SOCK_DGRAM, IPPROTO_UDP, kCFSocketAcceptCallBack | kCFSocketDataCallBack, (CFSocketCallBack)socketCallback, &socketContext);
if ( _cfSocket == NULL) {
// NSLog(#"CfSocketCreate failed");
}
else {
if( _cfSocket ) {
_addrData = [Utils createIpAddressDataRef:COMM_INT_LISTENER_ADDR];
int flag = 1;
CFSocketNativeHandle socket_handle = (CFSocketNativeHandle)CFSocketGetNative(_cfSocket);
setsockopt(socket_handle, SOL_SOCKET, SO_BROADCAST, &flag, sizeof(flag));
//set address of the socket to listen to
CFSocketSetAddress (_cfSocket, _addrData);
_cfSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, _cfSocket, 0);
CFRunLoopAddSource(CFRunLoopGetCurrent(), _cfSource, kCFRunLoopDefaultMode);
CFRelease(_cfSource);
CFRelease(_cfSocket);
}
}
}
And sometimes after the wifi "crash" happens, I get an "EXC_BAD_ACCESS" when using CFSocketSendData.
in iOS CFSocket the way handle callback function is as followed
void receiveData(CFSocketRef s,
CFSocketCallBackType type,
CFDataRef address,
const void *data,
void *info)
{
}
int main ()
{
CFSocketRef s = CFSocketCreate(NULL, PF_INET,
SOCK_STREAM, IPPROTO_TCP,
kCFSocketDataCallBack,
receiveData,
NULL);
struct sockaddr_in sin;
struct hostent *host;
host = gethostbyname("localhost");
memset(&sin, 0, sizeof(sin));
memcpy(&(sin.sin_addr), host->h_addr,host->h_length);
sin.sin_family = AF_INET;
sin.sin_port = htons(888);
CFDataRef address, data;
UInt8 message[] = "Hello world";
CFRunLoopSourceRef source;
address = CFDataCreate(NULL, (UInt8 *)&sin, sizeof(sin));
data = CFDataCreate(NULL, message, sizeof(message));
CFSocketConnectToAddress(s, address, 0);
CFSocketSendData(s, NULL, data, 0);
}
in CFSocket when we do a CFSocketCreate we put the callback function in the SocketCreate function.
But for C Code the Socket and read from Socket is as followed.
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (connect(sockfd,(struct sockaddr *) &serv_addr,sizeof(serv_addr)) < 0)
error("ERROR connecting");
printf("Please enter the message: ");
bzero(buffer,256);
fgets(buffer,255,stdin);
n = write(sockfd,buffer,strlen(buffer));
if (n < 0)
error("ERROR writing to socket");
bzero(buffer,256);
n = read(sockfd,buffer,255);
I don't how to implement the callback read function inside the C Code. The callback function would be implemented every time something come into the buffer.
As you noted BSD sockets is not callback based, you need to poll from read to receive new data. You can call this periodically on the main thread or create a custom thread for reading data repeatedly.
CFSocket wraps BSD sockets at a higher level. It has some nice features, I believe that it automatically handles threading, and sleeps the thread until data is received. Reimplemnenting these features would not be a trivial process.
Good resources:
http://beej.us/guide/bgnet/
BSD Sockets - How to use non-blocking sockets?
http://man7.org/linux/man-pages/man2/poll.2.html
Have been doing some google search and some reading on this subject but cannot seem to get it right no matter how much time i spent searching.
What i want to do is to receive broadcast message of devices that are connected on my network by advertising my interest in their services that they supply. Using wireshark i can see the broadcast/notification messages from the network devices that i want to connect to sent over my network but not my broadcast search for interest of their services.
But with network utility i can see that the socket is created but don't know which state it is in, whether listen or connected.
Yes i know that there are libraries that i can use to do this, but i wanted to build something of my own from the ground up and get a better understand of how it works.
MySocket.h
#import <Foundation/Foundation.h>
#import <CoreFoundation/CoreFoundation.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#if TARGET_OS_IPHONE
#import <CFNetwork/CFNetwork.h>
#endif
#interface MySocket : NSObject
{
NSString* _message;
CFSocketRef cfSocket;
CFRunLoopSourceRef cfSource;
}
- (void)listen;
#end
MySocket.m
#import "MySocket.h"
#define MAX_UDP_DATAGRAM_SIZE 65507
#implementation MySocket
static void socketCallback(CFSocketRef cfSocket, CFSocketCallBackType
type, CFDataRef address, const void *data, void *userInfo)
{
NSLog(#"socketCAllBAck was called");
}
- (void)listen
{
//Enable broadcast to network hosts
int yes = 1;
int setSockResult = 0;
_message = [[NSMutableString alloc ] initWithString: #"M-SEARCH *HTTP/1.1\r\nHOST:239.255.255.250:1900\r\nMAN:\"ssdp:discover\"\r\nST:ssdp:all\r\nMX:1\r\n\r\n"];
CFSocketContext socketContext = {0, self, NULL, NULL, NULL};
/* Create the server socket as a UDP IPv4 socket and set a callback */
/* for calls to the socket's lower-level accept() function */
cfSocket = CFSocketCreate(NULL, PF_INET, SOCK_DGRAM, IPPROTO_UDP,
kCFSocketAcceptCallBack | kCFSocketDataCallBack , (CFSocketCallBack)socketCallback, &socketContext);
if (cfSocket == NULL)
NSLog(#"UDP socket could not be created\n");
/* Re-use local addresses, if they're still in TIME_WAIT */
setSockResult = setsockopt(CFSocketGetNative(cfSocket), SOL_SOCKET, SO_BROADCAST, (void *)&yes, sizeof(yes));
if(setSockResult < 0)
NSLog(#"Could not setsockopt for broabcast");
/* Set the port and address we want to listen on */
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_len = sizeof(addr);
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr("239.255.255.250");
//inet_pton(AF_INET, "239.255.255.250", &(addr.sin_addr));
addr.sin_port = htons(1900);
//addr.sin_addr.s_addr = htonl(INADDR_ANY);
NSData *address = [ NSData dataWithBytes: &addr length: sizeof(addr) ];
if (address != nil && CFSocketSetAddress(cfSocket, (CFDataRef) address) != kCFSocketSuccess) {
NSLog(#"CFSocketSetAddress() failed\n");
CFRelease(cfSocket);
}
CFDataRef data = CFDataCreate(NULL, (const UInt8*)[_message UTF8String], [_message lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
setSockResult = CFSocketSendData(cfSocket, (CFDataRef)address, data, 0.0);
if(kCFSocketSuccess != setSockResult) NSLog(#"Unable to send data, %i", setSockResult);
else NSLog(#"Sending data");
cfSource = CFSocketCreateRunLoopSource(kCFAllocatorDefault, cfSocket, 0);
if(cfSource == NULL)
NSLog(#"CFRunLoopSourceRef is null");
CFRunLoopAddSource(CFRunLoopGetCurrent(), cfSource, kCFRunLoopDefaultMode);
NSLog(#"Socket listening on port 1900");
CFRelease(cfSource);
CFRelease(cfSocket);
[address release];
data = nil;
CFRunLoopRun();
}
- (void)dealloc
{
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), cfSource, kCFRunLoopDefaultMode);
CFRelease(cfSource);
CFRelease(cfSocket);
[_message release];
[super dealloc];
}
#end
Edit: Everything runs well until the call to send data.
Is there something small but critical in order for this to work that i'm missing?
Or i'm missing the big picture?
Any help or guide is appreciated.
Thanks in advance and have a nice weekend
remove SetAdress and all will be work fine. i've tested this now;
How about use this initialization with address
CFDataRef address=CFDataCreate(kCFAllocatorDefault,(UInt8 *)&addr,sizeof(addr));