I am new to winsock2 and networking in general but I am not new to C.
My goal is to make a program that can send and receive data from one computer to another.
Basically I want to make something like this:
Computer one initializes a transfer with computer two. Computer one does know the IP address of computer two but computer two does not know the IP address of computer one. So in other words computer two can be though of as a server and computer one as a client.
I would like for this to work without either of the users need to mess with router settings such as forwarding ports. My idea was to make something like an HTTP server. The reason for this is because pretty much all routers can view webpages which regularly send and receive data which is my goal. And just like what I want to do, the server does not know the clients IP address until the client tries to request something from the server. So with that said I realize that I should model my program off of HTTP. I decided to first write a simple program testing the programs ability to send a webpage.
#include <Winsock2.h>
#include <windows.h>
#include <stdio.h>
static const char html[]="HTTP/1.1 200 OK\r\n"
"Connection: close\r\n"
"Content-type: text/html\r\n\r\n"
"<html>\r\n"
"<head>\r\n"
"<title>Html Test</title>\r\n"
"</head>\r\n"
"<body>\r\n";
static const char htmlend[]="</body>\r\n"
"</html>\r\n\r\n";
static const char * defaultStr="Default";
int main(void){
int exit=0;
WSADATA wsa;
char buffer[512];
int bytes;
SOCKET s,client;
SOCKADDR_IN localAddress;
WSAStartup(MAKEWORD(2,2),&wsa);
while(!exit){
char * str;
s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
localAddress.sin_family = AF_INET;
localAddress.sin_port = htons(80);
localAddress.sin_addr.s_addr = INADDR_ANY;
bind(s, (SOCKADDR*)&localAddress, sizeof(localAddress));
listen(s, SOMAXCONN);
client = accept(s,NULL,NULL);
closesocket(s);
bytes = recv(client,buffer,512,0);
fputs(buffer,stdout);
str=strstr(buffer,"GET /")+5;
if(str){
char *str3;
if(*str==' '){
str=(char*)defaultStr;
}else{
char *str2=strstr(str," ");
*str2=0;
if(!strcmp(str,"Exit"))
exit=1;
}
puts(str);
str3=malloc(strlen(html)+strlen(htmlend)+strlen(str)+6);
strcpy(str3,html);
strcat(str3,str);
strcat(str3,"\r\n");
strcat(str3,htmlend);
fputs(str3,stdout);
send(client,str3,strlen(str3),0);
free(str3);
}
shutdown(client,SD_BOTH);
closesocket(client);
}
WSACleanup();
return 0;
}
The issue with the above program is that it works only on internal network. When I try to access it on the world wide web nothing happens. Am I even taking the right approach (simulating http for P2P?)? What is wrong with my program that it does not work on the world wide web? If anyone has an answer to either or both of these questions I thank you in advanced.
Related
I'm trying to port my desktop app written in C and C++ to webassembly platform and am investigating if it is possible at all. One of important things the app does is communicate by sending and receiving UDP messages. I have implemented minimal UDP client which just creates UDP socket and sends packets to server (which is build natively and is running as separate executable at the same machine). socket, bind and sendto APIs return no error and everything looks working but no messages are receiving on server side and wireshark shows no activity on that port.
Is UDP socket just stubbed at webassembly libc port, or it is implemented on top of some web standard connection (e.g. WebRTC)?
The client code is below. I checked that native build is working properly.
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#define BUFLEN 512
#define NPACK 100
#define PORT 9930
void diep(char *s)
{
perror(s);
exit(1);
}
#define SRV_IP "127.0.0.1"
int main(void)
{
struct sockaddr_in si_other;
int s, i, slen=sizeof(si_other);
char buf[BUFLEN];
if ((s=socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP))==-1)
diep("socket");
memset((char *) &si_other, 0, sizeof(si_other));
si_other.sin_family = AF_INET;
si_other.sin_port = htons(PORT);
if (inet_aton(SRV_IP, &si_other.sin_addr)==0) {
fprintf(stderr, "inet_aton() failed\n");
exit(1);
}
for (i=0; i<NPACK; i++) {
printf("Sending packet %d\n", i);
sprintf(buf, "This is packet %d\n", i);
if (sendto(s, buf, BUFLEN, 0, (struct sockaddr*)&si_other, slen)==-1)
diep("sendto()");
}
close(s);
return 0;
}
I followed instructions from http://webassembly.org/getting-started/developers-guide/ to build and run it.
Thanks in advance for any help or clues!
I found how UDP sockets are implemented at webassembly. Actually, they are emulated by websockets. It probably would work if both client and server were webassemblies, but my server is built natively. As wasm doesn't support dynamic linking, all the code (including libc implementation) is bundled to one JS file, were we can find UDP sendto implementation:
// if we're emulating a connection-less dgram socket and don't have
// a cached connection, queue the buffer to send upon connect and
// lie, saying the data was sent now.
if (sock.type === 2) {
if (!dest || dest.socket.readyState !== dest.socket.OPEN) {
// if we're not connected, open a new connection
if (!dest || dest.socket.readyState === dest.socket.CLOSING || dest.socket.readyState === dest.socket.CLOSED) {
dest = SOCKFS.websocket_sock_ops.createPeer(sock, addr, port);
}
dest.dgram_send_queue.push(data);
return length;
}
}
Anything that runs in the browser will not give you native socket access and I suspect that browser vendors would strongly object to any such access as it is a potential security violation.
Perhaps as more and more native applications move to the web as the performance difference shrinks due to webassembly and similar initiatives would make them change their stance, but until then, anything that wants direct socket control would have to remain a native app.
So I was trying to understand socket programming in C when I came across this code:
/* Sample UDP client */
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char**argv)
{
int sockfd,n;
struct sockaddr_in servaddr;
char sendline[] = "Hello UDP server! This is UDP client";
char recvline[1000];
if (argc != 2)
{
printf("usage: ./%s <IP address>\n",argv[0]);
return -1;
}
sockfd=socket(AF_INET,SOCK_DGRAM,0);
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr=inet_addr(argv[1]);
servaddr.sin_port=htons(32000);
sendto(sockfd,sendline,strlen(sendline),0,(struct sockaddr *)&servaddr,sizeof(servaddr));
n=recvfrom(sockfd,recvline,10000,0,NULL,NULL);
recvline[n]=0;
printf("Received: %s\n",recvline);
return 0;
}
It seems that the recvfrom() call does not need an ip address to send the message. A look at the man pages revealed the following lines:
If src_addr is not NULL, and the underlying protocol provides the
source address, this source address is filled in. When src_addr is
NULL, nothing is filled in; in this case, addrlen is not used,
and should also be NULL.
So I think that the underlying protocol provides the source IP address. My problem is, how does it really figure out the address to receive the message from ? Is it that, once you send a message to an address, you cannot use the same socket to send messages to other addresses ? So that it keeps on using the same address ?
Please help. Couldn't find an answer anywhere in Google or any lecture note.
Thank you in advance.
You have a misconception that recvfrom pulls data from a particular source address.
recvfrom is generally used for connectionless protocols like UDP. When an UDP packet is received, it could be from any source address. src_addr returns this address for the application usage.
If you are expecting messages only from a particular address, there are 2 ways. (1) Either you can ignore the packets received from other addresses by comparing the address returned in src_addr, or (2) use connect to specify a particular remote address from where you are expecting messages and the lower socket layer takes care of ignoring packets from other sources. After connect, you could also use recv instead of recvfrom.
Sending messages are done through sendto. You seem to be confusing the 2 calls. Using sendto it is possible to send messages to difference addresses on the same socket.
I'm having a problem with running a small block of code. This is regarding socket programming in C. What I'm trying to do is have a client communicate to a server on two different ports. But when i'm trying to compile the client code, i get 'segmentation fault'. I'm giving my client code here. Please let me know what's going wrong.
#include<stdio.h>
#include<stdbool.h>
#include<string.h>
#include<stdlib.h>
#include<time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
int main(int argc,char *argv[])
{
int sockfd,newsockfd,sockfd_infinite,sockfd_kitchen,portno,portno1,n,no_of_tables;
struct sockaddr_in serv_addr,kitchen_addr;
struct hostent *server,*kitchen;
struct timeval time_out;
time_out.tv_sec = 15; // 15 seconds
time_out.tv_usec = 0; // 0 milliseconds
// server=gethostbyname(argv[1]);
// char buffer[256];
portno=atoi(argv[2]);
portno1=atoi(argv[4]);
sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd==-1)
error("\nError creating socket");
server=gethostbyname(argv[1]);
serv_addr.sin_family=AF_INET;
serv_addr.sin_port=htons(portno);
bcopy((char *)server->h_addr,
(char *)&serv_addr.sin_addr.s_addr,
server->h_length);
sockfd_kitchen=socket(AF_INET,SOCK_STREAM,0);
if(sockfd_kitchen==-1)
error("\nError creating socket");
kitchen=gethostbyname(argv[3]);
kitchen_addr.sin_family=AF_INET;
kitchen_addr.sin_port=htons(portno1);
bcopy((char *)kitchen->h_addr,
(char *)&kitchen_addr.sin_addr.s_addr,
kitchen->h_length);
n=connect(sockfd_kitchen,(struct sockaddr *)&kitchen_addr,sizeof(kitchen_addr));
if(n==-1)
error("\nError connecting to kitchen");
n=connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(serv_addr));
if(n==-1)
error("\nError connecting to server");
return 0;
}
So the idea behind this code is - there is a client and there is a server. This client wants to communicate with the server on two different ports as if the client is talking to two different servers. I'm doing this on my laptop giving in the server names as 'localhost' for both the server names and different port numbers. Like you see in my code, I have two pointer variables for the two servers namely, *server and *kitchen.
Eg:
./thiscode localhost 15535 localhost 12345
In the example I've mentioned, this is how I'm compiling my client code. argv[1] is the first "localhost" (server name) and argv[2] is the port number of the first server. The second "localhost" and '12345' are the second server's name and port number.
Like I've said before, I'm trying to have three windows on my laptop running three different codes (one for client and two for servers). My two server codes compile without any problem but my client code gets this 'segmentation' fault. Please let me know where I'm going wrong.
Thanks.
For a start, you should be checking the return values from gethostbyname:
The gethostbyname() and gethostbyaddr() functions return the hostent structure or a NULL pointer if an error occurs. On error, the h_errno variable holds an error number.
It seems that the problem is in bcopy() function call.
You have not declared space for the variable before copying stuff into it, and hence you saw a segmentation fault.
Enter the following code before the bcopy() statement to remove the Seg-fault:
serv_addr.sin_addr.s_addr=malloc(sizeof(unsigned long));
I need to read all the HTML text from a url like http://localhost/index.html into a string in C.
I know that if i put on telnet -> telnet www.google.com 80 Get webpage.... it returns all the html.
How do I do this in a linux environment with C?
I would suggest using a couple of libraries, which are commonly available on most Linux distributions:
libcurl and libxml2
libcurl provides a comprehensive suite of http features, and libxml2 provides a module for parsing html, called HTMLParser
Hope that points you in the right direction
Below is a rough outline of code (i.e. not much error checking and I haven't tried to compile it) to get your started, but use http://www.tenouk.com/cnlinuxsockettutorials.html to learn socket programming. Lookup gethostbyname if you need to translate a hostname (like google.com) into an IP address. Also you may need to do some work to parse out the content length from the HTTP response and then make sure you keep calling recv until you've gotten all the bytes.
#include <netinet/in.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <string.h>
#include <stdlib.h>
void getWebpage(char *buffer, int bufsize, char *ipaddress)
{
int sockfd;
struct sockaddr_in destAddr;
if((sockfd = socket(PF_INET, SOCK_STREAM, 0)) == -1){
fprintf(stderr, "Error opening client socket\n");
close(sockfd);
return;
}
destAddr.sin_family = PF_INET;
destAddr.sin_port = htons(80); // HTTP port is 80
destAddr.sin_addr.s_addr = inet_addr(ipaddress); // Get int representation of IP
memset(&(destAddr.sin_zero), 0, 8);
if(connect(sockfd, (struct sockaddr *)&destAddr, sizeof(struct sockaddr)) == -1){
fprintf(stderr, "Error with client connecting to server\n");
close(sockfd);
return;
}
// Send http request
char *httprequest = "GET / HTTP/1.0";
send(sockfd, httprequest, strlen(httprequest), 0);
recv(sockfd, buffer, bufsize, 0);
// Now buffer has the HTTP response which includes the webpage. You can either
// trim off the HTTP header, or just leave it in depending on what you are doing
// with the page
}
if you really don't feel like messing around with sockets, you could always create a named temp file, fork off a process and execvp() it to run wget -0 , and then read the input from that temp file.
although this would be a pretty lame and inefficient way to do things, it would mean you wouldn't have to mess with TCP and sending HTTP requests.
You use sockets, interrogate the web server with HTTP (where you have "http://localhost/index.html") and then parse the data which you have received.
Helpful if you are a beginner in socket programming: http://beej.us/guide/bgnet/
Assuming you know how to read a file into a string, I'd try
const char *url_contents(const char *url) {
// create w3m command and pass it to popen()
int bufsize = strlen(url) + 100;
char *buf = malloc(bufsize);
snprintf(buf, bufsize, "w3m -dump_source '%s'");
// get a file handle, read all the html from it, close, and return
FILE *html = popen(buf, "r");
const char *s = read_file_into_string(html); // you write this function
fclose(html);
return s;
}
You fork a process, but it's a lot easier to let w3m do the heavy lifting.
I have written an HTTP proxy that does some stuff that's not relevant here, but it is increasing the client's time-to-serve by a huge amount (600us without proxy vs 60000us with it). I think I have found where the bulk of that time is coming from - between my proxy finishing sending back to the client and the client finishing receiving it. For now, server, proxy and client are running on the same host, using localhost as the addresses.
Once the proxy has finished sending (once it has returned from send() at least), I print the result of gettimeofday which gives an absolute time. When my client has received, it prints the result of gettimeofday. Since they're both on the same host, this should be accurate. All send() calls are with no flags, so they are blocking. The difference between the two is about 40000us.
The proxy's socket on which it listens for client connections is set up with the hints AF_UNSPEC, SOCK_STREAM and AI_PASSIVE. Presumably a socket from accept()ing on that will have the same parameters?
If I'm understanding all this correctly, Apache manages to do everything in 600us (including the equivalent of whatever is causing this 40000us delay). Can anybody suggest what might be causing this? I have tried setting the TCP_NODELAY option (I know I shouldn't, it's just to see if it made a difference) and the delay between finishing sending and finishing receiving went right down, I forget the number but <1000us.
This is all on Ubuntu Linux 2.6.31-19. Thanks for any help
40ms is the TCP ACK delay on Linux, which indicates that you are likely encountering a bad interaction between delayed acks and the Nagle algorithm. The best way to address this is to send all of your data using a single call to send() or sendmsg(), before waiting for a response. If that is not possible then certain TCP socket options including TCP_QUICKACK (on the receiving side), TCP_CORK (sending side), and TCP_NODELAY (sending side) can help, but can also hurt if used improperly. TCP_NODELAY simply disables the Nagle algorithm and is a one-time setting on the socket, whereas the other two must be set at the appropriate times during the life of the connection and can therefore be trickier to use.
You can't really do meaningful performance measurements on a proxy with the client, proxy and origin server on the same host.
Place them all on different hosts on a network. Use real hardware machines for them all, or specialised hardware test systems (e.g. Spirent).
Your methodology makes no sense. Nobody has 600us of latency to their origin server in practice anyway. Running all the tasks on the same host creates contention and a wholly unreaslistic network environment.
INTRODUCTION:
I already praised mark4o for the truly correct answer to the general question of lowering latency. I would like to translate the answer in terms of how it helped solve my latency issue because I think it's going to be the answer most people come here looking for.
ANSWER:
In a real-time network app (such as a multiplayer game) where getting short messages between nodes as quickly as possible is critical, TURN NAGLE OFF. In most cases this means setting the "no-delay" flag to true.
DISCLAIMER:
While this may not solve the OP specific problem, most people who come here will probably be looking for this answer to the general question of their latency issues.
ANECDOTAL BACK-STORY:
My game was doing fine until I added code to send two messages separately, but they were very close to each other in execution time. Suddenly, I was getting 250ms extra latency. As this was a part of a larger code change, I spent two days trying to figure out what my problem was. When I combined the two messages into one, the problem went away. Logic led me to mark4o's post and so I set the .Net socket member "NoDelay" to true, and I can send as many messages in a row as I want.
From e.g. the RedHat documentation:
Applications that require lower latency on every packet sent should be run on sockets with TCP_NODELAY enabled. It can be enabled through the setsockopt command with the sockets API:
int one = 1;
setsockopt(descriptor, SOL_TCP, TCP_NODELAY, &one, sizeof(one));
For this to be used effectively, applications must avoid doing small, logically related buffer writes. Because TCP_NODELAY is enabled, these small writes will make TCP send these multiple buffers as individual packets, which can result in poor overall performance.
In your case, that 40ms is probably just a scheduler time quantum. In other words, that's how long it takes your system to get back round to the other tasks. Try it on a real network, you'll get a completely different picture. If you have a multi-core machine, using virtual OS instances in Virtualbox or some other VM would give you a much better idea of what is really going to happen.
For a TCP proxy it would seem prudent on the LAN side to increase the TCP initial window size as discussed on linux-netdev and /. recently.
http://www.amailbox.org/mailarchive/linux-netdev/2010/5/26/6278007
http://developers.slashdot.org/story/10/11/26/1729218/Google-Microsoft-Cheat-On-Slow-Start-mdash-Should-You
Including paper on the topic by Google,
http://www.google.com/research/pubs/pub36640.html
And an IETF draft also by Google,
http://zinfandel.levkowetz.com/html/draft-ietf-tcpm-initcwnd-00
For Windows, I'm not sure if setting TCP_NODELAY helps. I tried that, but latency was still bad. One person suggested I try UDP, and that did the trick.
A few complicated examples of UDP did not work for me, but I ran across a simple one and it did the trick...
#include <Winsock2.h>
#include <WS2tcpip.h>
#include <system_error>
#include <string>
#include <iostream>
class WSASession
{
public:
WSASession()
{
int ret = WSAStartup(MAKEWORD(2, 2), &data);
if (ret != 0)
throw std::system_error(WSAGetLastError(), std::system_category(), "WSAStartup Failed");
}
~WSASession()
{
WSACleanup();
}
private:
WSAData data;
};
class UDPSocket
{
public:
UDPSocket()
{
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock == INVALID_SOCKET)
throw std::system_error(WSAGetLastError(), std::system_category(), "Error opening socket");
}
~UDPSocket()
{
closesocket(sock);
}
void SendTo(const std::string& address, unsigned short port, const char* buffer, int len, int flags = 0)
{
sockaddr_in add;
add.sin_family = AF_INET;
add.sin_addr.s_addr = inet_addr(address.c_str());
add.sin_port = htons(port);
int ret = sendto(sock, buffer, len, flags, reinterpret_cast<SOCKADDR *>(&add), sizeof(add));
if (ret < 0)
throw std::system_error(WSAGetLastError(), std::system_category(), "sendto failed");
}
void SendTo(sockaddr_in& address, const char* buffer, int len, int flags = 0)
{
int ret = sendto(sock, buffer, len, flags, reinterpret_cast<SOCKADDR *>(&address), sizeof(address));
if (ret < 0)
throw std::system_error(WSAGetLastError(), std::system_category(), "sendto failed");
}
sockaddr_in RecvFrom(char* buffer, int len, int flags = 0)
{
sockaddr_in from;
int size = sizeof(from);
int ret = recvfrom(sock, buffer, len, flags, reinterpret_cast<SOCKADDR *>(&from), &size);
if (ret < 0)
throw std::system_error(WSAGetLastError(), std::system_category(), "recvfrom failed");
// make the buffer zero terminated
buffer[ret] = 0;
return from;
}
void Bind(unsigned short port)
{
sockaddr_in add;
add.sin_family = AF_INET;
add.sin_addr.s_addr = htonl(INADDR_ANY);
add.sin_port = htons(port);
int ret = bind(sock, reinterpret_cast<SOCKADDR *>(&add), sizeof(add));
if (ret < 0)
throw std::system_error(WSAGetLastError(), std::system_category(), "Bind failed");
}
private:
SOCKET sock;
};
Server
#define TRANSACTION_SIZE 8
static void startService(int portNumber)
{
try
{
WSASession Session;
UDPSocket Socket;
char tmpBuffer[TRANSACTION_SIZE];
INPUT input;
input.type = INPUT_MOUSE;
input.mi.mouseData=0;
input.mi.dwFlags = MOUSEEVENTF_MOVE;
Socket.Bind(portNumber);
while (1)
{
sockaddr_in add = Socket.RecvFrom(tmpBuffer, sizeof(tmpBuffer));
...do something with tmpBuffer...
Socket.SendTo(add, data, len);
}
}
catch (std::system_error& e)
{
std::cout << e.what();
}
Client
char *targetIP = "192.168.1.xxx";
Socket.SendTo(targetIP, targetPort, data, len);