td;lr: trying to echo "Hello World" to an HTTP client but getting issues with the socket closing too soon and mysterious read errors from wrk benchmark tool.
I am trying to make a simple "Hello World" HTTP server with the picoev event loop library but the client/peer connection is dropping too soon and wrk benchmark tool returns read errors for whatever reason I'm not aware. This is the code I'm using:
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include "picoev.h"
#define HOST 0 /* 0x7f000001 for localhost */
#define PORT 8080
#define MAX_FDS 1024 * 128
#define TIMEOUT_SECS 10
char buf[1024];
ssize_t response;
int listen_sock;
static void close_conn(picoev_loop* loop, int fd)
{
picoev_del(loop, fd);
close(fd);
}
static void write_callback(picoev_loop* loop, int fd, int events, void* cb_arg)
{
// check whether neither events nor timeouts are present
if ((events & PICOEV_TIMEOUT) != 0) {
/* timeout */
close_conn(loop, fd);
} else if ((events & PICOEV_READ) != 0) {
/* update timeout, and read */
picoev_set_timeout(loop, fd, TIMEOUT_SECS);
ret = read(fd, buf, sizeof(buf));
if (ret == 0 | ret == -1) {
close_conn(loop, fd);
}
else {
write(fd, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: 13\r\nConnection: close\r\n\r\nHello, world!", ret);
close_conn(loop, fd);
}
}
}
static void accept_callback(picoev_loop* loop, int fd, int events, void* cb_arg)
{
int newfd = accept4(fd, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC);
if (newfd != -1) {
picoev_add(loop, newfd, PICOEV_READ, TIMEOUT_SECS, write_callback, NULL);
}
}
int main(void)
{
picoev_loop* loop;
/* listen to port */
listen_sock = socket(AF_INET, SOCK_STREAM, 0);
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, 1, sizeof(1));
struct sockaddr_in listen_addr;
listen_addr.sin_family = AF_INET;
listen_addr.sin_port = htons(PORT);
listen_addr.sin_addr.s_addr = htonl(HOST);
bind(listen_sock, (struct sockaddr*)&listen_addr, sizeof(listen_addr));
listen(listen_sock, 1000000);
/* init picoev */
picoev_init(MAX_FDS);
/* create loop */
loop = picoev_create_loop(60);
/* add listen socket */
picoev_add(loop, listen_sock, PICOEV_READ, 1, accept_callback, NULL);
/* loop */
while (1) {
// Picoev async call to write etc..
picoev_loop_once(loop, 10);
}
/* cleanup */
picoev_destroy_loop(loop);
picoev_deinit();
return 0;
}
Curling with curl http://0.0.0.0:8080/ -v returns:
* Trying 0.0.0.0...
* TCP_NODELAY set
* Connected to 0.0.0.0 (127.0.0.1) port 8080 (#0)
> GET / HTTP/1.1
> Host: 0.0.0.0:8080
> User-Agent: curl/7.52.1
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: text/html
< Content-Length: 13
* transfer closed with 13 bytes remaining to read
* Curl_http_done: called premature == 1
* stopped the pause stream!
* Closing connection 0
curl: (18) transfer closed with 13 bytes remaining to read
or the following after trying to benchmark thousands of concurrent connections a few times after another:
* Trying 0.0.0.0...
* TCP_NODELAY set
* connect to 0.0.0.0 port 8080 failed: Connection refused
* Failed to connect to 0.0.0.0 port 8080: Connection refused
* Closing connection 0
curl: (7) Failed to connect to 0.0.0.0 port 8080: Connection refused
and wrk -t1 -c400 http://0.0.0.0:8080/ returns all errors being read:
Running 10s test # http://0.0.0.0:8080/
1 threads and 400 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 0.00us 0.00us 0.00us -nan%
Req/Sec 0.00 0.00 0.00 -nan%
0 requests in 10.08s, 9.05MB read
Socket errors: connect 0, read 249652, write 0, timeout 0
Requests/sec: 0.00
Transfer/sec: 0.90MB
I don't understand if the problem is either the socket closing too soon, the response (ret) being incorrect, zombie fd's not being killed or a combination of them. Trying to strace the program doesn't give any valuable info as to where the issue lies, just a lot of epoll_wait's. I've already tried many HTTP response variations to no avail and as you can see I'm trying to kill any zombie or erring fd as soon as necessary but either I'm doing it wrong or the problem lies elsewhere. Can someone help me pinpoint the issue where it belongs?
In this line of code:
write(fd, "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: 13\r\nConnection: close\r\n\r\nHello, world!", ret);
You use ret for the third parameter of your call to write(). This parameter is used to indicate to write() how many bytes should be written.
However, ret was used to store the result of a call to read(). Thus, there is no relationship between the value passed to write() and the size of the message you want to send.
Fix this by initializing ret with the length of the message you want to send.
const char *msg = "HTTP/1.1 ...";
ret = strlen(msg);
write(fd, msg, ret);
Related
I've been making a map making robot car with Arduino for class. I want to make a user interface for it in C (on a PC running Linux) that would work like this: the user can press a Start and a Stop button, or click a specific area of the map to send the robot to there. Right now my test setup code looks like this:
Arduino:
`
if (BTSerial.available() > 0) {
c = BTSerial.readStringUntil('\n').toInt();
BTSerial.write(c);
if(c == 8) {
Buzzing(SOS);
BTSerial.println("eight");
}
}
**PC program**:
#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 addr = { 0 };
int s, status;
char dest[18] = "98:DA:60:03:F2:92";
// allocate a socket
s = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
// set the connection parameters (who to connect to)
addr.rc_family = AF_BLUETOOTH;
addr.rc_channel = (uint8_t) 1;
str2ba( dest, &addr.rc_bdaddr );
// connect to server
status = connect(s, (struct sockaddr *)&addr, sizeof(addr));
// send a message
if( status == 0 ) {
status = write(s, "8", 2);
}
if( status < 0 ) perror("uh oh");
int client, bytes_read;
char buf[1024] = { 0 };
// put socket into listening mode
listen(s, 1);
// read data from the client
bytes_read = read(client, buf, sizeof(buf));
if( bytes_read > 0 ) {
printf("received [%s]\n", buf);
}
close(s);
return 0;
}
`
Ideally if I send the number 8 to the Arduino it would send back the string "eight". When I run my PC program, my PC connects to the Arduino (I get a notification from the OS that my PC is connected and also the led on my HC-06 Bluetooth module connected to the Arduino stops blinking signaling that a device was connected to it) and the buzzer connected to the Arduino starts buzzing the morse code of SOS as expected. However after a second my program terminates, the Bluetooth connection ends (I get a notification that my PC is disconnected and the led on the Bluetooth module starts blinking again) and I don't get back the expected "eight" string.
I'm still just a beginner when it comes to the C language and since I can not find a detailed documentation of BlueZ, I'm kind of stuck. Any help would be greatly appreciated!
I tried to combine the server and the client code from this site: https://people.csail.mit.edu/albert/bluez-intro/x502.html#rfcomm-server.c
I also tested my code on the Arduino using Putty on PC and it worked with it properly.
Calling listen on the socket doesn't do what you think it does. Listening does not mean "wait for data". It means "wait for connect". And you cannot read from the listening socket; you can only accept the connection.
Your socket is already connected. Don't listen. Just read.
So after a bit of work I finally could get it working. I only needed to change the first parameter of the read() function. Here's my final code:
#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 addr = { 0 }, rem_addr = { 0 };
int s, status;
char dest[18] = "98:DA:60:03:F2:92";
// allocate a socket
s = socket(AF_BLUETOOTH, SOCK_STREAM, BTPROTO_RFCOMM);
// set the connection parameters (who to connect to)
addr.rc_family = AF_BLUETOOTH;
addr.rc_channel = (uint8_t) 1;
str2ba( dest, &addr.rc_bdaddr );
// connect to server
status = connect(s, (struct sockaddr *)&addr, sizeof(addr));
// send a message
if( status == 0 ) {
status = write(s, "8", 2);
}
if( status < 0 ) perror("uh oh");
int bytes_read;
char buf[1024] = { 0 };
// read data from the client
bytes_read = read(s, buf, sizeof(buf));
if( bytes_read > 0 ) {
printf("%s", buf);
}
close(s);
return 0;
}
This code sends the number "8" to the Arduino, to which the Arduino replies with the string "eight". It's probably not the nicest C code for Bluetooth connection, but at least it's working I guess.
I have a client server connection where the client is sending data to the server.
while (1) {
bzero(buffer, 256);
sleep(1);
n = read(sock, buffer);
if(n < 0) error("ERROR reading from socket");
c = message[0];
//do something
}//close while loop
The issue i only want to wait for a read to happen only for some seconds - in my code, if the client does not send anything, it gets stuck waiting for the server to read something.
How can I wait for a read to happen only some seconds please?
If your socket is non-blocking you can use the select function.
If your socket is blocking you can set a read timeout using the setsockopt function. See this stackoverflow question for more details. Linux: is there a read or recv from socket with timeout?
You can use select() api for this purpose. In this api u can mention the time in select api in seconds and microseconds.
Basically the read call attempts to read so if you don't want to get stack on it you've to declare the sock variable as non-blocking or to use the select function with timeout (man select). In the first case you can't wait for some seconds but you can try to read k times and then go through. Here's the example for non-blocking socket:
/*
* Non-blocking socket solution
* just put the read in a for-loop
* if you want to read k times
*/
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
int r1;
/*Setting the socket as non-blocking*/
int flags = fcntl(sock, F_GETFL, 0);
fcntl(sock, F_SETFL, flags | O_NONBLOCK);
errno = 0; /*If the read fails it sets errno*/
if((r1=read(sock,buf_in,N))== -1) { /*If the read returns an error*/
if(errno != EAGAIN && errno != EWOULDBLOCK){ /*If the error is not caused by the non-blocking socket*/
perror("Error in read\n");
exit(EXIT_FAILURE);
}
}
Here's the select solution:
/*
* Select solution.
* This is not a complete solution but
* it's almost everything you've to do
*/
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/select.h>
#define SVC(r,c,e) \
errno = 0; \
if((r=c)==-1) { perror(e);exit(errno); }
int r = 0;
int fd_skt;
fd_set rdset;
fd_set set;
struct timeval tv; /*Timer structure*/
int fd_num_max = 0; /*Maximum opened file descriptor*/
if(fd_skt > fd_num_max) fd_num_max = fd_skt;
FD_ZERO(set);
FD_SET(fd_skt,set); /*fd_skt is where you're waiting for new connection request*/
/*Setting the timer*/
tv.tv_sec = 0;
tv.tv_usec = 200*1000;
rdset = set;
SVC(r,select((fd_num_max+1),(&rdset),NULL,NULL,&tv),"Unable to select\n");
Note: This is a project for homework, I will try to write the remaining code, but cannot figure out why this is unable to connect to an input URL.
I was given skeleton code that I modified a bit to receive an input URL. Expected usage could be: ./a.out http://google.com
For whatever reason it never succeeds in connecting. The error message "could not connect" always is printed. Later I will need to take a file from the URL and save it to the local directory but I will try to figure out how to do that (my guess is that it has to do with recv() in the code below). In the case of "http://google.com" I would be expected to take "index.html".
The skeleton code is using connect() but the man page for getaddrinfo() uses bind() which seems to be much faster but also is not working. Using connect() it never seems to leave the for loop (Edit: It never leaves because it seems to be stuck trying to connect):
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char** argv) {
// Alex: Input usage (expecting one URL)
if (argc != 2) {
printf("Usage: ./hw1 URL\n");
exit(1);
}
// Alex: Set noHttp as argv[1] and remove "http://"
char* noHttp = argv[1];
char http[] = "http://";
if (strlen(noHttp) > 7 && !strncmp(noHttp, http, 7)) noHttp += 7;
else {
printf("Invalid URL, expecting http://host/path\n");
exit(1);
}
printf("%s\n", noHttp);
struct addrinfo hints;
struct addrinfo* result, * rp;
int sock_fd, s;
// Alex: I moved assigning hints.ai_socktype after memset()
memset(&hints, 0, sizeof(struct addrinfo));
//hints.ai_socktype = SOCK_STREAM;
s = getaddrinfo(noHttp, "8080", &hints, &result); // To Stack Overflow: This changed to "80", I am leaving it here because there are comments about it
if (0 != s) {
perror("Error populating address structure");
exit(1);
}
int i = 0;
for (rp = result; rp != NULL; rp = rp->ai_next) {
printf("i = %d\n", i);
i++;
//printf("rp->ai_flags = %d\n", rp->ai_flags);
printf("rp->ai_family = %d\n", rp->ai_family);
printf("rp->ai_socktype = %d\n", rp->ai_socktype);
printf("rp->ai_protocol = %d\n", rp->ai_protocol);
sock_fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
printf("sock_fd = %d\n", sock_fd);
if (sock_fd == -1) continue;
// Success
if (connect(sock_fd, rp->ai_addr, rp->ai_addrlen) != -1) break;
close(sock_fd);
}
if (rp == NULL) {
fprintf(stderr, "could not connect\n");
exit(1);
}
freeaddrinfo(result);
char buf[255];
memset(&buf, 0, sizeof(buf));
int recv_count = recv(sock_fd, buf, 255, 0);
if (recv_count < 0) {
perror("Receive failed");
exit(1);
}
printf("%s",buf);
shutdown(sock_fd, SHUT_RDWR);
return 0;
}
Edit: I replaced "8080" with "80" as Uku Loskit recommended.
Your program looks OK to me, run netcat on port 8080 and connet to the host:
$ echo "Hello" | ncat -l 8080
will return:
$ gcc -Wall sample.c
$ ./a.out http://127.0.0.1
127.0.0.1
i = 0
rp->ai_family = 2
rp->ai_socktype = 1
rp->ai_protocol = 6
sock_fd = 3
Hello
$
in order to connect to HTTP, you need to send HTTP request first or it will block, add after the line 64:
freeaddrinfo(result);
send(sock_fd, "GET / HTTP/1.1\n\n", 16, 0); // HTTP request
char buf[255];
memset(&buf, 0, sizeof(buf));
this will send the request:
GET / HTTP/1.1
and change the port to 80, it should work:
$ ./a.out http://google.com
google.com
i = 0
rp->ai_family = 2
rp->ai_socktype = 1
rp->ai_protocol = 6
sock_fd = 3
HTTP/1.1 200 OK
Date: Sun, 01 Sep 2013 21:05:16 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html; charset=ISO-8859-1
P3P: CP="This is not a P3P policy! See http://www.google.com/support/accounts/bin/answer.py?hl=en&answer=151
$
You should be connecting on port 80, not 8080. Port 80 is the default for HTTP.
When you change your port number to 80 and connect to http://google.com, it'll work as expected, but hangs on the recv() call because an HTTP server won't send anything to you until you ask it for something. Sp.'s answer gives you an example of how to do this by adding a send() call before your recv() call.
What's happening right now is you're connecting to it, and it's waiting for you to tell it what you want. What you are doing is just waiting for it to send you something with your recv() call, so you're both going to just wait until it times out.
I want to write a C program to generate a Get Request without using any external libraries. Is this possible using only C libraries, using sockets ? I'm thinking of crafting a http packet(using proper formatting) and sending it to the server. Is this the only possible way or is there a better way ?
Using BSD sockets or, if you're somewhat limited, say you have some RTOS, some simpler TCP stack, like lwIP, you can form the GET/POST request.
There are a number of open-source implementations. See the "happyhttp" as a sample ( http://scumways.com/happyhttp/happyhttp.html ). I know, it is C++, not C, but the only thing that is "C++-dependant" there is a string/array management, so it is easily ported to pure C.
Beware, there are no "packets", since HTTP is usually transfered over the TCP connection, so technically there is only a stream of symbols in RFC format. Since http requests are usually done in a connect-send-disconnect manner, one might actually call this a "packet".
Basically, once you have an open socket (sockfd) "all" you have to do is something like
char sendline[MAXLINE + 1], recvline[MAXLINE + 1];
char* ptr;
size_t n;
/// Form request
snprintf(sendline, MAXSUB,
"GET %s HTTP/1.0\r\n" // POST or GET, both tested and works. Both HTTP 1.0 HTTP 1.1 works, but sometimes
"Host: %s\r\n" // but sometimes HTTP 1.0 works better in localhost type
"Content-type: application/x-www-form-urlencoded\r\n"
"Content-length: %d\r\n\r\n"
"%s\r\n", page, host, (unsigned int)strlen(poststr), poststr);
/// Write the request
if (write(sockfd, sendline, strlen(sendline))>= 0)
{
/// Read the response
while ((n = read(sockfd, recvline, MAXLINE)) > 0)
{
recvline[n] = '\0';
if(fputs(recvline, stdout) == EOF)
{
printf("fputs() error\n");
}
/// Remove the trailing chars
ptr = strstr(recvline, "\r\n\r\n");
// check len for OutResponse here ?
snprintf(OutResponse, MAXRESPONSE,"%s", ptr);
}
}
POSIX 7 minimal runnable example
Let's fetch http://example.com.
wget.c
#define _XOPEN_SOURCE 700
#include <arpa/inet.h>
#include <assert.h>
#include <netdb.h> /* getprotobyname */
#include <netinet/in.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
int main(int argc, char** argv) {
char buffer[BUFSIZ];
enum CONSTEXPR { MAX_REQUEST_LEN = 1024};
char request[MAX_REQUEST_LEN];
char request_template[] = "GET / HTTP/1.1\r\nHost: %s\r\n\r\n";
struct protoent *protoent;
char *hostname = "example.com";
in_addr_t in_addr;
int request_len;
int socket_file_descriptor;
ssize_t nbytes_total, nbytes_last;
struct hostent *hostent;
struct sockaddr_in sockaddr_in;
unsigned short server_port = 80;
if (argc > 1)
hostname = argv[1];
if (argc > 2)
server_port = strtoul(argv[2], NULL, 10);
request_len = snprintf(request, MAX_REQUEST_LEN, request_template, hostname);
if (request_len >= MAX_REQUEST_LEN) {
fprintf(stderr, "request length large: %d\n", request_len);
exit(EXIT_FAILURE);
}
/* Build the socket. */
protoent = getprotobyname("tcp");
if (protoent == NULL) {
perror("getprotobyname");
exit(EXIT_FAILURE);
}
socket_file_descriptor = socket(AF_INET, SOCK_STREAM, protoent->p_proto);
if (socket_file_descriptor == -1) {
perror("socket");
exit(EXIT_FAILURE);
}
/* Build the address. */
hostent = gethostbyname(hostname);
if (hostent == NULL) {
fprintf(stderr, "error: gethostbyname(\"%s\")\n", hostname);
exit(EXIT_FAILURE);
}
in_addr = inet_addr(inet_ntoa(*(struct in_addr*)*(hostent->h_addr_list)));
if (in_addr == (in_addr_t)-1) {
fprintf(stderr, "error: inet_addr(\"%s\")\n", *(hostent->h_addr_list));
exit(EXIT_FAILURE);
}
sockaddr_in.sin_addr.s_addr = in_addr;
sockaddr_in.sin_family = AF_INET;
sockaddr_in.sin_port = htons(server_port);
/* Actually connect. */
if (connect(socket_file_descriptor, (struct sockaddr*)&sockaddr_in, sizeof(sockaddr_in)) == -1) {
perror("connect");
exit(EXIT_FAILURE);
}
/* Send HTTP request. */
nbytes_total = 0;
while (nbytes_total < request_len) {
nbytes_last = write(socket_file_descriptor, request + nbytes_total, request_len - nbytes_total);
if (nbytes_last == -1) {
perror("write");
exit(EXIT_FAILURE);
}
nbytes_total += nbytes_last;
}
/* Read the response. */
fprintf(stderr, "debug: before first read\n");
while ((nbytes_total = read(socket_file_descriptor, buffer, BUFSIZ)) > 0) {
fprintf(stderr, "debug: after a read\n");
write(STDOUT_FILENO, buffer, nbytes_total);
}
fprintf(stderr, "debug: after last read\n");
if (nbytes_total == -1) {
perror("read");
exit(EXIT_FAILURE);
}
close(socket_file_descriptor);
exit(EXIT_SUCCESS);
}
GitHub upstream.
Compile:
gcc -ggdb3 -std=c99 -Wall -Wextra -o wget wget.c
Get http://example.com and output to stdout:
./wget example.com
We see something like:
debug: before first read
debug: after a read
HTTP/1.1 200 OK
Age: 540354
Cache-Control: max-age=604800
Content-Type: text/html; charset=UTF-8
Date: Tue, 02 Feb 2021 15:21:14 GMT
Etag: "3147526947+ident"
Expires: Tue, 09 Feb 2021 15:21:14 GMT
Last-Modified: Thu, 17 Oct 2019 07:18:26 GMT
Server: ECS (nyb/1D11)
Vary: Accept-Encoding
X-Cache: HIT
Content-Length: 1256
<!doctype html>
<html>
...
</html>
After printing the reply, this command hangs for most servers until timeout, and that is expected:
either server or client must close the connection
we (client) are not doing it
most HTTP servers leave the connection open until a timeout expecting further requests, e.g. JavaScript, CSS and images following an HTML page
we could parse the response, and close when Content-Length bytes are read, but we didn't for simplicity. What HTTP response headers are required says that if Content-Length
is not sent, the server can just close to determine length.
We could however make the host close by passing adding the HTTP 1.1 standard header Connection: close to the server:
char request_template[] = "GET / HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n";
The connection part also works with the IP:
host example.com
gives:
example.com has address 93.184.216.34
example.com has IPv6 address 2606:2800:220:1:248:1893:25c8:1946
and so we do:
./wget 93.184.216.34
however, the reply is an error, because we are not setting the Host: properly in our program, and that is required in HTTP 1.1.
Tested on Ubuntu 18.04.
Why doesn't POSIX supply wget?
It is a great shame, considering that all main capabilities are in place! Is wget or similar programs always available on POSIX systems?
Server examples
minimal POSIX C example: Send and Receive a file in socket programming in Linux with C/C++ (GCC/G++)
minimal Android Java example: how to create Socket connection in Android?
“Without any external libraries” strictly speaking would exclude libc as well, so you'd have to write all syscalls yourself. I doubt you mean it that strict, though. If you don't want to link to another library, and don't want to copy source code from another library into your application, then directly dealing with the TCP stream using the socket API is your best approach.
Creating the HTTP request and sending it over a TCP socket connection is easy, as is reading the answer. It's parsing the answer which is going to be real tricky, particularly if you aim to support a reasonably large portion of the standard. Things like error pages, redirects, content negotiation and so on can make our life quite hard if you're talking to arbitrary web servers. If on the other hand the server is known to be well-behaved, and a simple error message is all right for any unexpected server response, then that is reasonably simple as well.
Try Socket Programming, the below C++ code issues a simple GET Request to specified host and prints the response header and content
Tested in Windows 10
#include <windows.h>
#include <string>
#include <stdio.h>
#include <winsock2.h>
using std::string;
SOCKET conn;
WSADATA wsaData;
struct hostent *hp;
unsigned int addr;
struct sockaddr_in server;
long fileSize;
const int bufSize = 512;
char readBuffer[bufSize], sendBuffer[bufSize], tmpBuffer[bufSize];
char *memBuffer=NULL;
char *headerBuffer=NULL;
long totalBytesRead, thisReadSize, headerLen;
char *tmpResult=NULL, *result;
char* antenna(string host,string path);
SOCKET connectToServer(char *szServerName, WORD portNum);
int getHeaderLength(char *content);
int main(){
if(WSAStartup(0x101, &wsaData) != 0){printf("startup failure");}
memBuffer = antenna("www.spreadsheets.google.com", "/feeds/list/{Published_Sheet_ID-1}/1/public/values?alt=json");
printf("Response content:\n%s\n\n", memBuffer);
memBuffer = antenna("www.spreadsheets.google.com", "/feeds/list/{Published_Sheet_ID-2}/1/public/values?alt=json");
printf("Response content:\n%s", memBuffer);
WSACleanup();
}
char *antenna(string host, string path){
fileSize=0;
totalBytesRead=0;
memBuffer=NULL;
headerBuffer=NULL;
tmpResult=NULL,
conn = connectToServer((char*)host.c_str(), 80);
if(conn == 0){printf("No Internet connection");}
sprintf(sendBuffer, "GET %s HTTP/1.0 \r\nHost: %s\r\nConnection: close\r\n\r\n", path.c_str(),host.c_str());
send(conn, sendBuffer, strlen(sendBuffer), 0);
printf("Request Format: \n%s",sendBuffer);
while(1){
memset(readBuffer, 0, bufSize);
thisReadSize = recv (conn, readBuffer, bufSize, 0);
if ( thisReadSize <= 0 ){break;}
tmpResult = (char*)realloc(tmpResult, thisReadSize+totalBytesRead);
memcpy(tmpResult+totalBytesRead, readBuffer, thisReadSize);
totalBytesRead += thisReadSize;
}
headerLen = getHeaderLength(tmpResult);
long contenLen = totalBytesRead-headerLen;
result = new char[contenLen+1];
memcpy(result, tmpResult+headerLen, contenLen);
result[contenLen] = 0x0;
char *myTmp;
myTmp = new char[headerLen+1];
strncpy(myTmp, tmpResult, headerLen);
myTmp[headerLen] = 0;
delete(tmpResult);
headerBuffer = myTmp;
printf("Response Header: \n%s",headerBuffer);
fileSize = contenLen;
closesocket(conn);
if(fileSize != 0){
delete(memBuffer);
delete(headerBuffer);
}
return(result);
}
SOCKET connectToServer(char *szServerName, WORD portNum)
{
conn = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (conn == INVALID_SOCKET){return 0;}
if(inet_addr(szServerName)==INADDR_NONE){hp=gethostbyname(szServerName);}
else{
addr=inet_addr(szServerName);
hp=gethostbyaddr((char*)&addr,sizeof(addr),AF_INET);
}
if(hp==NULL){closesocket(conn);return 0;}
server.sin_addr.s_addr=*((unsigned long*)hp->h_addr);
server.sin_family=AF_INET;
server.sin_port=htons(portNum);
if(connect(conn,(struct sockaddr*)&server,sizeof(server)))
{
closesocket(conn);
return 0;
}
return conn;
}
int getHeaderLength(char *content)
{
const char *srchStr1 = "\r\n\r\n", *srchStr2 = "\n\r\n\r";
char *findPos;
int ofset = -1;
findPos = strstr(content, srchStr1);
if (findPos != NULL)
{
ofset = findPos - content;
ofset += strlen(srchStr1);
}
else
{
findPos = strstr(content, srchStr2);
if (findPos != NULL)
{
ofset = findPos - content;
ofset += strlen(srchStr2);
}
}
return ofset;
}
To compile (using g++) :
g++ -static test.cpp -o test.exe -lws2_32
-lws2_32 specifies the linker to link with winsock dlls
I need to write program which follow such steps:
Start program (daemon)
Wait (sleep, block) until I have wifi connection up
Send/get some data from server
Wait until wifi connection goes down
goto 2
Problem with step 2. I dont know how to catch moment when there is established network connection. There is /proc/net/wireless entry, where information about available wireless connections appear, but trying to monitor it with inotify have no success. Network connection is established asynchronously.
Here is my test code with inotify (copied mostly from R.Loves book):
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/inotify.h>
#include <sys/select.h>
#define BUF_LEN 1024
int
main() {
int fd, wd, rc;
char buf[BUF_LEN];
ssize_t len, i = 0;
static fd_set read_fds;
fd = inotify_init();
if (fd == -1) {
perror("inotify_init");
exit(EXIT_FAILURE);
}
wd = inotify_add_watch(fd, "/proc/net/wireless", IN_ALL_EVENTS);
if (wd == -1) {
perror("inotify_add_watch");
exit(EXIT_FAILURE);
}
for (;;) {
FD_ZERO(&read_fds);
FD_SET(wd, &read_fds);
rc = select(wd + 1, &read_fds, NULL, NULL, NULL);
if (rc == -1)
perror("select");
len = read(fd, buf, BUF_LEN);
while (i < len) {
struct inotify_event *event = (struct inotify_event *) &buf[i];
printf("wd=%d mask=%d cookie=%d len=%d dir=%s\n",
event->wd, event->mask, event->cookie, event->len,
(event-> mask & IN_ISDIR) ? "yes" : "no");
if (event->len)
printf("name=%s\n", event->name);
i += sizeof(struct inotify_event) + event->len;
}
sleep(1);
}
return 0;
}
It only catches evernt when I do cat /proc/net/wireless
Question: How to catch moment, when I have network connection running (wifi), using only Linux features?
P.S. This is my first post here, hope everything is ok.
You can detect when a network connection (not just wifi) beomes link-ready through the netlink interface, rtnetlink.
This is not an easy interface to program against, so you might wish to invoke the process "ip monitor link" instead. If you see the interface have the LOWER_UP flag, that means it's ready to send/ receive (EDIT: You may also want to check the NO_CARRIER flag is absent; see Simon's comment).
However, there is also a problem that you may have a race condition with a daemon like NetworkManager, which will (if so configured) attempt to get an IP address after the link becomes available.