Sending data over unix domain socket for UV4L data-channel - c++

I have a question related to sending binary data from a Raspberry Pi to web client over WebRTC data channel. I have actually found a solution, but I believe it may be inefficient, and I'm not sure exactly why it works.
Basically, I have a UV4L server with WebRTC data channels enabled. My goal is to send data gathered over I2C to a web client over WebRTC. The UV4L documentation explains that a Unix domain socket must be created and data is passed through the socket from the UV4L server and the application running on the Raspberry. I used C code in my C++ project, since I am more familiar with the language.
This is how I'm creating the socket in my application code. The UV4L server is configured to create a connection with the appropriate socket.
struct thread_info *info = (struct thread_info *)args;
int fd = 0, connfd = 0, returned_len = 0;
fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
if (fd < 0) {
fprintf(stderr, "Failed to create socket file descriptor\n");
exit(1);
}
struct sockaddr_un addr;
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, "/tmp/uv4l.socket", sizeof(addr.sun_path)-1);
bind(fd, (struct sockaddr *) &addr, sizeof(addr));
if (listen(fd, 10)) {
fprintf(stderr, "Failed to listen on UNIX socket %d\n", errno);
exit(1);
}
socklen_t socket_length = sizeof(addr);
connfd = accept(fd,(struct sockaddr *)&addr, &socket_length);
if (connfd < 0) {
fprintf(stderr, "Failed to accept socket connection\n");
exit(1);
}
info->socketfd = connfd;
This connection is successful. I then use the file descriptor in another thread to send the data. The I2C library I'm using (pigpio) allows to copy data into as char * buffer, which I define char buffer[nb_reads];
I try to send this data using send directly, but I observe no messages on the other side of my data channel (browser). It's only when I encode the data as a base64 string that I actually get the expected result.
if (total_read > 0) {
size_t encoded_length;
unsigned char *encoded = base64_encode((const unsigned char*)buffer, total_read, &encoded_length);
ssize_t sent = send(info->socketfd, encoded, encoded_length, MSG_EOR);
if (sent < 0) {
fprintf(stderr, "Failed to send all necessary MPU6050 data");
}
free(encoded);
}
Why is it that I cannot just send the byte array directly?

WebRTC data channels can handle messages in two different binaryTypes: either Blob or ArrayBuffer. The latter is the only type of messages that UV4L supports (at the moment) and that expects to send or receive to/from the other peer (e.g. the browser). In other words, make sure the browser is interpreting the data as an ArrayBuffer.

Related

Bluetooth can receive data but cannot transmit it (socket programming in C++ to communicate Matlab)

I am using Raspberry Pi 3's internal bluetooth and I am writing a c++ code to connect the bluetooth of my windows PC. On the PC side, I use Matlab and I am able to send bytes to raspberry. However when I try to send bytes from raspberry to PC, I get the following error:
"Transport endpoint is not connected"
and Matlab says "Unsuccessful read: the specified amount of data was not returned within the timeout period".
Another interesting thing is that, when I try to send more than three bytes from Matlab, raspberry only receives the first three as if the rest did not exist. If I use two reads in a row, I am able to get 6 bytes and so on. Just pointing this odd fact since I thought it might be connected with my main problem and be a clue.
I have also tried to send a file manually, using the bluetooth symbol on menubar and it worked. So c++ code should be doing something different to cause this problem.
What is likely to be the cause of my problem? How can I send data from raspberry to my computer using c++?
My code is as follows:
(Referred website: http://people.csail.mit.edu/albert/bluez-intro/index.html)
#include <stdio.h>
#include <unistd.h>
#include <sys/socket.h>
#include <bluetooth/bluetooth.h>
#include <bluetooth/rfcomm.h>
int main(int argc, char **argv)
{
struct sockaddr_rc loc_addr = { 0 }, rem_addr = { 0 };
char buf[1024] = { 0 };
int s, client, bytes_read;
socklen_t opt = sizeof(rem_addr);
// allocate socket
s = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
bdaddr_t tempBDADDR = {0};
// bind socket to port 1 of the first available
// local bluetooth adapter
loc_addr.rc_family = AF_BLUETOOTH;
loc_addr.rc_bdaddr = tempBDADDR;
loc_addr.rc_channel = (uint8_t) 1;
bind(s, (struct sockaddr *)&loc_addr, sizeof(loc_addr));
// put socket into listening mode
listen(s, 1);
// accept one connection
client = accept(s, (struct sockaddr *)&rem_addr, &opt);
ba2str( &rem_addr.rc_bdaddr, buf );
fprintf(stderr, "accepted connection from %s\n", buf);
memset(buf, 0, sizeof(buf));
// read data from the client
bytes_read = read(client, buf, sizeof(buf));
if( bytes_read > 0 ) {
printf("received [%s]\n", buf);
}
int status = 0;
// send a message
if( status == 0 ) {
status = write(s, "hello!", 6);
}
if( status < 0 ) perror("uh oh");
// close connection
close(client);
close(s);
return 0;
}
Matlab side is as straight forward as:
b = Bluetooth('raspberrypi', 1);
fopen(b);
fwrite(b, uint('1234'));
input = fread(b,6)
fclose(b);
clear('b');
EDIT:
Just figured that I do not get the "Transport endpoint is not connected" when I use the following line. However this only allows me to connect as client, whereas matlab only has a client type of connection. So now, I am able to send data to my computer from another socket without getting any errors, but cannot read it with matlab.
status = connect(s, (struct sockaddr *)&addr, sizeof(addr));
Just figured it out. Leaving this here in case it helps someone else as well.
When a connection is accepted, a new descriptor is returned (along with a new socket). This is a significant difference from connect(). So I was wrong at the following line.
status = write(s, "hello!", 6);
changing it to
status = write(client, "hello!", 6);
worked like a charm.
(Reference: http://users.pja.edu.pl/~jms/qnx/help/tcpip_4.25_en/prog_guide/sock_advanced_tut.html)

Recreate and send packet captured by nfqueue

I capture all the packets in one side with help of nfqueue, "record" them (all the data: ip info, next protocol info etc) with nfq_get_payload and deliver them into another side with help of udp. How can I restore this packet on another side and then send to myself(2 side) like there is no udp-encapsulation between? Should I use some nfqueue API or I have to implement all the protocols packet creation (UDP, ICMP, TCP, etc)? And how should I send this restored packet?
Ok, I successfully recreated and sent forward my packet encapsulated in UDP. After recreation I needed to send this packet to another IP, but you can use original destination address. So the code snippet:
char *full_packet;
int size;
// some actions to get full_packet and size from UDP packet
// assume you recreated this: int size = nfq_get_payload(nfa, &full_packet);
int sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
// also optional string needed in my case:
setsockopt(sock, SOL_SOCKET, SO_BINDTODEVICE, "wlan0", strlen("wlan0"));
if (sock == -1) {
perror("socket() failed");
return 1;
}
struct sockaddr_in to;
struct ip *iph = (struct ip *)full_packet;
to.sin_addr.s_addr = inet_addr("192.168.0.107"); // here you can set IP address where you need send this packet
to.sin_family = AF_INET;
int bytes = sendto(sock, full_packet, size, 0, (struct sockaddr*) &to, sizeof(to));
if (bytes == -1) {
perror("send() failed");
return 1;
}
I hope this will help somebody

Simple FTP Client C++

I'm attempting to create a simple FTP client in C/C++ that will do simple operations (connect, retrieve file). What I have working so far is the connection and login. I connect using sockets onto port 21, like any regular FTP client. The trouble I'm having is connecting to the port that is specified when the command PASV is entered. I get the message, parse it, then calculate the port from the replay message when PASV is entered.
227 Entering Passive Mode (a1, a2, a3, a4, p1, p2)
DataPort = (p1 * 256) + p2
Once I have the port, I try to create another socket and connecting to it the same way. That's where my issues are. My code so far is posted below. I don't know if I need to get the server address again the same way. I'm not getting a response back from the server (if I'm actually suppose to get one, I don't know) Please ask any questions or concerns, thanks.
const int FTP_PORT = 21; // Server Port
const int SIZE = 1024; // Size of Buffers
char receiveBuff[SIZE]; // Buffer to send to the server
char sendBuff[SIZE]; // Buffer to receive from server
char pasvBuff[] = "pasv"; // Buffer to see if PASV Command was entered
char quitBuff[] = "QUIT"; // Buffer to see if QUIT Command was entered
char pasvMessage[100]; // String for PASV information
int main(int argc, char *argv[])
{
int length = 0, i=0;
int a1, a2, a3, a4, p1, p2, dataPort; //PASV Information
/* Get Server Name from User */
if (argc != 2)
{
cerr << "Usage: " << argv[0] << " server" << endl;
return 1;
}
/* Obtain Host (Server) Info */
struct hostent *host;
host = gethostbyname(argv[1]);
if (host == (struct hostent *)NULL)
{
perror("Client: gethostbyname");
return 2;
}
/* Add Server Information */
struct sockaddr_in servAdr; // Internet address of server
memset(&servAdr, 0, sizeof(servAdr)); // Clear structure
servAdr.sin_family = AF_INET; // Set address typedef
memcpy(&servAdr.sin_addr, host->h_addr, host->h_length);
servAdr.sin_port = htons(FTP_PORT); // Use FTP port
/* Create Socket to Connect to FTP Server */
int origSock; // Original socket in client
if ((origSock = socket(PF_INET, SOCK_STREAM, 0)) < 0)
{
perror("Client: generate error");
return 3;
}
/* Connect to FTP Server on Port 21 */
if (connect(origSock, (struct sockaddr *)&servAdr, sizeof(servAdr)) < 0)
{
perror("Client: connect error");
return 4;
}
/* Get Conenct Message and Print to Screen */
read(origSock, receiveBuff, sizeof(receiveBuff) - 1);
write(fileno(stdout), receiveBuff, sizeof(receiveBuff) - 1);
do
{
/* Clear Buffers */
memset(receiveBuff, 0, SIZE);
memset(sendBuff, 0, SIZE);
write(fileno(stdout), "Please enter a FTP Command: ", 28); // Write User Interface
read(fileno(stdin), sendBuff, SIZE); // Read Command from User
send(origSock, sendBuff, strlen(sendBuff) , 0); // Send Command to Server
read(origSock, receiveBuff, sizeof(receiveBuff) - 1); // Read Response from Server
write(fileno(stdout), receiveBuff, sizeof(receiveBuff) - 1); // Print Response from Server to screen
/* If PASV Command was Entered */
if (strncmp(sendBuff, pasvBuff, 4) == 0)
{
sscanf(receiveBuff, "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)", &a1,&a2,&a3,&a4,&p1,&p2);
dataPort = (p1 * 256) + p2;
struct sockaddr_in servAdr2; // Internet address of server
memset(&servAdr2, 0, sizeof(servAdr2)); // Clear structure
servAdr2.sin_family = AF_INET; // Set address typedef
memcpy(&servAdr2.sin_addr, host->h_addr, host->h_length);
servAdr.sin_port = htons(dataPort); // Use FTP port
/* Create Socket to Connect to FTP Server */
int dataSock; // Data socket in client
if ((dataSock = socket(PF_INET, SOCK_STREAM, 0)) < 0)
{
perror("Client: generate error");
return 3;
}
/* Connect to FTP Server on Data Port */
if (connect(dataSock, (struct sockaddr *)&servAdr, sizeof(servAdr)) < 0)
{
perror("Client: connect error");
return 4;
}
read(dataSock, receiveBuff, sizeof(receiveBuff) - 1);
write(fileno(stdout), receiveBuff, sizeof(receiveBuff) - 1);
}
} while (strncmp(sendBuff, quitBuff, 4) != 0); // Go until QUIT Command is entered
close(origSock);
return 0;
}
When you are parsing the PASV reply, you are populating the servAdr2 variable, except for its sin_port field. You are assigning the reported port to the servAdr.sin_port field instead. You are then connecting the data socket using servAdr instead of servAdr2. So, you are effectively connecting the data socket to the original IP address of the server on the reported port, instead of connecting to the reported IP address (which can be different than the server IP). a1-a4 are the IPv4 octets of the IP address you should be connecting to.
That said, if the server supports the EPSV command, you really should use that instead. It is much easier to parse then PASV, as PASV does not have a standardized format (so be prepared to parse multiple vendor-specific formats). EPSV solves that problem by standardizing the format in a machine-parsable manner.
As for why you are not getting any response, it is because you are not telling the server to send any files over the open data connection. Sending PASV merely opens the server's data port. After you connect to it, you then have to send a STOR or RETR command on the control socket to actually perform a file transfer over the data socket. You also have to read the server's final response on the control socket after the transfer is finished, before you can then send any new commands.
This is a C++ sockets library I wrote that does that has a FTP client (connect, retrieve list and files). The comments in the code explain
https://github.com/pedro-vicente/lib_netsockets
Basically:
FTP uses two TCP connections to transfer files : a control connection and a data connection
connect a socket(control socket) to a ftp server on the port 21
receive on the socket a message from the ftp server(code : 220)
send login to the ftp server using the command USER and wait for confirmation
(331)
send password using the command PASS and wait for confirmation that you are logged on the server (230)
receive file:
use the passive mode: send command PASV
receive answer with an IP address and a port (227), parse this message.
connect a second socket(a data socket) with the given configuration
use the command RETR on the control socket
receive data through the data socket, close data socket.
leave session using on the control socket the command QUIT.

Client connects to server but server does not think client has connected in C++

I have a C++ program, using mpi, that follows a typical client server model. Each mpi instance of the client connects to a corresponding mpi instance of the server. This has worked relatively well until I have had to do some testing with added latency (1 second of added latency to be precise).
Problem:
Sometimes one of the server processes do not think the client has connected but the client thinks it has connected. i.e. After using gdb, the server is waiting at accept() but the client has continued on past connect(). Thus, it appears the client thinks it has connected when the server does not think it has connected.
My best guess is that I need to set an sock-option somewhere, however talking to fellow programmers and googling has not yielded any helpful results.
EDIT:
There are two sets of MPI processes (so two different calls to mpirun), the accept() and connect() calls are for the sockets, which are between the two sets of MPI processes. It is openmpi.
The code (from someone else's code, actually) [reduced]:
Client (connect code): (m_socket is the actual socket)
if (-1 == m_socket)
{
perror("cannot create socket");
exit(EXIT_FAILURE);
}
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
res = inet_pton(AF_INET, host_ip, &addr.sin_addr);
if (0 > res)
{
perror("error: first parameter is not a valid address family");
close(m_socket);
exit(EXIT_FAILURE);
}
else if (0 == res)
{
perror("error: second parameter does not contain valid IP address");
close(m_socket);
exit(EXIT_FAILURE);
}
//backoff
for (int sec = 1; sec < 20000; sec++ )
{
int ret;
if (0 == (ret = connect(m_socket, (struct sockaddr *)&addr, sizeof(addr))))
{
return;
}
sleep(1);
close(m_socket);
m_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
}
perror("connect failed");
close(m_socket);
exit(EXIT_FAILURE);
Server: (m_socket is the actual socket)
int socket = ::accept(m_socket, NULL, NULL);
if(socket < 0)
{
fprintf(stderr, "accept() failed: %s\n", strerror(errno));
close(m_socket);
exit(EXIT_FAILURE);
}
It looks like you're trying to do your connect/accept manually rather than with MPI. You might take a look at the example on Deino (http://mpi.deino.net/mpi_functions/MPI_Comm_accept.html) if you're trying to use MPI for your connections.
Alternatively, you might just need to look at a more general tutorial (some available here: http://www.mcs.anl.gov/research/projects/mpi/tutorial/) of MPI to get a feel for how communication works. Most of the time and application doesn't use Connect/Accept to communicate, but uses MPI Communicators to set up communication mechanisms between processes. It's a different model (SPMD as opposed to MPMD).

Socket programming in Linux by C++

I am developing a C++ app in openSUSE 12.3 and one of it's part is responsible to send data to a device via Socket (in LAN). I am using this code
int sockfd, portno, n;
struct sockaddr_in serv_addr;
struct hostent *printer;
portno = 9100;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0) error("ERROR opening socket\n");
printer = gethostbyname("100.0.69.23");
if(printer == NULL) error("No such device on 100.0.69.23\n");
//set bit set to zero
bzero((char *) &serv_addr, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
bcopy((char *) printer->h_addr, (char *) &serv_addr.sin_addr.s_addr, printer- >h_length);
serv_addr.sin_port = htons(portno);
if(connect(sockfd, (struct sockaddr *) & serv_addr, sizeof(serv_addr)) < 0)
{error("ERROR connecting");
return;
}
n = write(sockfd, data, datalenght);
if(n < 0) error("ERROR sending command to printer");
n = read(sockfd, buffer, 200);
I think the code is correct but the connect function returns -1 and seems that could not connect to the device (printer) . This code was written in openSUSE 11 and was working OK and I could send/receive data to device but when I copy/paste it to new system (openSUSE 12.3) it gives me failure in connecting. I ping result on the specific IP which is in use show that device is reachable via LAN
I think you should consider the possibility that hostent returned by gethostbyname function might have AF_INET6 address family (in which case it will be IPv6 instead of IPv4 address).
http://linux.die.net/man/3/gethostbyname
So you can either use GNU extension function gethostbyname2 function that will allow you to specify address family.
printer = gethostbyname2("100.0.69.23", AF_INET);
Or instead you can use getaddrinfo function, as gethostbyname function is said to be obsolete, by the documentation.
As already mentioned, you are checking for printer == NULL before initializing it. I think you meant the following instead:
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (sockfd < 0) error("ERROR opening socket\n");
printer = gethostbyname("100.0.69.23");
...
Also the structure of the code seems to indicate that when you want to send a command to the printer you connect(), write() then read(), which is OK if you are only ever sending one command, but suboptimal if you are sending multiple commands. In the latter case you want to separate the connect() from the write() as it's fairly expensive to connect so you want to do it just once.