C http proxy server socket - not hitting - c

I am trying to understand HTTP proxy server program. I wrote a simple C based socket which sends a response to as Message received. Please find the program below.
#include <stdio.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <sys/types.h>
int main(){
int recvSocketFd = 0;
struct sockaddr_in serv_input_addr;
char inputBuff[1024];
memset(&serv_input_addr,'0',sizeof(serv_input_addr));
memset(&inputBuff, '0', sizeof(inputBuff));
recvSocketFd = socket(AF_INET,SOCK_STREAM,0);
if(recvSocketFd <= 0){
printf("\nError occurred while creating a socket\n");
return -1;
}
serv_input_addr.sin_family = AF_INET;
serv_input_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_input_addr.sin_port = htons(8080);
bind(recvSocketFd, (struct sockaddr*)&serv_input_addr, sizeof(serv_input_addr));
listen(recvSocketFd,5);
int connFd = 0;
int n = 0;
int client_length = sizeof(serv_input_addr);
while(1){
connFd = accept(recvSocketFd, (struct sockaddr*)&serv_input_addr, &client_length);
if(connFd <0){
printf("\nError in accepting messages from socket\n");
return -1;
}
n = read(connFd,inputBuff,1023);
if(n>0){
printf("\nMessage : %s\n",inputBuff);
}
write(connFd,"Message received",15);
close(connFd);
}
}
This program on running is responding properly for the below request http://<localhost>:8080/test.
I tried to set this as a proxy. So I tried to set this as manual proxy in Firefox as http://<localhost> and port as 8080.
But the request is not hitting this socket.
It would be really helpful if somebody could help me in understanding this.
Note: This is not a homework program and I am not a student. I am just trying to learn things. This statement is for our honorable disgraceful respondents.

Re-Edit:
Note the importance of checking success on bind(). There is various cases; where one could be that the socket is in a TIME_WAIT state. If one does not actually validate the bind() and abort on failure the program could possibly seemingly be running OK. listen() can be passed without error. Though on the client side Firefox most likely would report with a refused connection it is not a given.
URI-scheme-name
In your question you state that you have used http://<localhost> for proxy settings. If that is literal it will fail: You are not to include the scheme name in the proxy settings.
Note that Firefox does not validate the hostname. E.g. a4a6®←#ł´ªĸµª5d4a"#¤&%=)(=/)= fly by with no warnings.
If one include the scheme, http://, Firefox will fail to connect and one will get Server not found.
Original answer :-)
I'll leave it here even with comment on removed parts, as someone else might find it useful. (And that I used some time on writing it up.)
There are some issues here. I'll take them one by one.
First fix this, then report back if you want.
Missing header files and wrong types
read(), write() and close() are defined in <unistd.h>, but you do not include this file. It is presumably linked in, but best to add.
client_length is not signed int. You can use unsigned int, but best to use socklen_t as is defined in manual.
read() returns ssize_t, not int. Change this one as well.
Lack of error checking
No error checking where it should be. Rule #1: Always check for errors! Having trouble and not validating requests is … a big no no.
bind(), listen() and close() return int. 0 is success, -1 is error. On error errno is set.
The manual is very clear and easy to read on this. Read it for each function you use.
Include <errno.h> and use perror().
In simplest way. Aborting and no attempt to rescue we get:
int res;
...
res = bind(recvSocketFd, (struct sockaddr*)&serv_input_addr, sizeof(serv_input_addr));
if (res != 0) {
perror("bind");
return errno;
}
res = listen(recvSocketFd, 5);
if (res != 0) {
perror("listen");
return errno;
}
...
res = close(connFd);
if (res != 0) {
perror("close");
return errno;
}
write() returns ssize_t which on success is how many bytes was written, else -1 and errno set appropriately.
res_wr = write(connFd, ...);
if (res == -1) {
perror("write");
return errno;
}
/* Else check if entire buffer was written, if not, act accordingly. */
Write statement
Bad write statement:
write(connFd, "Message received", 15);
First: "Message received" is 17 bytes including terminating null. If you want to write this to connection use something like:
const char msg_received[] = "Message received";
...
write(connFd, msg_received, sizeof(msg_received));
Second: This is what you pass on to the client, in your case Firefox. Firefox would in other words get the characters:
{ 'M', 'e', 's', 's', 'a', 'g', 'e', ' ', 'r', 'e', 'c', 'e', 'i', 'v', 'e' }
Third: As you promptly do a close() after first read/write. Firefox will most likely reply with a The connection was reset. Normally you would write your inputBuff, and not some status message like this.
Print to stderr
As a side not: Whilst it is fair enough to write to stdout in this sample, it is a good habit to write errors and debugging data to stderr. Keep it as a habit. Example:
sz_buf = read(connFd, inputBuff, BUF_SIZE - 1)
inputBuff[sz_buf] = 0x00;
fprintf(stderr,
"\n====================== Message (%4d) ================\n"
"%s\n"
"--------------------------------------------------------\n"
,
sz_buf, inputBuff
);

Related

TCP Server - Recover from "Too many open files"

I'm writing a TCP server in C and find something unusual happens once the listening fd get "Too many open files" error. The accept call doesn't block anymore and returns -1 all the time.
I also tried closing the listening fd and re-opening, re-binding it, but didn't seem to work.
My questions are why accept keeps returning -1 in this situation, what am I supposed to do to stop it and make the server be able to accept new connections after any old clients closed? (the socket is of course able to accept correctly again when some connections closed)
====== UPDATE: clarification ======
The problem occurs just because the number of active clients is more than the limit of open fds, so I don't close any of the accepted fds in the sample code, just to make it reproduce more quickly.
I add the timestamp each time accept returns to the output and slow down connect frequency to once in 2 seconds, then I find that in fact the "Too many open files" error occurs immediately after the lastest success accept. So I think that is because when the maxium fds is reached, each call to accept will return immediately, and the return value is -1. (What I thought is that accept would still block, but returns -1 at the next incoming connect. The behavior of accept in this situation is my own theory, not from the man page. If it's wrong, please let me know).
So to my second question, to make it stop, I think it's a solution that stop to call accept before any connection is closed.
Also update the sample codes. Thanks for your help.
====== Sample codes ======
Here is how I test it. First set ulimit -n to a low value (like 16) and run the server program compiled from the following C source; then use the Python script to create several connections
/* TCP server; bind :5555 */
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define BUFSIZE 1024
#define PORT 5555
void error(char const* msg)
{
perror(msg);
exit(1);
}
int listen_port(int port)
{
int parentfd; /* parent socket */
struct sockaddr_in serveraddr; /* server's addr */
int optval; /* flag value for setsockopt */
parentfd = socket(AF_INET, SOCK_STREAM, 0);
if (parentfd < 0) {
error("ERROR opening socket");
}
optval = 1;
setsockopt(parentfd, SOL_SOCKET, SO_REUSEADDR,
(const void *)&optval , sizeof(int));
bzero((char *) &serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons((unsigned short)port);
if (bind(parentfd, (struct sockaddr *) &serveraddr, sizeof(serveraddr)) < 0) {
error("ERROR on binding");
}
if (listen(parentfd, 5) < 0) {
error("ERROR on listen");
}
printf("Listen :%d\n", port);
return parentfd;
}
int main(int argc, char **argv)
{
int parentfd; /* parent socket */
int childfd; /* child socket */
int clientlen; /* byte size of client's address */
struct sockaddr_in clientaddr; /* client addr */
int accept_count; /* times of accept called */
accept_count = 0;
parentfd = listen_port(PORT);
clientlen = sizeof(clientaddr);
while (1) {
childfd = accept(parentfd, (struct sockaddr *) &clientaddr, (socklen_t*) &clientlen);
printf("accept returns ; count=%d ; time=%u ; fd=%d\n", accept_count++, (unsigned) time(NULL), childfd);
if (childfd < 0) {
perror("error on accept");
/* the following 2 lines try to close the listening fd and re-open it */
// close(parentfd);
// parentfd = listen_port(PORT);
// the following line let the program exit at the first error
error("--- error on accept");
}
}
}
The Python program to create connections
import time
import socket
def connect(host, port):
s = socket.socket()
s.connect((host, port))
return s
if __name__ == '__main__':
socks = []
try:
try:
for i in xrange(100):
socks.append(connect('127.0.0.1', 5555))
print ('connect count: ' + str(i))
time.sleep(2)
except IOError as e:
print ('error: ' + str(e))
print ('stop')
while True:
time.sleep(10)
except KeyboardInterrupt:
for s in socks:
s.close()
why accept keeps returning -1 in this situation
Because you've run out of file descriptors, just like the error message says.
what am I supposed to do to stop it and make the server be able to accept new connections after any old clients closed?
Close the clients. The problem is not accept() returning -1, it is that you aren't closing accepted sockets once you're finished with them.
Closing the listening socket isn't a solution. It's just another problem.
EDIT By 'finished with them' I mean one of several things:
They have finished with you, which is shown by recv() returning zero.
You have finished with them, e.g. after sending a final response.
When you've had an error sending or receiving to/from them other than EAGAIN/EWOULDBLOCK.
When you've had some other internal fatal error that prevents you dealing further with that client, for example receiving an unparseable request, or some other fatal application error that invalidates the connection or the session, or the entire client for that matter.
In all these cases you should close the accepted socket.
The answer of EJP is correct, but it does not tell you how to deal with the situation. What you have to do is actually do something with the sockets that you get as accept returns. Simple calling close on them you won't receive anything of course but it would deal with the resource depletion problem. What you have to do to have a correct implementation is start receiving on the accepted sockets and keep receiving until you receive 0 bytes. If you receive 0 bytes, that is an indication that the peer is done using his side of the socket. That is your trigger to call close on the socket as well and deal with the resource problem.
You don't have to stop listening. That would stop your server from being able to process new requests and that is not the problem here.
The solution I implemented here was to review the value of the new (accepted) fd and if that value was equal or higher then the allowed server capacity, then a "busy" message is sent and the new connection is closed.
This solution is quite effective and allows you to inform your clients about the server's status.

why write() doesn't return 0 when it should?

I've encountered a case where using write() server-side on a remotely closed client doesn't return 0.
According to man 2 write :
On success, the number of bytes written is returned (zero indicates
nothing was written). On error, -1 is returned, and errno is set
appropriately.
From my understanding: when using read/write on a remotely closed socket, the first attempt is supposed to fail (thus return 0), and the next try should trigger a broken pipe. But it doesn't. write() acts as if it succeeded in sending the data on the first attempt, and then i get a broken pipe on the next try.
My question is why?
I know how to handle a broken pipe properly, that's not the issue. I'm just trying to understand why write doesn't return 0 in this case.
Below is the server code I wrote. Client-side, I tried a basic C client (with close() and shutdown() for closing the socket) and netcat. All three gave me the same result.
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#define MY_STR "hello world!"
int start_server(int port)
{
int fd;
struct sockaddr_in sin;
fd = socket(AF_INET, SOCK_STREAM, 0);
if (fd == -1)
{
perror(NULL);
return (-1);
}
memset(&sin, 0, sizeof(struct sockaddr_in));
sin.sin_addr.s_addr = htonl(INADDR_ANY);
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
if (bind(fd, (struct sockaddr *)&sin, sizeof(struct sockaddr)) == -1
|| listen(fd, 0) == -1)
{
perror(NULL);
close(fd);
return (-1);
}
return (fd);
}
int accept_client(int fd)
{
int client_fd;
struct sockaddr_in client_sin;
socklen_t client_addrlen;
client_addrlen = sizeof(struct sockaddr_in);
client_fd = accept(fd, (struct sockaddr *)&client_sin, &client_addrlen);
if (client_fd == -1)
return (-1);
return (client_fd);
}
int main(int argc, char **argv)
{
int fd, fd_client;
int port;
int ret;
port = 1234;
if (argc == 2)
port = atoi(argv[1]);
fd = start_server(port);
if (fd == -1)
return (EXIT_FAILURE);
printf("Server listening on port %d\n", port);
fd_client = accept_client(fd);
if (fd_client == -1)
{
close(fd);
printf("Failed to accept a client\n");
return (EXIT_FAILURE);
}
printf("Client connected!\n");
while (1)
{
getchar();
ret = write(fd_client, MY_STR, strlen(MY_STR));
printf("%d\n", ret);
if (ret < 1)
break ;
}
printf("the end.\n");
return (0);
}
The only way to make write return zero on a socket is to ask it to write zero bytes. If there's an error on the socket you will always get -1.
If you want to get a "connection closed" indicator, you need to use read which will return 0 for a remotely closed connection.
This is just how the sockets interface was written. When you have a connected socket or pipe, you are supposed to close the transmitting end first, and then the receiving end will get EOF and can shut down. Closing the receiving end first is "unexpected" and so it returns an error instead of returning 0.
This is important for pipes, because it allows complicated commands to finish much more quickly than they would otherwise. For example,
bunzip2 < big_file.bz2 | head -n 10
Suppose big_file.bz2 is huge. Only the first part will be read, because bunzip2 will get killed once it tries sending more data to head. This makes the whole command finish much quicker, and with less CPU usage.
Sockets inherited the same behavior, with the added complication that you have to close the transmitting and receiving parts of the socket separately.
The point to be observed is that, in TCP, when one side of the connection closes its
socket, it is actually ceasing to transmit on that socket; it sends a packet to
inform its remote peer that it will not transmit anymore through that
connection. It doesn't mean, however, that it stopped receiving too. (To
continue receiving is a local decision of the closing side; if it stops receiving, it can
lose packets transmitted by the remote peer.)
So, when you write() to a socket that is remotely closed, but
not locally closed, you can't know if the other end is still waiting to read
more packets, and so the TCP stack will buffer your data and try to send it. As
stated in send() manual page,
No indication of failure to deliver is implicit in a send(). Locally detected
errors are indicated by a return value of -1.
(When you write() to a socket, you are actually send()ing to it.)
When you write() a second time, though, and the remote peer has definitely
closed the socket (not only shutdown() writing), the local TCP stack has probably
already received a reset packet from the peer informing it about the error on
the last transmitted packet. Only then can write() return an error, telling
its user that this pipe is broken (EPIPE error code).
If the remote peer has only shutdown() writing, but still has the socket open,
its TCP stack will successfully receive the packet and will acknowledge the
received data back to the sender.
if you read the whole man page then you would read, in error return values:
"EPIPE fd is connected to a pipe or *socket whose reading end is closed*."
So, the call to write() will not return a 0 but rather -1 and errno will be set to 'EPIPE'

multiple requests with recv() call [duplicate]

I have an application that reads large files from a server and hangs frequently on a particular machine. It has worked successfully under RHEL5.2 for a long time. We have recently upgraded to RHEL6.1 and it now hangs regularly.
I have created a test app that reproduces the problem. It hangs approx 98 times out of 100.
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <sys/time.h>
int mFD = 0;
void open_socket()
{
struct addrinfo hints, *res;
memset(&hints, 0, sizeof(hints));
hints.ai_socktype = SOCK_STREAM;
hints.ai_family = AF_INET;
if (getaddrinfo("localhost", "60000", &hints, &res) != 0)
{
fprintf(stderr, "Exit %d\n", __LINE__);
exit(1);
}
mFD = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (mFD == -1)
{
fprintf(stderr, "Exit %d\n", __LINE__);
exit(1);
}
if (connect(mFD, res->ai_addr, res->ai_addrlen) < 0)
{
fprintf(stderr, "Exit %d\n", __LINE__);
exit(1);
}
freeaddrinfo(res);
}
void read_message(int size, void* data)
{
int bytesLeft = size;
int numRd = 0;
while (bytesLeft != 0)
{
fprintf(stderr, "reading %d bytes\n", bytesLeft);
/* Replacing MSG_WAITALL with 0 works fine */
int num = recv(mFD, data, bytesLeft, MSG_WAITALL);
if (num == 0)
{
break;
}
else if (num < 0 && errno != EINTR)
{
fprintf(stderr, "Exit %d\n", __LINE__);
exit(1);
}
else if (num > 0)
{
numRd += num;
data += num;
bytesLeft -= num;
fprintf(stderr, "read %d bytes - remaining = %d\n", num, bytesLeft);
}
}
fprintf(stderr, "read total of %d bytes\n", numRd);
}
int main(int argc, char **argv)
{
open_socket();
uint32_t raw_len = atoi(argv[1]);
char raw[raw_len];
read_message(raw_len, raw);
return 0;
}
Some notes from my testing:
If "localhost" maps to the loopback address 127.0.0.1, the app hangs on the call to recv() and NEVER returns.
If "localhost" maps to the ip of the machine, thus routing the packets via the ethernet interface, the app completes successfully.
When I experience a hang, the server sends a "TCP Window Full" message, and the client responds with a "TCP ZeroWindow" message (see image and attached tcpdump capture). From this point, it hangs forever with the server sending keep-alives and the client sending ZeroWindow messages. The client never seems to expand its window, allowing the transfer to complete.
During the hang, if I examine the output of "netstat -a", there is data in the servers send queue but the clients receive queue is empty.
If I remove the MSG_WAITALL flag from the recv() call, the app completes successfully.
The hanging issue only arises using the loopback interface on 1 particular machine. I suspect this may all be related to timing dependencies.
As I drop the size of the 'file', the likelihood of the hang occurring is reduced
The source for the test app can be found here:
Socket test source
The tcpdump capture from the loopback interface can be found here:
tcpdump capture
I reproduce the issue by issuing the following commands:
> gcc socket_test.c -o socket_test
> perl -e 'for (1..6000000){ print "a" }' | nc -l 60000
> ./socket_test 6000000
This sees 6000000 bytes sent to the test app which tries to read the data using a single call to recv().
I would love to hear any suggestions on what I might be doing wrong or any further ways to debug the issue.
MSG_WAITALL should block until all data has been received. From the manual page on recv:
This flag requests that the operation block until the full request is satisfied.
However, the buffers in the network stack probably are not large enough to contain everything, which is the reason for the error messages on the server. The client network stack simply can't hold that much data.
The solution is either to increase the buffer sizes (SO_RCVBUF option to setsockopt), split the message into smaller pieces, or receiving smaller chunks putting it into your own buffer. The last is what I would recommend.
Edit: I see in your code that you already do what I suggested (read smaller chunks with own buffering,) so just remove the MSG_WAITALL flag and it should work.
Oh, and when recv returns zero, that means the other end have closed the connection, and that you should do it too.
Consider these two possible rules:
The receiver may wait for the sender to send more before receiving what has already been sent.
The sender may wait for the receiver to receive what has already been sent before sending more.
We can have either of these rules, but we cannot have both of these rules.
Why? Because if the receiver is permitted to wait for the sender, that means the sender cannot wait for the receiver to receive before sending more, otherwise we deadlock. And if the sender is permitted to wait for the receiver, that means the receiver cannot wait for the sender to send before receiving more, otherwise we deadlock.
If both of these things happen at the same time, we deadlock. The sender will not send more until the receiver receives what has already been sent, and the receiver will not receive what has already been sent unless the sender send more. Boom.
TCP chooses rule 2 (for reasons that should be obvious). Thus it cannot support rule 1. But in your code, you are the receiver, and you are waiting for the sender to send more before you receive what has already been sent. So this will deadlock.

How to create a simple Proxy to access web servers in C

I’m trying to create an small Web Proxy in C. First, I’m trying to get a webpage, sending a GET frame to the server.
I don’t know what I have missed, but I am not receiving any response. I would really appreciate if you can help me to find what is missing in this code.
int main (int argc, char** argv) {
int cache_size, //size of the cache in KiB
port,
port_google = 80,
dir,
mySocket,
socket_google;
char google[] = "www.google.es", ip[16];
struct sockaddr_in socketAddr;
char buffer[10000000];
if (GetParameters(argc,argv,&cache_size,&port) != 0)
return -1;
GetIP (google, ip);
printf("ip2 = %s\n",ip);
dir = inet_addr (ip);
printf("ip3 = %i\n",dir);
/* Creation of a socket with Google */
socket_google = conectClient (port_google, dir, &socketAddr);
if (socket_google < 0) return -1;
else printf("Socket created\n");
sprintf(buffer,"GET /index.html HTTP/1.1\r\n\r\n");
if (write(socket_google, (void*)buffer, MESSAGE_LENGTH+1) < 0 )
return 1;
else printf("GET frame sent\n");
strcpy(buffer,"\n");
read(socket_google, buffer, sizeof(buffer));
// strcpy(message,buffer);
printf("%s\n", buffer);
return 0;
}
And this is the code I use to create the socket. I think this part is OK, but I copy it just in case.
int conectClient (int puerto, int direccion, struct sockaddr_in *socketAddr) {
int mySocket;
char error[1000];
if ( (mySocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
printf("Error when creating the socket\n");
return -2;
}
socketAddr->sin_family = AF_INET;
socketAddr->sin_addr.s_addr = direccion;
socketAddr->sin_port = htons(puerto);
if (connect (mySocket, (struct sockaddr *)socketAddr,sizeof (*socketAddr)) == -1) {
snprintf(error, sizeof(error), "Error in %s:%d\n", __FILE__, __LINE__);
perror(error);
printf("%s\n",error);
printf ("-- Error when stablishing a connection\n");
return -1;
}
return mySocket;
}
Thanks!
First, you're not checking how many bytes the write(2) call actually wrote to the socket. The return value of the call tells you that. Same for the read(2). TCP socket is a bi-directional stream, so as a rule always do both in a loop until expected number of bytes is transferred, EOF is read (zero return from read(2)), or an error occurred (which you are not checking for when reading either).
Then HTTP is rather complex protocol. Make yourself familiar with RFC 2616, especially application level connection management and transfer encodings.
Edit 0:
Hmm, there's no such thing as "simple" proxy. You need to manage multiple connections (at least client-to-proxy and proxy-to-server), so it's probably best to look into select(2)/poll(2)/epoll(4)/kqueue(2) family of system call, which allow you to multiplex I/O. This is usually combined with non-blocking sockets. Look into helper libraries like libevent. Look at how this is done in good web-servers/proxies like nginx. Sound like it's a lot for you to discover, but don't worry, it's fun :)
Since you didn't post the GetIP routine, I am not certain that your hostname lookup is correct, as from the looks of it, I am not sure that you are using inet_addr function correctly.
Nikolai has pointed out some very good points (and I fully agree). In fact you GET request is actually broken, and while I was testing it on my own local Apache web server on my system, it didn't work.
sprintf(buffer,"GET /index.html HTTP/1.1\r\n\r\n");
if (write(socket_google, (void*)buffer, LONGITUD_MSJ+1) < 0 )
return 1;
else printf("GET frame sent\n");
...
strcpy(buffer,"\n");
read(socket_google, buffer, sizeof(buffer));
should be replaced with
snprintf(buffer, sizeof(buffer),
"GET / HTTP/1.1\r\nHost: %s\r\nUser-Agent: TEST 0.1\r\n\r\n",
google);
if (write(socket_google, buffer, strlen(buffer)+1) < 0 ) {
close(socket_google);
return 1;
} else
printf("GET frame sent\n");
...
buffer[0] = '\0';
/* Read message from socket */
bytes_recv = read(socket_google, buffer, sizeof(buffer));
if (bytes_recv < 0) {
fprintf(stderr, "socket read error: %s\n", strerror(errno));
close(socket_google);
exit(10);
}
buffer[bytes_recv] = '\0'; /* NUL character */
/* strcpy(message,buffer); */
printf("%s\n", buffer);
...
You should also close the socket before exiting the program. Enable standard C89/90 or C99 mode of your compiler (e.g. -std=c99 for gcc) and enable warnings (e.g. -Wall for gcc), and read them. And #include the necessary header files (assuming Linux in my case) for function prototypes:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h> /* for gethostbyname() */
There is some casting of pointers and structs in regards to the hostname / IP address resolving, which can be confusing and easy place to make a mistake, so verify that is working as you expect it is.
in_addr_t ip;
...
GetIP(google, &ip); /* I changed the parameters */
printf("IP address = %x (%s)\n",
ip,
inet_ntoa(*((struct in_addr*)&ip)));
Actually, I've been implementing a small web proxy using my library called rzsocket link to it.
One of the most difficult things I've found when implementing the web proxy, perhaps this might also be your problem, was that, in order to make the proxy work properly, I had to set keep-alive settings false. One way of doing this in FireFox, is accessing about:config address, and setting the value of network.http.proxy.keep-alive to false.

With C TCP sockets, can 'send' return zero?

Is it ever possible for the C send function to return zero when using TCP sockets? The man page just says that it will return the number of bytes sent, but I am not sure if it will just return -1 when it can't send any data.
I'm pretty certain, though the memory is deep in the mists of time, that I've seen it return zero before, in the situation of massive data transfers where the other end was not keeping up.
From memory, in that case, the remote TCP stack buffers had filled up, the stack had notified the local end that it was to delay until some space was cleared out and the local buffers had filled up as well.
At that point, it's not technically an error (hence no -1 returned) but no data could be accepted by the local stack.
I'm not entirely certain that's the case now since the current Posix standard seems to indicate it will simply block in that case (or fail if it's set up for non-blocking).
However, I suspect it's a moot point. You do have the possibility that it will return less than the bytes you requested to send and you therefore should have code in place to handle that.
And, since it will be pretty much the same logic handling 'one less than what you requested' as handling 'zero bytes', you may as well assume it can return zero.
Well, there is always the case where you passed in zero as the number of bytes to send... in that case, "returning the number of bytes sent" would indicate that it should return zero bytes.
Probably best to handle the returns-zero case properly anyway; it can't hurt, and it might help.
The answer to this may well be implementation dependent and therefore vary based on the operating system.
One circumstance where 0 would be expected, when you request a transmission of 0 bytes.
The BSD man page states:
If no messages space is available at the socket to hold the message to be transmitted, then send() normally blocks, unless the socket has been placed in non-blocking I/O mode.
The Posix specification goes further and states that in blocking mode all data is transferred, unless an interrupt occurs.
In both cases zero cannot be returned unless the count supplied was zero.
I do observe a zero return from the send(2) on an AF_UNIX type socket right now.
Yepp, it was due to the size field of zero value.
So, JFYI.
send returns 0 when you send(socket, buf, 0, 0)
I'd like to provide simple code for others to test.
Server:
In one terminal:
nc -l localhost 10086
Client:
In another terminal:
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
int create_socket()
{
addrinfo hints = {};
addrinfo* servinfo;
int sockfd = -1;
int rv;
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = 0;
if ((rv = getaddrinfo("localhost", "10086", &hints, &servinfo)))
{
printf("getaddrinfo failed: %s\n", gai_strerror(rv));
exit(1);
}
for(auto p = servinfo; p; p = p->ai_next)
{
if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) == -1)
{
perror("socket");
continue;
}
else
{
if(connect(sockfd, p->ai_addr, p->ai_addrlen) == -1)
{
close(sockfd);
perror("connect");
continue;
}
else
puts("client: connected");
}
break;
}
freeaddrinfo(servinfo);
return sockfd;
}
void client()
{
int socket = create_socket();
if(socket == -1)
{
puts("no good");
exit(1);
}
char buf[100] = {0};
while(1){
int ret = send(socket, buf, 0, 0);
if (ret == 0){
printf(".");
}else{
printf("xxxxxxx\n");
}
}
}
int main()
{
setvbuf(stdout, NULL, _IONBF, 0);
client();
return 0;
}
g++ -g -Wall -o send_zero_bytes send_zero_bytes.cpp
./send_zero_bytes

Resources