Why does read() block indefinitely when reading a buffer - c++

I'm new to socket programming and wanted to try something simple. This program can manipulate settings on my tv. All messages are 24 bytes. There may be one or more messages returned. I cannot figure out a good solution to get all of the messages without read() blocking on me.
What is below would be what I hoped to be a simple solution. It seems to work in a lot of example code I have found. However, what happens is after the first loop it seems to just block on the read() operation infinitely. If I remove the loop and just put multiple reads, the same thing happens. As long as I don't try to read more information that is sent, I'm ok.
I did try a couple of other things like turning off blocking, and adding a timer. neither worked. At this point I can live with a couple seconds of blocking. I just want the program to exit normally after the read.
adding output for a power_on command. It correctly outputs the two lines it should then blocks indefinitely.
Dans-MBP:~ mreff555$ ./tvthing
24: *SAPOWR0000000000000000
24: *SNPOWR0000000000000001
code below:
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <cstring>
#include <sys/time.h>
#define PORT 20060
#define POWER_ON "*SCPOWR0000000000000001\n"
#define POWER_OFF "*SCPOWR0000000000000000\n"
#define POWER_STATUS "*SEPOWR################\n"
#define POWER_TOGGLE "*STPOWR################\n"
int main(int argc, char const * argv[])
{
struct sockaddr_in tvAddress;
struct hostent *host = gethostbyname("192.168.1.128");
memset(&tvAddress, 0, sizeof(tvAddress));
tvAddress.sin_family = AF_INET;
tvAddress.sin_addr.s_addr = htonl(INADDR_ANY);
tvAddress.sin_addr.s_addr = ((struct in_addr*)(host->h_addr))->s_addr;
tvAddress.sin_port = htons(PORT);
char sendBuffer[24] = {0};
char recBuffer[24] = {0};
int socket_fd;
if((socket_fd = socket(AF_INET,SOCK_STREAM, 0)) < 0)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
else
{
if(connect(socket_fd, (struct sockaddr *)&tvAddress, sizeof(struct sockaddr)))
{
perror("connection failed failed");
exit(EXIT_FAILURE);
}
memcpy(&sendBuffer, &POWER_STATUS, sizeof(sendBuffer));
write(socket_fd, sendBuffer, strlen(sendBuffer));
int ret;
while((ret = read(socket_fd, recBuffer, sizeof(recBuffer)) > 0))
{
printf("%d: %s\n", ret, recBuffer);
}
close(socket_fd);
}
}

You need to read until your buffer is full like this:
unsigned readLen = 0;
unsigned totalLen = sizeof(recBuffer);
while (readLen < totalLen) {
int ret = read(socket_fd, recBuffer + readLen, totalLen - readLen);
if (ret > 0) {
readLen += ret;
} else {
// error handling here
break;
}
}
This is needed because read() returns only the currently available amount of bytes which might be less than you have requested. From the corresponding man-page:
RETURN VALUE
On success, the number of bytes read is returned (zero indicates end of file), and the file position is advanced by this number. It is not an error if this number is smaller than the number of bytes requested; this may happen for example because fewer bytes are actually available right now (maybe because we were close to end-of-file, or because we are reading from a pipe, or from a terminal), or because read() was interrupted by a signal.
If you need to receive several responses you can put the described algorithm into a function and use it repeatedly. In any case you need to know how many responses to expect otherwise your read() will block because it seems that your TV's server is programmed to keep the connection open and it is client's responsibility to choose when to disconnect.
If you decide to make your application more sophisticated you can use one of the IO Multiplexing mechanisms to make your wait for response interruptable by timer or terminal input. For example:
while (true) {
pollfd fds[] = {
{ socket_fd, POLLIN, 0 },
{ STDIN_FILENO, POLLIN, 0 }
};
int ret = poll(fds, sizeof(fds) / sizeof(*fds), -1);
if (ret > 0) {
if (fds[0].revents & POLLIN) {
readResponse(); // read and process response
}
if (fds[1].revents & POLLIN) {
break; // exit on terminal input
}
}
}

As it turns out, select is designed exactly for that purpose. It checks the specified file descriptors for a specified time interval, and if successful repeats the process. Tweaking the time interval minimizes the blocking while allowing enough time for additional messages to come in.
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <cstring>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/select.h>
#define PORT 20060
#define POWER_ON "*SCPOWR0000000000000001\n"
#define POWER_OFF "*SCPOWR0000000000000000\n"
#define POWER_STATUS "*SEPOWR################\n"
#define POWER_TOGGLE "*STPOWR################\n"
int main(int argc, char const * argv[])
{
struct sockaddr_in tvAddress;
struct hostent *host = gethostbyname("192.168.1.128");
memset(&tvAddress, 0, sizeof(tvAddress));
tvAddress.sin_family = AF_INET;
tvAddress.sin_addr.s_addr = htonl(INADDR_ANY);
tvAddress.sin_addr.s_addr = ((struct in_addr*)(host->h_addr))->s_addr;
tvAddress.sin_port = htons(PORT);
char sendBuffer[24] = {0};
char recBuffer[24] = {0};
int socket_fd;
if((socket_fd = socket(AF_INET,SOCK_STREAM, 0)) < 0)
{
perror("socket failed");
exit(EXIT_FAILURE);
}
else
{
if(connect(socket_fd, (struct sockaddr *)&tvAddress, sizeof(struct sockaddr)))
{
perror("connection failed failed");
exit(EXIT_FAILURE);
}
struct timeval tv;
fd_set sockRead;
int selectStatus;
memcpy(&sendBuffer, &POWER_ON, sizeof(sendBuffer));
write(socket_fd, sendBuffer, strlen(sendBuffer));
do
{
FD_ZERO(&sockRead);
FD_SET(socket_fd, &sockRead);
tv.tv_sec = 2;
tv.tv_usec = 500000;
selectStatus = select(socket_fd + 1, &sockRead, NULL, NULL, &tv);
switch(selectStatus)
{
case -1:
perror("select()");
exit(EXIT_FAILURE);
break;
case 0:
break;
default:
printf("Ready for Reading\n");
read(socket_fd, recBuffer, sizeof(recBuffer));
printf("%s\n", recBuffer);
}
}while (selectStatus > 0);
close(socket_fd);
}
}

Related

TCP "connecttion refused" error (c++ CLion)

I create a TCP server, I always get following error when I was run. How I get rid of this error, I do not understand where wrong?
Also it say data received while connection refused, I do not run client. Why?
Error:
Socket Created
Error connect to server: Connection refused
Error to accept: Invalid argument
Data received
Server
#include <iostream>
#include "serverh.hpp"
#include "glimpsepackages.h"
#include "soupbintcppackages.h"
int main() {
//soupBinTCP structures
DebugPacket D; //"+"
LoginAcceptedPacket A;
LoginRejectPacket J;
SequencedDataPacket S;
ServerHeartBeatPacket H;
EndOfSessionPacket Z;
//glimpse structures
SecondsMessage T;
CombinationOrderBookDelivery M;
TickSizeTableEntryPackage L;
OrderBookDirectoryPackage R;
OrderBookStatePackage O;
AddOrderMessageNoMPIDPackage A1;
AddOrderMessageMPIDPackage F;
std::string ip="127.0.0.1";
int port=7888;
ssize_t size=1024;
void *data={};
TcpServer server(ip, port);
server.connectToClient();
server.accept();
for (;;) {
server.receivingData(size);
switch (server.pop()) {
case '+':
// D.length=server.pop();
D.code = server.pop8();
D.text = server.pop();
break;
case 'A' :
A.to_little_endian();
A.code = server.pop8();
A.sequence_number = server.pop32();
for (int i = 0; i < 10; i++) {
A.session[i] = server.pop();
}
break;
case 'J':
J.code = server.pop8();
J.rejectreasoncode = server.pop();
break;
case 'S':
S.code = server.pop8();
S.message = server.pop();
break;
case 'H':
H.code = server.pop8();
case 'Z':
Z.code = server.pop8();
default:
return 0;
}
}
serverh.cpp
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <arpa/inet.h>
#include <string>
#include "serverh.hpp"
#include "util.h"
#include <iostream>
TcpServer::TcpServer(std::string ip, int port):_ip(ip),
_port(port), _addrSize(sizeof _serverAddress)
{
_sockFd=-1;
};
bool TcpServer::connectToClient(){
_sockFd=socket(AF_INET,SOCK_STREAM,0);
if(_sockFd==-1)
perror("Error creating socket");
else
std::cerr<<"Socket Created\n";
_serverAddress.sin_addr.s_addr=inet_addr(_ip.c_str());
_serverAddress.sin_family=AF_INET;
_serverAddress.sin_port=htons(_port);
if(::connect(_sockFd,(struct sockaddr *)&_serverAddress, sizeof(_serverAddress))<0) {
perror("Error connect to server");
return false;
}
else
std::cerr<<"Connected\n";
if(::bind(_sockFd,(struct sockaddr *)&_serverAddress,_addrSize)<0)
perror("Error bind to server");
else
std::cerr<<"Socket bind\n";
if(::listen(_sockFd,5)==0)
std::cerr<<"Listining\n";
else
perror("Error on Listening");
return true;
}
bool TcpServer::accept(){
if(::accept(_sockFd,(struct sockaddr *) &_serverAddress, &_addrSize)<0)
perror("Error to accept");
else
std::cerr<<"Accepted\n";
return true;
}
void *TcpServer::receivingData(ssize_t size){
char *buffer=new char[size];
if(::recv(_sockFd,buffer, size,0)>0);
std::cerr<<"Data received\n";
return buffer;
}
void TcpServer::incOffset(int rest_length) {
void *restbuff = alloca(rest_length);
if (::recv(_sockFd, restbuff, rest_length, 0) < 0)
std::cerr << "Data received\n";
}
char TcpServer::pop(){
char ch;
if(::recv(_sockFd, &ch, 1, 0)>0)
return ch;
}
int8_t TcpServer::pop8() {
int8_t ch;
if (::recv(_sockFd, &ch, 8, 0) > 0)
return getLeValue(ch); // func include endian functions
}
int16_t TcpServer::pop16(){
int16_t ch;
if(::recv(_sockFd, &ch, 16, 0)>0)
return getLeValue(ch);
}
int32_t TcpServer::pop32(){
int32_t ch;
if(::recv(_sockFd, &ch, 32, 0)>0)
return getLeValue(ch);
}
int64_t TcpServer::pop64() {
int64_t ch;
if (::recv(_sockFd, &ch, 64, 0) > 0)
return getLeValue(ch);
}
Serverh.hpp
#ifndef SERVERH_H
#define SERVERH_H
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <string>
class TcpServer{
public:
TcpServer(const std::string _ip,int _port);
bool connectToClient();
bool accept();
void *receivingData(ssize_t size);
bool sendToClient();
char pop();
int8_t pop8();
int16_t pop16();
int32_t pop32();
int64_t pop64();
void incOffset(int rest_length);
private:
std::string _ip;
int _port;
int _sockFd;
sockaddr_in _serverAddress;
socklen_t _addrSize;
};
#endif
The problem
Error: Socket Created Error connect to server: Connection refused Error to accept: Invalid argument Data received
Two errors here, a failure to connect because the other address is not accepting connections and a failure to accept because one or more of the input parameters are bad.
The failure to connect is understandable. Clients are rarely listening on a port for connections. Clients usually initiate the connection.
The failure to accept is caused by connectToClient exiting after failing to connect. It never binds and listens, so the socket has not been set up for accept.
Fixing this
A server should not attempt to connect. It should listen for attempts to connect to it. This makes connectToClient bizarre. It tries to connect to a client and then, on the same socket, bind and listen. You can't do both, so I'd discard
if(::connect(_sockFd,(struct sockaddr *)&_serverAddress, sizeof(_serverAddress))<0) {
perror("Error connect to server");
return false;
}
else
std::cerr<<"Connected\n";
and
reconsider
_serverAddress.sin_addr.s_addr=inet_addr(_ip.c_str());
as this will only listen to one incoming address which might be what you want, but isn't usually. I would also rename the function to reflect the change in purpose.
Note:
if(::accept(_sockFd,(struct sockaddr *) &_serverAddress, &_addrSize)<0)
passes in a pointer to the server address. This location will be overwritten with the address of the client. It's harmless, but you probably don't want to do this. You aren't using the returned address as far as I can see, so you might as well just pass in NULLs and pass on it.
if(::accept(_sockFd, NULL, NULL)<0)

Possible to retrieve input from user and running another process?

Is it possible to use getline(cin,buffer); at the top of my program, then have a "animated menu" still running below it?
For example (very basic):
#include <iostream>
#include <string>
using namespace std;
int stringLen=0;
string buffer;
getline(cin, buffer);
for (int i = 0; i < kMaxWait;i++)
{
printf("counter waiting for user input %d",i);
if (1 >= buffer.length())
break;
}
Would I have to fork that loop somehow so it would keep counting and display the counter until the user enters something??
One possible answer, given in the comments, is to use threads. But it's not necessary, there's a way to do this without threads.
Make stdin a non-blocking file descriptor.
Wait for stdin to become readable, via poll()/select(), in the meantime do your animation, etc...
Make stdin a blocking file descriptor, again.
Use std::getline().
There are also some ancillary issues to consider, such as the buffering that comes from std::streambuf, so before doing all that, check if there's already something to read from std::cin, first.
This is something I used sometime ago. It's quite rudimentary, but you can get the gist of the process - using poll. It returns true if there is input, and puts it in str, false otherwise. So, you can put this in your loop somewhere, and take action when there is input.
bool polled_input(std::string& str)
{
struct pollfd fd_user_in;
fd_user_in.fd = STDIN_FILENO;
fd_user_in.events = POLLIN;
fd_user_in.revents = 0;
int rv = poll(&fd_user_in, 1, 0);
if (rv == -1) {/* error */}
else if (rv == 0) return false;
else if (fd_user_in.revents & POLLIN)
{
char buffer[MAX_BUFF_SIZE];
int rc = read(STDIN_FILENO, buffer, MAX_BUFF_SIZE-1);
if (rc >= 0)
{
buffer[rc]='\0';
str = std::string(buffer);
return true;
}
else {/* error */}
}
else {/* error */}
}
select is meant for this, multiplexed, blocking I/O. It can be done without a poll I think:
#include <iostream>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char **arg)
{
const int time_in_secs = 10;
const int buffer_size = 1024;
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(STDIN_FILENO, &readfds);
struct timeval tv;
tv.tv_sec = time_in_secs;
tv.tv_usec = 0;
int ret = select(STDIN_FILENO + 1, &readfds, NULL, NULL, &tv);
if (!ret)
{
std::cout << "Timeout\n";
exit(1);
}
char buf[buffer_size];
if (FD_ISSET(STDIN_FILENO, &readfds))
{
int len = read(STDIN_FILENO, buf, buffer_size);
buf[len] = '\0';
}
std::cout << "You typed: " << buf << "\n";
return 0;
}

How to implement a recv() callback

I'm trying to improve my knowledge of OOP and decided to create a simple class to simplify sockets programming.
This is a learning experiment so I do not want to use boost, or other libraries.
I want to implement an event-driven recv(). Meaning, everytime there is new data coming in, it should call my function.
I think I need to create a thread to run a recv() loop and then call my function everytime there is new data. Is there other way around using threads? I want my code to be portable.
Here is my simple Class and example code:
class.h:
#ifndef _SOCKETSCLASS_H
#define _SOCKETSCLASS_H
#if defined(WIN32) || defined(_WIN32) || defined(__WIN32) && !defined(__CYGWIN__)
#define W32
#include <WinSock2.h>
#pragma comment(lib, "ws2_32.lib")
#else
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netdb.h>
#define SOCKET int
#endif
#include <string>
#include<ctime>
#include <stdio.h>
#include <stdarg.h>
#include <varargs.h>
#include <tchar.h>
using namespace std;
#ifdef _DEBUG
#define DEBUG(msg) XTrace(msg)
#else
#define DEBUG(msg, params)
#endif
struct TCP_Client_opts
{
BOOL UseSCprotocol;
BOOL UseEncryption;
BOOL UseCompression;
int CompressionLevel;
void *Callback;
BOOL async;
};
struct TCP_Stats
{
unsigned long int upload; //bytes
unsigned long int download;//bytes
time_t uptime; //seconds
};
class TCP_Client
{
public:
TCP_Client();
TCP_Client(TCP_Client_opts opts_set);
~TCP_Client();
SOCKET GetSocket();
void SetOptions(TCP_Client_opts opts_set);
TCP_Client_opts GetOptions();
BOOL Connect(string server, int port);
int Send(string data);
int Recv(string *data);
BOOL IsConnected();
int Disconnect();
TCP_Stats GetStats();
private:
SOCKET s = SOCKET_ERROR;
TCP_Client_opts opts;
TCP_Stats stats;
BOOL connected = FALSE;
time_t starttime;
};
#endif
class.cpp:
#include "SocketsClass.h"
void XTrace(LPCTSTR lpszFormat, ...)
{
va_list args;
va_start(args, lpszFormat);
int nBuf;
TCHAR szBuffer[512]; // get rid of this hard-coded buffer
nBuf = _vsnwprintf_s(szBuffer, 511, lpszFormat, args);
::OutputDebugString(szBuffer);
va_end(args);
}
TCP_Client::TCP_Client(TCP_Client_opts opts_set)
{
SetOptions(opts_set);
}
TCP_Client::~TCP_Client()
{
Disconnect();
}
TCP_Client::TCP_Client()
{
}
void TCP_Client::SetOptions(TCP_Client_opts opts_set)
{
opts = opts_set;
}
TCP_Client_opts TCP_Client::GetOptions()
{
return opts;
}
SOCKET TCP_Client::GetSocket()
{
return s;
}
BOOL TCP_Client::IsConnected()
{
return connected;
}
int TCP_Client::Disconnect()
{
connected = FALSE;
stats.uptime = time(0) - starttime;
return shutdown(s, 2);
}
BOOL TCP_Client::Connect(string server, int port)
{
struct sockaddr_in RemoteHost;
#ifdef W32
WSADATA wsd;
if (WSAStartup(MAKEWORD(2, 2), &wsd) != 0)
{
DEBUG(L"Failed to load Winsock!\n");
return FALSE;
}
#endif
//create socket if it is not already created
if (s == SOCKET_ERROR)
{
//Create socket
s = socket(AF_INET, SOCK_STREAM, 0);
if (s == SOCKET_ERROR)
{
DEBUG(L"Could not create socket");
return FALSE;
}
}
//setup address structure
if (inet_addr(server.c_str()) == INADDR_NONE)
{
struct hostent *he;
//resolve the hostname, its not an ip address
if ((he = gethostbyname(server.c_str())) == NULL)
{
//gethostbyname failed
DEBUG(L"gethostbyname() - Failed to resolve hostname\n");
return FALSE;
}
}
else//plain ip address
{
RemoteHost.sin_addr.s_addr = inet_addr(server.c_str());
}
RemoteHost.sin_family = AF_INET;
RemoteHost.sin_port = htons(port);
//Connect to remote server
if (connect(s, (struct sockaddr *)&RemoteHost, sizeof(RemoteHost)) < 0)
{
DEBUG(L"connect() failed");
return FALSE;
}
connected = TRUE;
starttime = time(0);
stats.download = 0;
stats.upload = 0;
return TRUE;
}
TCP_Stats TCP_Client::GetStats()
{
if (connected==TRUE)
stats.uptime = time(0)-starttime;
return stats;
}
int TCP_Client::Send(string data)
{
stats.upload += data.length();
return send(s, data.c_str(), data.length(), 0);
}
int TCP_Client::Recv(string *data)
{
int ret = 0;
char buffer[512];
ret = recv(s, buffer, sizeof(buffer), 0);
data->assign(buffer);
data->resize(ret);
stats.download += data->length();
return ret;
}
main.cpp:
#include <stdio.h>
#include <string.h>
#include "SocketsClass.h"
using namespace std;
int main(int argc, char *argv)
{
TCP_Client tc;
tc.Connect("127.0.0.1", 9999);
tc.Send("HEllo");
string data;
tc.Recv(&data);
puts(data.c_str());
tc.Disconnect();
printf("\n\nDL: %i\nUP: %i\nUptime: %u\n", tc.GetStats().download, tc.GetStats().upload, tc.GetStats().uptime);
return 0;
}
Some extra questions:
Imagine I'm sending a file. How would my function know that the current data is related to the previous message?
How is my class design and implementation? SHould I change anything?
Thank you
If by "portable" you mean runs on other platforms besides Windows then a recv() loop in a worker thread is your only portable option. On Windows specifically, you have some additional choices:
Allocate a hidden window and then use WSAAsyncSelect() to receive FD_READ notifications. This requires a message loop, which you can put in a worker thread.
Use WSAEventSelect() to register a waitable event for FD_READ notifications and then wait for those events via WSAWaitForMultipleEvents() in a thread.
use WSARecv() with an I/O Completion Port. Poll the IOCP via GetQueuedCompletionResult() in a thread.
As for your question regarding messaging, TCP is a byte stream, it has no concept of messages. You have to frame your messages yourself. You can either:
give each message a fixed header that contains the message length. Read the header first, then read however many bytes it says, then read the next header, and so on.
separate each message with a unique delimiter that does not appear in the message data. Read until you encounter that delimiter, then read until the next delimiter, and so on.
Have your event loop call either poll or select to determine if there is data that can be read on the socket(s). Then read it, and call the appropriate callback function.

PACKET_TX_RING only sending out first packet, then not doing anything anymore

I have the following code:
#ifndef RAWSOCKET_H
#define RAWSOCKET_H
#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <arpa/inet.h>
#include <netinet/if_ether.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <linux/if.h>
#include <linux/if_packet.h>
#include "IPPacket.h"
#define CONF_RING_FRAMES 128
/// Initialize a packet socket ring buffer
// #param ringtype is one of PACKET_RX_RING or PACKET_TX_RING
static inline char *
init_packetsock_ring(int fd, int ringtype)
{
tpacket_req tp;
char *ring;
// tell kernel to export data through mmap()ped ring
tp.tp_block_size = 1024 * 8;
tp.tp_block_nr = 1024;
tp.tp_frame_size = 1024 * 8;
tp.tp_frame_nr = 1024;
setsockopt(fd, SOL_PACKET, ringtype, (void*) &tp, sizeof(tp));
int val = TPACKET_V1;
setsockopt(fd, SOL_PACKET, PACKET_VERSION, &val, sizeof(val));
// open ring
ring = (char*)mmap(0, tp.tp_block_size * tp.tp_block_nr,
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (!ring)
return NULL;
return ring;
}
/// transmit a packet using packet ring
// NOTE: for high rate processing try to batch system calls,
// by writing multiple packets to the ring before calling send()
//
// #param pkt is a packet from the network layer up (e.g., IP)
// #return 0 on success, -1 on failure
static inline int
process_tx(int fd, char *ring, const char *pkt, int pktlen, sockaddr_ll *txring_daddr)
{
static int ring_offset = 0;
struct tpacket_hdr *header;
struct pollfd pollset;
char *off;
int ret;
// fetch a frame
// like in the PACKET_RX_RING case, we define frames to be a page long,
// including their header. This explains the use of getpagesize().
header = (tpacket_hdr*)(void *) ring + (ring_offset * 1024);
while (header->tp_status != TP_STATUS_AVAILABLE) {
// if none available: wait on more data
pollset.fd = fd;
pollset.events = POLLOUT;
pollset.revents = 0;
ret = poll(&pollset, 1, 1000 /* don't hang */);
if (ret < 0) {
if (errno != EINTR) {
perror("poll");
return -1;
}
return 0;
}
ring_offset++;
if(ring_offset >= 1024 * 8) ring_offset = 0;
header = (tpacket_hdr*)(void *) ring + (ring_offset * 1024);
}
// fill data
off = (char*)(((char*) header) + (TPACKET_HDRLEN - sizeof(struct sockaddr_ll)));
memcpy(off, pkt, pktlen);
// fill header
header->tp_len = pktlen;
header->tp_status = TP_STATUS_SEND_REQUEST;
// increase consumer ring pointer
/*ring_offset++;
if(ring_offset >= 1024 * 8) ring_offset = 0;*/
// notify kernel
if (sendto(fd, NULL, 0, 0, (sockaddr*)txring_daddr, sizeof(sockaddr_ll)) < 0) {
perror("sendto");
return -1;
}
return 0;
}
class RawSocket
{
public:
inline RawSocket() { }
inline void initialize() {
sockfd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_IP));
ring = init_packetsock_ring(sockfd, PACKET_TX_RING);
ifreq ifr;
memset (&ifr, 0, sizeof (ifr));
strncpy((char *) ifr.ifr_name, "eth0", IFNAMSIZ);
ioctl(sockfd, SIOCGIFINDEX, &ifr);
int index = ifr.ifr_ifindex;
ioctl(sockfd, SIOCGIFHWADDR, &ifr);
sll = new sockaddr_ll();
sll->sll_family = AF_PACKET;
sll->sll_ifindex = index;
sll->sll_protocol = htons(ETH_P_IP);
sll->sll_halen = htons(6);
memcpy(IPPacket::our_mac, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
memcpy(sll->sll_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
/*struct packet_mreq mr;
memset (&mr, 0, sizeof (mr));
mr.mr_ifindex = ifr.ifr_ifindex;
mr.mr_type = PACKET_MR_PROMISC;
setsockopt(sockfd, SOL_PACKET,PACKET_ADD_MEMBERSHIP, &mr, sizeof (mr));*/
//setsockopt(sockfd, IPPROTO_IP, IP_HDRINCL, &optval, sizeof(int));
}
inline ~RawSocket() {
close(sockfd);
}
inline void send(const IPPacket* ip) const {
process_tx(sockfd, ring, ip->packet_ptr, ip->tot_len, sll);
printf("TX\n");
}
protected:
char *ring;
int sockfd;
sockaddr_ll *sll;
};
#endif // RAWSOCKET_H
ip->packet_ptr being a pointer to a packet containing ethhdr and iphdr and so on.
The packets are being correcly sent via "normal" PF_PACKET sockets.
Now I tried using the TX Ring feature. However, only the first packet ever gets sent (and it gets sent 100% correctly).
Nothing else seems to happen on the network layer (tcpdump -vvv -e shows no network traffic at all occuring!)
However, the sendto() calls get processed correctly.
I didnt test this functionality myself, but I think you have an error in configuring the struct tpacket_req fields. the _nr fields are quite large. See this example code (linked to from the wiki ):
/* Setup the fd for mmap() ring buffer */
req.tp_block_size=4096;
req.tp_frame_size=1024;
req.tp_block_nr=64;
req.tp_frame_nr=4*64;
if ( (setsockopt(fd,
SOL_PACKET,
PACKET_RX_RING,
(char *)&req,
sizeof(req))) != 0 ) {
perror("setsockopt()");
close(fd);
return 1;
};
/* mmap() the sucker */
map=mmap(NULL,
req.tp_block_size * req.tp_block_nr,
PROT_READ|PROT_WRITE|PROT_EXEC, MAP_SHARED, fd, 0);
(I know this is a bit late but the documentation for this is still poor and examples are few, so hopefully this will help someone):
As per my comments above, this is the working code for me now (with no error checking, just a crude proof of concept):
struct tpacket2_hdr *hdr;
for (uint16_t i = 0; i < tpacket_req.tp_frame_nr; i += 1) {
hdr = (void*)(mmapped_buffer + (tpacket_req.tp_frame_size * i));
uint8_t *data = (uint8_t*)(hdr + TPACKET_ALIGN(TPACKET2_HDRLEN));
memcpy(data, tx_buffer, frame_size);
hdr->tp_len = frame_size;
hdr->tp_status = TP_STATUS_SEND_REQUEST;
}
int32_t send_ret = sendto(sock_fd, NULL, 0, 0, NULL, 0);

Using select without listen()ing, possible?

I am building a client that:
Should be able to recieve information from both the server and the standart input
Should be able to recieve information from the server without asking, for example when another client sends a message.
To do so I tried using select to monitor both possible inputs.
What happens is that when a keyboard input is monitored I send a message to the client and I expect one back, so there's no problem. But when the server sends an unexpected message nothing happens, and I don't know why. Is using select() the proper way to do so? Is it even possible to use select() without listen()ing?
Here's my code (compileable):
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <cstring>
#include <arpa/inet.h>
#include <iostream>
#include <fstream>
#define MAX_CLIENT_NAME 30
#define MAX_TWIT_SIZE 140
#define NUM_OF_ARG 4
#define ERROR -1
#define GREAT_SUCCESS 0
#define OK "OK"
#define EXIT "EXIT"
using std::string;
using std::cerr;
using std::endl;
using std::cout;
string clientName;
int srverfd, numbytes, status, maxSock ;
fd_set inputFdSet; /* Socket file descriptors we want to wake
up for, using select() */
int establishConnection(char * serverAddress,char * port){
if ((srverfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
return ERROR;
}
struct sockaddr_in server;
server.sin_family = AF_INET;
inet_aton(serverAddress, &server.sin_addr);
server.sin_port = htons(atoi(port));
memset(&(server.sin_zero), '\0', 8);
if (connect(srverfd,(const struct sockaddr *)&server,sizeof(struct sockaddr)) == -1) {
perror("connect");
close(srverfd);
return ERROR;
}
maxSock = srverfd;
return GREAT_SUCCESS;
}
const char * getUserTweet(){
string temp;
getline(std::cin,temp);
return temp.c_str();
}
void sendMessage(string message){
if ((numbytes = send(srverfd, message.c_str(), message.length(), 0)) == -1) {
perror("sendMessage");
close(srverfd);
}
cout<<"Message sent: "<< message << endl;
return;
}
const char * getMessage(){
char buf[MAX_TWIT_SIZE];
memset(buf,'\0',MAX_TWIT_SIZE);
if ((numbytes = recv(srverfd, buf, 140, 0)) == -1) {
perror("getMessage");
close(srverfd);
}
string temp = buf;
return temp.c_str();
}
void build_select_list() {
FD_ZERO(&inputFdSet);
FD_SET(srverfd,&inputFdSet);
FD_SET(STDIN_FILENO,&inputFdSet);
if (STDIN_FILENO > maxSock)
maxSock = STDIN_FILENO;
return;
}
void readSocket(fd_set tempfd) {
const char * tweet, * inMessage;
if (FD_ISSET(srverfd,&tempfd)) {
inMessage = getMessage();
cout << inMessage << endl;
}
if (FD_ISSET(STDIN_FILENO,&tempfd)) {
tweet = getUserTweet();
sendMessage(tweet);
inMessage = getMessage();
if (strcmp(inMessage,OK) != 0) {
cout << inMessage << endl;
}
if (strcmp(inMessage,EXIT) == 0) {
return;
}
}
return;
}
int main (int argc, char *argv[] ){
int value;
bool clientON = false;
if(establishConnection(argv[2],argv[3])){
cerr << "usage: failed to make connection" << endl << "exiting..." << endl;
exit(EXIT_FAILURE);
}
cout << "Connected successfully" << endl;
sendMessage("CONNECT "+clientName); //Connect
if(strcmp(getMessage(),OK) == 0){
clientON = true;
}
while(clientON){
build_select_list();
value = select(maxSock, &inputFdSet, NULL, NULL, NULL);
if (value < 0) {
perror("select");
exit(EXIT_FAILURE);
}
if (value == 0) {
continue;
}
else {
readSocket(inputFdSet);
}
}
sendMessage("DISCONNECT");
if(strcmp(getMessage(),OK) == 0){
// do nothing
}
close(srverfd);
return 0;
}
Your select call is invalid. The first parameter must be the highest file descriptor in any of the sets, plus one.
As you have it, an event on srverfd will not "wake up" the select call (unless STDIN_FILENO was somehow less than srverfd, in which case stdin events wouldn't unlock select - but that won't happen in practice).
There are quite a few other problems with your code. (It doesn't really look like C++.)
getUserTweet is unreliable (undefined behavior - temp is destroyed as soon as the function returns, so the char* you return has disappeared by the time its caller will try to use it). Same for getMessage. To remedy that, use std::string everywhere, and only extract the char* when you call into C library functions).
readSocket needlessly copies the FD set (can be expensive).
You should really get rid of all those globals - build one or two classes to encapsulate that state and the networking functions, or something like that.