Unable to connect to host using getaddrinfo() - c

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.

Related

C HTTP sockets closing prematurely and apparently giving read errors

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);

Non-blocking BIO and hang after BIO_do_connect

I am writing a small little IRC bot in C using openssl to start a secure socket. It isn't the most beautifully written bot, but its mostly just to see how the openssl API works. Currently I have the following code:
#include <stdio.h>
#include <string.h>
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
int main() {
SSL_load_error_strings();
ERR_load_BIO_strings();
OpenSSL_add_all_algorithms();
BIO *bio;
SSL_CTX * ctx = SSL_CTX_new(SSLv23_client_method());
SSL * ssl;
SSL_CTX_load_verify_locations(ctx, NULL, "/etc/ssl/certs/");
bio = BIO_new_ssl_connect(ctx);
BIO_get_ssl(bio, & ssl);
SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
BIO_set_nbio(bio, 1);
BIO_set_conn_hostname(bio, "irc.freenode.net:6697");
BIO_do_connect(bio);
if(SSL_get_verify_result(ssl) != X509_V_OK) {
printf("error\n");
}
char irc1[] = "NICK bartender\r\n";
char irc2[] = "USER bartender * * :serve(&drinks);\r\n";
BIO_write(bio, irc1, strlen(irc1));
BIO_write(bio, irc2, strlen(irc2));
fd_set read_set;
int sock = BIO_get_fd(bio, NULL);
while(1) {
FD_ZERO(&read_set);
FD_SET(sock, &read_set);
struct timeval timeout = { 0, 0 };
select(sock+1, &read_set, NULL, NULL, &timeout);
if(FD_ISSET(sock, &read_set)) {
char buf[21];
size_t x = BIO_read(bio, buf, 20);
if(x == 0) {
continue;
} else if(x == -1){
int code = ERR_get_error();
if(code == 0) {
continue;
}
printf("(%d)%s\n", code, ERR_error_string(code, NULL));
} else {
buf[x] = '\0';
printf("%s", buf);
}
}
}
}
Whenever I compile and run this code, it just hangs and prints nothing. However, if I remove line 20 (which currently puts the socket into nonblocking mode) it works fine. Why does putting the socket in non-blocking mode cause this behavior? Thank you and have a great day!
Whenever I run this code, it just hangs and prints nothing. However, if I remove line 20 (which currently puts the socket into nonblocking mode) it works fine.
BIO_do_connect returns immediately in non-blocking mode. You should loop on BIO_should_retry. Here's what the man page has to say about BIO_do_connect:
BIO_do_connect() attempts to connect the supplied BIO. It returns 1 if
the connection was established successfully. A zero or negative value
is returned if the connection could not be established, the call
BIO_should_retry() should be used for non blocking connect BIOs to
determine if the call should be retried.
Why does putting the socket in non-blocking mode cause this behavior?
The call to BIO_do_connect returns immediately; the socket/bio is probably not ready for data (yet).
An alternative to looping on BIO_do_connect/BIO_should_retry is to wait on the underlying file descriptor. Its the technique used by OpenSSL in the ocsp subcommand (the source can be found in <openssl src>/apps/ocsp.c):
if (req_timeout != -1)
BIO_set_nbio(cbio, 1);
rv = BIO_do_connect(cbio);
if ((rv <= 0) && ((req_timeout == -1) || !BIO_should_retry(cbio))) {
BIO_puts(err, "Error connecting BIO\n");
return NULL;
}
if (BIO_get_fd(cbio, &fd) < 0) {
BIO_puts(bio_err, "Can't get connection fd\n");
goto err;
}
if (req_timeout != -1 && rv <= 0) {
FD_ZERO(&confds);
openssl_fdset(fd, &confds);
tv.tv_usec = 0;
tv.tv_sec = req_timeout;
rv = select(fd + 1, NULL, (void *)&confds, NULL, &tv);
if (rv == 0) {
BIO_puts(err, "Timeout on connect\n");
return NULL;
}
}
Also see Non-blocking BIO and BIO_do_connect problem on the OpenSSL Users mailing list. There's also a few hits on Stack Overflow, but I'm not sure which is the best fit for this question:
nonblocking BIO_do_connect blocked when there is no internet connected
OpenSSL connection fails with non-blocking socket
Changing an OpenSSL BIO from blocking to non-blocking mode
Unable to establish connection using OpenSSL BIO interface

How to make an HTTP get request in C without libcurl?

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

g_io_channel + socket = server , still just get one client ? in C language

folks,
here ma code :
#include <glib.h>
#include <gio/gio.h> // gio channel
#include <sys/socket.h> //socket();
#include <netdb.h> // structure
#include <stdio.h> // printf
void deal(GIOChannel *in, GIOCondition condition, gpointer data)
{
struct sockaddr_storage income;
int insock = g_io_channel_unix_get_fd(in);
socklen_t income_len = sizeof(income);
int newsock = accept(insock, (struct sockaddr*)&income, &income_len );
if(newsock == -1)
{
printf("failure on newsock\n");
}
char buff[128];
int recv_total = 0;
int recv_byte = 128;
int recv_sizing;
while (recv_total < recv_byte ){
recv_sizing = recv(newsock,buff + recv_total,recv_byte,0);
// breaking if recv_sizing = -1 assuming as error, 0 assuming as lost communication from client suddenly
if(recv_sizing < 0 || recv_sizing == 0)
{
printf("connection lost or error while recv(); [ just guess ] number : %d \n",recv_sizing);
break;
}
recv_byte -= recv_sizing;
recv_total += recv_sizing;
}
buff[recv_total] = '\0';
//recv_sizing = recv(newsock,buff,recv_byte,0);
printf("data : %s\n",buff);
close(newsock); // close immediate and look for another some1 new
}
int main()
{
GIOChannel *in;
struct sockaddr_in my;
my.sin_addr.s_addr = INADDR_ANY;
my.sin_family = AF_INET;
my.sin_port = htons(3000);
//socket initiate root socket
int rsock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
//allow re-use address
setsockopt(rsock,SOL_SOCKET,SO_REUSEADDR,(int*)1,sizeof(int));
//binding
bind(rsock,(struct sockaddr*)&my,sizeof(my));
//listen
listen(rsock,10);
in = g_io_channel_unix_new(rsock);
g_io_add_watch(in, G_IO_IN | G_IO_OUT | G_IO_HUP, (GIOFunc) deal, NULL);
GMainLoop *loop = g_main_loop_new(NULL,FALSE); // pengganti while(1) ato gtk_main_loop
g_main_loop_run(loop);
return 0;
}
these is how it get compiled and running :
$ gcc -o dengersocket_glib dengersocket_glib.c `pkg-config --cflags --libs glib-2.0`
$ ./dengersocket_glib
the client wanna try to send :
$ echo wew | nc -v localhost 3000
nc: connect to localhost port 3000 (tcp) failed: Connection refused
Connection to localhost 3000 port [tcp/*] succeeded!
the server receiving :
$ ./dengersocket_glib
connection lost or error while recv(); [ just guess ] number : 0
data : wew
receiving ok, but when another client trying to connecting :
$ echo dor | nc -v localhost 3000
nc: connect to localhost port 3000 (tcp) failed: Connection refused
Connection to localhost 3000 port [tcp/*] succeeded!
nothing happen on server, how to make server could accept more than one client ? do fd_set really needed on these case ?
The type GIOFunc returns gboolean, not void. You're working around that by casting your function to GIOFunc when registering it as a callback.
Since your function doesn't return anything, the glib code calling it probably sees "FALSE" as the return value of the function. FALSE in glib means "I don't need to monitor this event source anymore", so while youre main loop is still running (you never call g_main_loop_quit()), it is not monitoring your socket anymore.
Implement GIOFunc correctly and your problem will probably go away.

g_io_channel + socket = server , still didnt receive proper data and just get one client ? in C language

folks,
if you dont mind please see following code :
#include <glib.h>
#include <gio/gio.h> // gio channel
#include <sys/socket.h> //socket();
#include <netdb.h> // structure
#include <stdio.h> // printf
void deal(GIOChannel *in, GIOCondition condition, gpointer data)
{
struct sockaddr_storage income;
int insock = g_io_channel_unix_get_fd(in);
socklen_t income_len = sizeof(income);
int newsock = accept(insock, (struct sockaddr*)&income, &income_len );
if(newsock == -1)
{
printf("failure on newsock\n");
}
char buff[128];
int recv_total = 0;
int recv_byte = 128;
int recv_sizing;
while (recv_total < recv_byte ){
recv_sizing = recv(newsock,buff + recv_total,recv_byte,0);
// breaking if recv_sizing = -1 assuming as error, 0 assuming as lost communication from client suddenly
if(recv_sizing < 0 || recv_sizing == 0)
{
printf("connection lost or error while recv(); [ just guess ] number : %d \n",recv_sizing);
break;
}
recv_byte -= recv_sizing;
recv_total += recv_sizing;
}
buff[recv_total] = '\0';
//recv_sizing = recv(newsock,buff,recv_byte,0);
printf("data : %s\n",buff);
close(newsock); // close immediate and look for another some1 new
}
int main()
{
GIOChannel *in;
struct sockaddr_in my;
my.sin_addr.s_addr = INADDR_ANY;
my.sin_family = AF_INET;
my.sin_port = htons(3000);
//socket initiate root socket
int rsock = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
//allow re-use address
setsockopt(rsock,SOL_SOCKET,SO_REUSEADDR,(int*)1,sizeof(int));
//binding
bind(rsock,(struct sockaddr*)&my,sizeof(my));
//listen
listen(rsock,10);
in = g_io_channel_unix_new(rsock);
g_io_add_watch(in, G_IO_IN | G_IO_OUT | G_IO_HUP, (GIOFunc) deal, NULL);
GMainLoop *loop = g_main_loop_new(NULL,FALSE); // pengganti while(1) ato gtk_main_loop
g_main_loop_run(loop);
return 0;
}
and it get compiled :
$ gcc -o dengersocket_glib dengersocket_glib.c `pkg-config --cflags --libs glib-2.0`
and now listening and look forward any packet data from client
and the client send the following packet :
$ echo wew | nc -v localhost 3000
nc: connect to localhost port 3000 (tcp) failed: Connection refused
Connection to localhost 3000 port [tcp/*] succeeded!
and now the server receive following weird packet :
$ ./dengersocket_glib
data : �o=
and my question is, where is the fault on my code ?,
1.how to get the proper packet and every single client could connect to the server ? [solved]
2.the passing data is solved, but still just could accept only one client, how to get more than one client?
int recv_total;
should be
int recv_total = 0;
With the random garbage value your recv_total has due to lack of initialization, you'll also get random garbage data in buf unless recv_total just happened to be <128, and the first char in the buffer will be garbage unless recv_total happened to be 0.
EDIT:
Also, your accept call is wrong, you cast a size to void * but are supposed to pass a pointer to a socklen_t which should contain and receive the size of the sockaddr.
socklen_t ss = sizeof(income);
accept(..., &ss);
Then, check the return value from accept, see that you got a valid socket.
if (newsock == -1) {
printf("...");
}

Resources