I have a project that uses TCP sockets to communicate between a server and one client. As of now I have been doing this on one computer so I have just used local address of "127.0.0.1" for the address to bind and connect to on both sides and its worked fine. Now I have a second computer to act as a client, but I don't know how to change the addresses accordingly. They are connected through a network that is not connected to the Internet. Before the code looked like this -
Server -
struct addrinfo hints;
struct addrinfo *servinfo; //will point to the results
//store the connecting address and size
struct sockaddr_storage their_addr;
socklen_t their_addr_size;
memset(&hints, 0, sizeof hints); //make sure the struct is empty
hints.ai_family = AF_INET; //local address
hints.ai_socktype = SOCK_STREAM; //tcp
hints.ai_flags = AI_PASSIVE; //use local-host address
//get server info, put into servinfo
if ((status = getaddrinfo("127.0.0.1", port, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
return false;
}
//make socket
fd = socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol);
if (fd < 0) {
printf("\nserver socket failure %m", errno);
return false;
}
//allow reuse of port
int yes=1;
if (setsockopt(fd,SOL_SOCKET,SO_REUSEADDR,(char*) &yes,sizeof(int)) == -1) {
perror("setsockopt");
return false;
}
//unlink and bind
unlink("127.0.0.1");
if(bind (fd, servinfo->ai_addr, servinfo->ai_addrlen) < 0) {
printf("\nBind error %m", errno);
return false;
}
Client -
struct addrinfo hints;
struct addrinfo *servinfo; //will point to the results
memset(&hints, 0, sizeof hints); //make sure the struct is empty
hints.ai_family = AF_INET; //local address
hints.ai_socktype = SOCK_STREAM; //tcp
hints.ai_flags = AI_PASSIVE; //use local-host address
//get server info, put into servinfo
if ((status = getaddrinfo("127.0.0.1", port, &hints, &servinfo)) != 0) {
fprintf(stderr, "getaddrinfo error: %s\n", gai_strerror(status));
return false;
}
//make socket
fd = socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol);
if (fd < 0) {
printf("\nserver socket failure %m", errno);
return false;
}
//connect
if(connect(fd, servinfo->ai_addr, servinfo->ai_addrlen) < 0) {
printf("\nclient connection failure %m", errno);
return false;
}
I know it should be simple, but I can't figure out how to change the IPs to get them to work. I tried setting the server computer's IP address in the quotes in these lines -
if ((status = getaddrinfo("127.0.0.1", port, &hints, &servinfo)) != 0)
and
unlink("127.0.0.1");
and then change the address in the client code to the client computer's IP address in this line -
if ((status = getaddrinfo("127.0.0.1", port, &hints, &servinfo)) != 0)
Whenever I do that, it tells me connection refused. I have also tried doing the opposite way of putting the server's address in the client's line and client's address in the server's lines along with a few other attempts. At this point I feel like I am just guessing though. So can someone please help me understand how to change this from using the local address with one computer to connecting two computers? Any help is appreciated.
First, unlink("127.0.0.1"); is totally wrong here, don't do that.
Then, you have two computers connected by some network. Both should have IP addresses. Replace 127.0.0.1 with the server's IP address in both client and the server. The server does not to have to know client's address beforehand - it'll get that information from the accept(2) call. The client needs server's address to know where to connect. The server needs its own address for the bind(2) call.
The main problem is that your putting AI_PASSIVE in your client code. AI_PASSIVE is meant for servers only (that's what it signals).
Also on the server side you should first of all not call unlink. That's for AF_UNIX sockets only, not AF_INET. Secondly you don't need to put "127.0.0.1" in the getaddrinfo line on the server side. It's better to use NULL to bind to all available addresses.
If you change those things, I believe your code should work. However you're actually supposed to loop on the getaddrinfo result using the ai_next pointer and try to connect to each result, using the first that succeeds.
Connection Refused usually means your client received a RST to his SYN. This is most often caused by the lack of a listening socket on the server, on the port you're trying to connect to.
Run your server
On the CLI, type netstat -ant. Do you see an entry that's in LISTEN state on your port?
Something like:
tcp4 0 0 *.3689 *.* LISTEN
I bet you do not, and therefore have a problem with your server listening socket. I also bet the changes you made this this line:
if ((status = getaddrinfo("127.0.0.1", port, &hints, &servinfo)) != 0) {
Weren't quite right. Try changing that IP to 0.0.0.0 on the server to tell it to to bind to any IP on the system. On the client, that line should have the IP address of the server. You should also remove the unlink() call in the server; unnecessary.
If you do have a listening socket, then there's probably a firewall or something in between your boxes that's blocking the SYN. Try typing service iptables stop on the CLI of both systems.
Related
I've created a TCP server application using BSD sockets and NUCLEO-H743ZI2 development board with STM32CubeMX 5.6.0 & LwIP 2.0.3 in Keil-MDKARM.
I noticed that:
If a client connects and sends 11 bytes or more at first, server
receives the data correctly and read() responds displaying the data.
However, if client sends the first data lower than 11
bytes, read() function blocks even next received data is higher than 11 bytes, until client disconnects. After the disconnection, all the data queued is displayed.
Namely, if first data sent from a client to my server is lower than 11 bytes, event_callback for a rcvevent is not triggered until disconnection.
My aim is to make the server available to one byte reception.
I've pasted my Server task/thread below. Let me have your kind response at your earliest convenience and feel free to request other related files/libraries(lwip.h, lwipopts.h..).
Kind Regards
void StartTask01(void const * argument)
{
/* USER CODE BEGIN StartTask01 */
MX_LWIP_Init();
/*start a listening tcp server*/
int iServerSocket;
struct sockaddr_in address;
if ((iServerSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0)
{
printf("Socket could not be created\n");
}
else
{
address.sin_family = AF_INET;
address.sin_port = htons(80);
address.sin_addr.s_addr = INADDR_ANY;
if (bind(iServerSocket, (struct sockaddr *)&address, sizeof (address)) < 0)
{
printf("socket could not be bound\n");
}
else
{
listen(iServerSocket, MEMP_NUM_NETCONN);
}
}
/*server started listening*/
struct sockaddr_in remoteHost;
int newconn;
char caReadBuffer[1500];
memset(caReadBuffer, 0, 1500);
for(;;)
{
/*block until accepting an incoming connection*/
newconn = accept(iServerSocket, (struct sockaddr *)&remoteHost, (socklen_t *)(sizeof(remoteHost)));
if (newconn != -1)/*if accepted well*/
{
/*block until data arrives*/
read(newconn, caReadBuffer, sizeof(caReadBuffer));
printf("data read: %s\n", caReadBuffer);
memset(caReadBuffer, 0, 1500);
}
}
/* USER CODE END StartTask01 */
}
The problem that's causing this issue is that you only call read once on each connection. If you don't happen to receive all the data from that single call to read (which is entirely unpredictable), you will never call read on that connection again.
When you call read on a blocking TCP connection, it will only block if there is no data available. Otherwise, it will give you whatever data is available up to the maximum number of bytes you ask for. It will not wait for more data if only some is available. It's up to you to call read again if you didn't receive all the data you expected.
One your second iteration of the for loop, you overwrite newconn with a new connection. You don't close the old connection. So you have a socket leak.
SOLVED:
The problem is, my server was listening port 80. I changed it to port 7 and thankfully bug is resolved, now read() works as expected.
This bug let me think that LwIP had problems on listening that web(80) port instead of others. There should be a some kind of discrimination between listening some spectacular ports even for unimplemented protocols.
This question already has an answer here:
How to check if network address is local in Qt
(1 answer)
Closed 6 years ago.
My application connects to a tcp server. I'd like it to be aware of being running on the same host as the server app, so it can eventually directly lauch the server process if it's not up.
As the server listens on an interface and the application resolves a hostname to connect to the server, it's not so obvious for me to determine if the configured hostname used to connect the server points to the same host as the server or not.
I'd like something like this:
bool isThisLocalHost(QString hostName) {
//resolve hostname's address
//list localhost interfaces ip or hw addresses ?
//if the hostname address matches one of the host interfaces address
//pseudo code
bool bRes = interfaces_addresses_list.contains(hostname_address);
return bRes;
}
I'm actually trying to achieve this with
QNetworkInterface, QNetworkAddressEntry, QHostInfo, QHostAddress.
Maybe is there a simple way?
Here is what i got:
bool isThisLocalHost(QString hostName) {
QList <QHostAddress> lAddrHostName = QHostInfo::fromName(hostName).addresses();
QList <QHostAddress> lAddrLocalHostInterfaces = QNetworkInterface::allAddresses();
bool bRes = false;
foreach (QHostAddress addr, lAddrHostName) {
bRes = bRes || lAddrLocalHostInterfaces.contains(addr);
}
return bRes;
}
QHostAddress has isLoopback() which should get you what you need.
If you just want to know if you're connected to yourself this is (partly?) a duplicate of this question.
I apologize, I was not sure how to deliver a concise title for this issue.
Background: I am using QTcpSocket to connect to a PLC Simulator application. Previously I was using PLCQTLIB to connect to the simulator and everything was working fine. The library did not offer enough functionality for my project so I have created my own library for interfacing Qt with the libnodave library.
The simulator runs on IP Address 192.168.32.1 and Port 102
Current Issue: I enter the IP (192.168.32.1) and Port (102) and press connect. I receive:
TCP Error = QAbstractSocket::ConnectionRefusedError
TCP Error = QAbstractSocket::SocketTimeoutError
If I change the port to 80 and press connect, the connection is successful. However the connection to the PLC Simulator will fail because it is not listening on Port 80.
Now that a successful connection has been established to 192.168.32.1 and the current state of the connection is disconnected, I can enter the correct port 102 and connect successfully.
Question: Why does the TCP Socket not establish a connection to Port 102 until after a connection has been previously opened on Port 80? No firewalls exist and all communication is occurring on the local machine.
Declared in Header:
QTcpSocket *tcp;
Source File:
PLCLibNoDave::PLCLibNoDave()
{
tcp = new QTcpSocket();
connect(tcp, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
this, SLOT(tcpStateChanged(QAbstractSocket::SocketState)));
connect(tcp, SIGNAL(error(QAbstractSocket::SocketError)),
this, SLOT(tcpErrorHandler(QAbstractSocket::SocketError)));
connect(tcp, SIGNAL(hostFound()), this, SLOT(tcpHostFound()));
connect(tcp, SIGNAL(connected()), this, SLOT(tcpConnected()));
connect(tcp, SIGNAL(disconnected()), this, SLOT(tcpDisconnected()));
}
void PLCLibNoDave::connectTCP(int port, QString ip)
{
tcp->connectToHost(ip,port);
if(!tcp->waitForConnected(3000)){
tcp->disconnectFromHost();
return;
}
tcpHandle = tcp->socketDescriptor();
if(tcpHandle == -1){
tcp->disconnectFromHost();
qDebug() << "Invalid Socket Descriptor on Connect";
return;
}
return;
}
void PLCLibNoDave::disconnectTCP()
{
tcp->disconnectFromHost();
if(tcp->state() == QAbstractSocket::UnconnectedState ||
tcp->waitForDisconnected(1000)){
tcpError = tcp->error();
}
else{
tcpError = tcp->error();
qDebug() << "Disconnect Failed: " << tcp->errorString();
}
return;
}
I am trying to access a PHP file in my server with an Arduino and the Ethernet shield. This file captures the URL parameters "Sensor" and "Value" and stores the read data into a database.
This is my code:
#include <SPI.h>
#include <Ethernet.h>
EthernetClient client;
byte MACaddress[] = {0xDE,0xAD,0xBE,0xEF,0xFE,0xED};
byte IPaddress[] = {10,0,0,178};
byte DNSserverIPaddress[] = {4,4,4,4};
byte gatewayIPaddress[] = { 10, 0, 0, 100 };
byte subnetMask[] = { 255, 255, 255, 0 };
char serverName[] = "log.server.com";
void setup() {
Serial.begin(9600);
Ethernet.begin(MACaddress, IPaddress, DNSserverIPaddress, gatewayIPaddress, subnetMask);
}
void loop()
{
delay (5000);
Serial.println("connecting to server...");
client.connect(serverName, 80);
Serial.println("making HTTP request...");
client.println("GET /logger.php?sensor=temp&value=19 HTTP/1.1");
client.println("HOST: log.server.com");
client.println();
}
After uploading this code to my Arduino Mega + Ethernet shield, nothing changes in my database...
What is wrong?
Well, first you should add a condition check to know if the Arduino think it worked or not:
if (client.connect(...)) {
/* Stuff you do on success */
}
else {
Serial.println("failure! :-(")
}
If it does print failure! you got a network configuration problem on the Arduino side. If it does work, the problem is after the Arduino.
Then try opening a server with nc -kl 42000 and change the port you connect to on 42000 in the Arduino sketch, to be sure whether the network connection works.
If it does work, then you've got a problem on your host side (the webserver), if it does not, you may have a problem on the network between the Arduino and the host.
You should then try to connect to a server's IP address instead of a fully qualified domain name (FQDN). If that works, it may be the DNS server that is unreachable, and you should try to use 8.8.8.8 instead (or your local network's DNS server).
Also check that the IP address you're using is indeed free of use (and not assigned by a DHCP or used by another computer), as well as the MAC address is really unused... (addresses like {0xDE,0xAD,0xBE,0xEF,0xFE,0xED} tend to be used a lot in hacks...).
My opinion, is that your bug is the DNS server that is unreachable, as there's no DNS resolver opened on 4.4.4.4.
I am working on Ubuntu 9.04. I am running this on VMware workstation. Here is my C code:
int sockfd,cnt,addrlen;
const int on = 1;
struct sockaddr_in servaddr,cliaddr;
char reply[512];
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
if (sockfd < 0) {
perror("socket");
exit(1);
}
setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR, &on,sizeof(on));
bzero(&cliaddr, sizeof(cliaddr));
cliaddr.sin_family = AF_INET;
cliaddr.sin_addr.s_addr = htonl(INADDR_ANY);
cliaddr.sin_port = htons(68);
addrlen = sizeof(servaddr);
if (bind(sockfd, (struct sockaddr *) &cliaddr, sizeof(cliaddr)) < 0) {
perror("bind");
exit(1);
}
while(1)
{
cnt = recvfrom(sockfd, reply, sizeof(reply), 0,(struct sockaddr *) &servaddr, &addrlen);
if (cnt < 0) {
perror("recvfrom");
exit(1);
}
printf("\nReply Received\n");
}
I run this program in one terminal and run 'dhclient' on another. I receive no datagrams. What am I doing wrong?
Looks like you're listening on UDP port 68 for a broadcasted message from the client? If I'm reading DHCP correctly, the client will send its broadcase 'discover' request FROM UDP port 68, but TO UDP port 67 on the server, so you would need to be listening on port 67 to receive it.
An easy 'first' test to test you're code before trying it with dhclient would be to try talking to your server with netcat. a command line like
echo "Foo" | netcat -u localhost 68
Should cause a packet to be received by your current code.
Another good debugging tool is wireshark which will let you see exactly what UDP packets are being sent by dhclient and what they contain.
I'm not sure what you're doing wrong but if I were you I'd write my own client which is very simple and see if it can talk to your server code above (who knows what dhclient might do outside of contact your code). I'd also temporarily change the port number to something not well-known. This way I wouldn't be interfering with any other programs and interfaces.
I recommend this tutorial. Also, are you running as root? You can't get that low-numbered port otherwise.