Actually I'm trying to capture trafic with TCPdumpand redirect result in a file (.pcap) my first problem is how to read MAC address IP address and Signal strenghfrom the .pcap using C++.
Second problem is that I want to scan trafic from a specefic MAC address, then be able to change it with another MAC address
here is what my tcpdump:
sudo tcpdump -i wlan0 -e ether host 90:B6:86:15:A9:DB -vvv -w capture.pcap
The library for dealing with pcap files is called libpcap.
For starters a tutorial can be found here:
http://www.tcpdump.org/pcap.html
I will answer how to read MAC address and IP address from the .pcap using C++. This is a minimal working example, I specifically stripped all error handling and such.
#include <iomanip>
#include <iostream>
#include <pcap/pcap.h>
#include <net/ethernet.h>
#include <netinet/ip.h>
#include <netinet/udp.h>
int main(int argc, char const *argv[])
{
char errbuf[PCAP_ERRBUF_SIZE];
pcap_t* handle = pcap_open_offline("dump.pcap", errbuf);
struct pcap_pkthdr* header;
const u_char* packet;
int result = 0;
do {
result = pcap_next_ex(handle, &header, &packet);
if (result == PCAP_ERROR_BREAK) break;
// Ethernet layer
const struct ether_header* ethernet_header = reinterpret_cast<const struct ether_header*>(packet);
std::cout << "Source MAC: ";
for (int i = 0; i < ETH_ALEN; ++i) {
std::cout << std::setfill('0') << std::setw(2) << std::hex << std::uppercase
<< static_cast<int>(ethernet_header->ether_shost[i]);
if (i < ETH_ALEN - 1) std::cout << ":";
}
std::cout << std::endl;
std::cout << "Destination MAC: ";
for (int i = 0; i < ETH_ALEN; ++i) {
std::cout << std::setfill('0') << std::setw(2) << std::hex << std::uppercase
<< static_cast<int>(ethernet_header->ether_dhost[i]);
if (i < ETH_ALEN - 1) std::cout << ":";
}
std::cout << std::endl;
if (ntohs(ethernet_header->ether_type) == ETHERTYPE_IP) {
// IP level
const struct ip* ip_header = (struct ip*)(packet + sizeof(struct ether_header));
char source_ip[INET_ADDRSTRLEN];
char dest_ip[INET_ADDRSTRLEN];
inet_ntop(AF_INET, &(ip_header->ip_src), source_ip, INET_ADDRSTRLEN);
inet_ntop(AF_INET, &(ip_header->ip_dst), dest_ip, INET_ADDRSTRLEN);
std::cout << "Source IP: " << source_ip << std::endl;
std::cout << "Destination IP: " << dest_ip << std::endl;
}
} while (result == 1);
pcap_close(handle);
return 0;
}
Reference
https://www.tcpdump.org/manpages/pcap_open_offline.3pcap.html
https://elf11.github.io/2017/01/22/libpcap-in-C.html
Related
I am trying to test sending data from 2 distinct network adapters on the same machine to a common remote endpoint, but I keep getting "bind: invalid argument" AFTER the first bind comes through. What am I missing? I have searched, tried to modify the code, but I was not able to find any lead and I keep getting the same error. The same happens when I swap out the IPs.
#include <iostream>
#include <boost/asio.hpp>
#include <sstream>
#include <thread>
#include <chrono>
#include <boost/random.hpp>
const unsigned int MS_INTERVAL = 100;
enum CMD_ARG
{
PROG_NAME = 0,
LOCAL_IP_1,
LOCAL_IP_2,
REMOTE_IP,
REMOTE_PORT
};
using namespace boost::asio;
using std::string;
using std::cout;
using std::endl;
int main(int argc, char *argv[]) {
if(argc == 5)
{
//Test data initialisation
unsigned int counter = 0;
boost::random::mt19937 randSeed; // seed, produces randomness out of thin air
boost::random::uniform_int_distribution<> randGen(-1000,1000); // Random number generator between -100 and 100
//Initialise ASIO service
io_service io_service;
//socket creation and binding (one per network adapter)
std::cout << "Opening and binding local sockets to " << argv[LOCAL_IP_1] << " and " << argv[LOCAL_IP_2] << std::endl;
ip::tcp::socket socket1(io_service);
ip::tcp::socket socket2(io_service);
socket1.open(ip::tcp::v4());
socket2.open(ip::tcp::v4());
socket1.bind(ip::tcp::endpoint(ip::address::from_string(argv[LOCAL_IP_1]), 0));
std::cout << "1/2 done" << std::endl;
socket2.bind(ip::tcp::endpoint(ip::address::from_string(argv[LOCAL_IP_2]), 0));
//Connection to remote end point starting with defining the remote endpoint
std::istringstream iss(argv[REMOTE_PORT]);
unsigned int port = 0;
iss >> port;
ip::tcp::endpoint remoteEndpoint = ip::tcp::endpoint( ip::address::from_string(argv[REMOTE_IP]), port);
std::cout << "Connecting to " << argv[REMOTE_IP] << " on port " << port << std::endl;
socket1.connect(remoteEndpoint);
std::cout << "1/2 done" << std::endl;
socket2.connect(remoteEndpoint);
std::cout << "Ready" << std::endl;
while(1)
{
//Build message
std::ostringstream oss;
oss << counter << "," << randGen(randSeed) << "," << randGen(randSeed) << "," << randGen(randSeed) << std::endl;
//Send message on both interfaces
boost::system::error_code error1, error2;
write(socket1, boost::asio::buffer(oss.str()), error1);
write(socket2, boost::asio::buffer(oss.str()), error2);
//Check errors
if( !error1 && !error2) {
cout << "Sending: " << oss.str() << endl;
counter++;
}
else {
cout << "Error: " << (error1?error1.message():error2.message()) << endl;
}
std::this_thread::sleep_for(std::chrono::milliseconds(MS_INTERVAL));
}
}
else
{
std::cout << "Usage: <program> <local IP 1> <local IP 2> <remote server IP> <server's opened port>" << argc << std::endl;
}
return 0;
}
socket1.bind(ip::tcp::endpoint(ip::address::from_string(argv[LOCAL_IP_1]), 0));
...
socket1.bind(ip::tcp::endpoint(ip::address::from_string(argv[LOCAL_IP_2]), 0));
You are trying to bind the same socket1 twice. Likely you mean socket2 in the second statement.
I've been gleaning information about the NETLINK socket which allows us to listen on what is happening in socket land. It seems to very partially work, but I'm wondering whether I missed something.
I'm using C++, although of course all the NETLINK is pure C, so here we have mainly C headers. The following code has three main parts:
Binding
First I create a socket() and bind() it.
The bind() is kind of magical. When using a bound NETLINK socket, you start receiving events without having to have a polling setup (which is why I'm trying to do this, to avoid polling for when a socket starts listening for connections).
I put -1 in the nl_groups so that way all events are sent to my socket. But, at this point, I seem to only receive two of them: TCP_ESTABLISHED and TCP_CLOSE. The one I really would like to receive is the TCP_LISTEN and "not listening" (which apparently is not going to be available...)
Explicit Request
I tried with an explicit request. I have it in the code below so you can see how I've done it. That request works as expected. I get an explicit reply if the socket exists or an error "No such file or directory" when the socket is closed. Great, except that mechanism means I'd be using a poll (i.e. I need my process to try over and over again on a timer until the socket is visible).
Note: the error when no one is listening is happening because the request is explicit, i.e. it includes the expected IP address and port that I'm interested in.
Response
The next part is a loop, which sits until a response is received. The recvmsg() call is blocking in this version, which is why it sits around in this test.
If I sent my explicit request (see point 2. above), then, as I mentioned, I get a reply if another process is listening, otherwise I get an error saying it's not listening. The state is clearly set to 10 (TCP_LISTEN), so everything works as expected.
When listening to all the events (-1 in the bind), the process will go on and receive more data as events happen. However, so far, the only events I've received are 1 and 7 (i.e. TCP_ESTABLISHED and TCP_CLOSE).
I used the following to compile my code:
g++ -Wall -o a test.cpp
Here is my test code with which I can reproduce my current results:
#include <iostream>
#include <linux/netlink.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/sock_diag.h>
#include <linux/inet_diag.h>
int
main(int argc, char ** argv)
{
// socket / bind
int d = socket(AF_NETLINK, SOCK_RAW, NETLINK_SOCK_DIAG);
if(d < 0)
{
std::cerr << "error: could not create RAW socket.\n";
return 1;
}
struct sockaddr_nl addr = {};
addr.nl_family = AF_NETLINK;
addr.nl_pid = getpid();
addr.nl_groups = -1;
// You can find these flags in Linux source:
//
// "/usr/src/linux-headers-4.15.0-147/include/net/tcp_states.h
//
// (1 << 7) // TCPF_CLOSE
// | (1 << 8) // TCPF_CLOSE-WAIT
// | (1 << 10) // TCPF_LISTEN
// | (1 << 11) // TCPF_CLOSING
// ;
if(bind(d, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)) != 0)
{
perror("bind failure\n");
return 1;
}
// request
struct sockaddr_nl nladdr = {};
nladdr.nl_family = AF_NETLINK;
struct nl_request
{
struct nlmsghdr f_nlh;
struct inet_diag_req_v2 f_inet;
};
nl_request req = {};
req.f_nlh.nlmsg_len = sizeof(req);
req.f_nlh.nlmsg_type = SOCK_DIAG_BY_FAMILY;
req.f_nlh.nlmsg_flags = NLM_F_REQUEST;
req.f_inet.sdiag_family = AF_INET;
req.f_inet.sdiag_protocol = IPPROTO_TCP;
req.f_inet.idiag_ext = 0;
req.f_inet.pad = 0;
req.f_inet.idiag_states = 0;
req.f_inet.id.idiag_sport = htons(4998);
req.f_inet.id.idiag_dport = 0;
req.f_inet.id.idiag_src[0] = htonl(0x0A00020A);
req.f_inet.id.idiag_dst[0] = 0;
req.f_inet.id.idiag_if = 0;
req.f_inet.id.idiag_cookie[0] = INET_DIAG_NOCOOKIE;
req.f_inet.id.idiag_cookie[1] = INET_DIAG_NOCOOKIE;
struct iovec vector = {};
vector.iov_base = &req;
vector.iov_len = sizeof(req);
struct msghdr msg = {};
msg.msg_name = &nladdr;
msg.msg_namelen = sizeof(nladdr);
msg.msg_iov = &vector;
msg.msg_iovlen = 1;
int const r(sendmsg(d, &msg, 0));
if(r < 0)
{
perror("sendmsg");
return 1;
}
// response
struct sockaddr_nl r_nladdr = {};
r_nladdr.nl_family = AF_NETLINK;
struct iovec r_vector = {};
long buf[8192 / sizeof(long)];
r_vector.iov_base = buf;
r_vector.iov_len = sizeof(buf);
for(int i(1);; ++i)
{
struct msghdr r_msg = {};
r_msg.msg_name = &r_nladdr;
r_msg.msg_namelen = sizeof(r_nladdr);
r_msg.msg_iov = &r_vector;
r_msg.msg_iovlen = 1;
//std::cout << "wait for message...\n";
ssize_t size(recvmsg(d, &r_msg, 0));
if(size < 0)
{
perror("recvmsg");
return 1;
}
if(size == 0)
{
std::cout << "end of message stream received." << std::endl;
break;
}
//std::cout << "got message #" << i << ": size = " << size << std::endl;
struct nlmsghdr const * h(reinterpret_cast<struct nlmsghdr *>(buf));
if(!NLMSG_OK(h, size))
{
std::cerr << "NLMSG_OK() says there is an error." << std::endl;
return 1;
}
do
{
if(h->nlmsg_type == NLMSG_DONE)
{
std::cout << "explicit end of message stream received (NLMSG_DONE)." << std::endl;
break;
}
if(h->nlmsg_type == NLMSG_ERROR)
{
struct nlmsgerr const * err(reinterpret_cast<struct nlmsgerr const *>(NLMSG_DATA(h)));
if(h->nlmsg_len < NLMSG_LENGTH(sizeof(*err)))
{
std::cerr << "unknown NLMSG_ERROR received." << std::endl;
}
else
{
// here is the location display an error when trying to get an
// event about the LISTEN and no one is listening on that port.
//
errno = -err->error;
perror("NLMSG_ERROR:");
}
return 1;
}
if(h->nlmsg_type != SOCK_DIAG_BY_FAMILY)
{
std::cerr << "unexpected message type (h->nlmsg_type) "
<< h->nlmsg_type
<< std::endl;
return 1;
}
//std::cout << "------- sock_diag info!\n";
struct inet_diag_msg const * k_msg(reinterpret_cast<struct inet_diag_msg const *>(NLMSG_DATA(h)));
if(h->nlmsg_len < NLMSG_LENGTH(sizeof(*k_msg)))
{
std::cerr << "unexpected message length (h->nlmsg_len) "
<< h->nlmsg_type
<< std::endl;
return 1;
}
switch(k_msg->idiag_state)
{
case 1:
case 7:
break;
default:
{
std::uint32_t const src_ip(ntohl(k_msg->id.idiag_src[0]));
std::uint32_t const dst_ip(ntohl(k_msg->id.idiag_dst[0]));
std::cout << "inet_diag_msg->idiag_family = " << static_cast<int>(k_msg->idiag_family) << "\n"
<< "inet_diag_msg->idiag_state = " << static_cast<int>(k_msg->idiag_state) << "\n"
<< "inet_diag_msg->idiag_timer = " << static_cast<int>(k_msg->idiag_timer) << "\n"
<< "inet_diag_msg->idiag_retrans = " << static_cast<int>(k_msg->idiag_retrans) << "\n"
<< "inet_diag_msg->id.idiag_sport = " << ntohs(k_msg->id.idiag_sport) << "\n"
<< "inet_diag_msg->id.idiag_dport = " << ntohs(k_msg->id.idiag_dport) << "\n"
<< "inet_diag_msg->id.idiag_src[0] = " << ((src_ip >> 24) & 255)
<< "." << ((src_ip >> 16) & 255) << "." << ((src_ip >> 8) & 255) << "." << (src_ip & 255) << "\n"
<< "inet_diag_msg->id.idiag_dst[0] = " << ((dst_ip >> 24) & 255)
<< "." << ((dst_ip >> 16) & 255) << "." << ((dst_ip >> 8) & 255) << "." << (dst_ip & 255) << "\n"
<< "inet_diag_msg->id.idiag_if = " << k_msg->id.idiag_if << "\n"
<< "inet_diag_msg->id.idiag_cookie[0] = " << k_msg->id.idiag_cookie[0] << "\n"
<< "inet_diag_msg->id.idiag_cookie[1] = " << k_msg->id.idiag_cookie[1] << "\n"
<< "inet_diag_msg->idiag_expires = " << k_msg->idiag_expires << "\n"
<< "inet_diag_msg->idiag_rqueue = " << k_msg->idiag_rqueue << "\n"
<< "inet_diag_msg->idiag_wqueue = " << k_msg->idiag_wqueue << "\n"
<< "inet_diag_msg->idiag_uid = " << k_msg->idiag_uid << "\n"
<< "inet_diag_msg->idiag_inode = " << k_msg->idiag_inode << "\n"
<< "\n";
}
break;
}
// next message
//
h = NLMSG_NEXT(h, size);
}
while(NLMSG_OK(h, size));
}
return 0;
}
To test that IP:port combo, I simply used the nc command like so:
nc -l 10.0.2.10 4998
You of course need the 10.0.2.10 IP on one of your interfaces for this to work.
My question is:
Did I do something wrong that I do not receive TCP_LISTEN events on that socket unless explicitly requested?
P.S. Just in case, I tried to run this test app as root. Same results.
I am writting a program to display local IP address of the machine.
I am getting able to display IPv4 address, while getting unable to display IPv6 address.
Below is the program that i am using to display IPv4 address:
#include <iostream.h>
#include <winsock.h>
int doit(int, char **)
{
char ac[80];
if (gethostname(ac, sizeof(ac)) == SOCKET_ERROR) {
cerr << "Error " << WSAGetLastError() <<
" when getting local host name." << endl;
return 1;
}
cout << "Host name is " << ac << "." << endl;
struct hostent *phe = gethostbyname(ac);
if (phe == 0) {
cerr << "Yow! Bad host lookup." << endl;
return 1;
}
for (int i = 0; phe->h_addr_list[i] != 0; ++i) {
struct in_addr addr;
memcpy(&addr, phe->h_addr_list[i], sizeof(struct in_addr));
cout << "Address " << i << ": " << inet_ntoa(addr) << endl;
}
return 0;
}
int main(int argc, char *argv[])
{
WSAData wsaData;
if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) {
return 255;
}
int retval = doit(argc, argv);
WSACleanup();
return retval;
}
gethostbyname is obsolete and on many systems ignores IPv6 entries.
Use modern getaddrinfo function and check ai_family member for AI_INET or AI_INET6 in order to identify address type.
As previous answer stated, use getaddrinfo(). Also, since you are using C++, make sure you use RAII for the cleanup, so cleanup will be done even if exceptions are thrown. Here is an example:
if ( (n = getaddrinfo(host.c_str(), service.c_str(), &hints, &res)) != 0) {
ostringstream ss;
ss << "getaddrinfo error for " << host << ", " << service
<< ", " << gai_strerror(n);
throw std::runtime_error(ss.str());
}
// Make sure freeaddrinfo is called even if exceptions are thrown.
// Note that we do not need to check res for NULL, unique_ptr handles that
// when deallocating.
auto cleanup = [](addrinfo* ai) { freeaddrinfo(ai); };
unique_ptr<addrinfo, decltype(cleanup)> aip(res, cleanup);
This is my sender code snippet.
if(ThreadQ.try_dequeue(temp)){
if(seqno>=2147483645)
{
seqno=-1;
}
if(frameno>=29)
{
frameno=-1;
}
seqno++;
frameno++;
fragno=0;
std::ofstream f1("packet.txt",std::ios::app);
for(int j=0;j<5;j++)
{
//Packetize-Fragment
fp.fragno=j;
fp.pl.line[0]=temp.line[k++];
fp.pl.line[1]=temp.line[k++];
fp.pl.line[2]=temp.line[k++];
fp.pl.line[3]=temp.line[k++];
fp.seqno = seqno;
fp.frameno = frameno;
retval = send(conn_socket, (char *)&fp, sizeof(fp), 0);
for (i = 0; i < 4; i++)
{
f1 << fp.seqno << " " << fp.frameno << " " << fp.fragno << " " << fp.pl.line[i].x << " " << fp.pl.line[i].y << " " << fp.pl.line[i].z << " " << fp.pl.line[i].ch << "\n";
}
}
f1 << "\n\n";
k=0;
}
and these are the relevant structures,
typedef struct PacketPos{
float x;
float y;
float z;
int ch;
};
typedef struct PacketPL2{
PacketPos line[4];
};
typedef struct FinalPacket{
PacketPL2 pl;
int seqno;
int frameno;
int fragno;
};
But when I receive it at the receiver end, over UDP (Receiver code shown below):
char * Buffer = (char *)malloc(1000);
while (1){
retval = recvfrom(msgsock, Buffer, 10000, 0, (struct sockaddr *)&from, &fromlen);
printf("%d ", retval);
fp = *(FinalPacket*)Buffer;
std::ofstream fout("output.txt", std::ios::app);
for (int i = 0; i < 4; i++)
{
fout << fp.seqno << " " << fp.frameno << " " << fp.fragno << " " << fp.pl.line[i].x << " " << fp.pl.line[i].y << " " << fp.pl.line[i].z << " " << fp.pl.line[i].ch;
fout << "\n";
}
fout << "\n\n";
}
the float data is not received and I just see 0s in the place of the float data. I'm a beginner, so can anyone tell me what I'm doing wrong here? Thanks in advance.
I don't know the architecture where you are running. I suppose it is x86 or 64 bits.
The snippet you show is incomplete and there is at least one coding error.
First error, line is a vector of 4 elements:
typedef struct PacketPL2 {
PacketPos line[4];
};
In the client:
fp.pl.line[0]=temp.line[k++];
k at some moment will be greater than 3 and you have a buffer overflow because you are setting k to 0 outside the loop.
I suppose conn_socket is already connected to the server, is it correct? Otherwise, there is another error.
Other than this, your code should work alright.
VERY IMPORTANT: YOUR CODE IS NOT PORTABLE AT ALL. You must not just cast structures to buffers (and the other way around) if you want to make it portable. I'm talking about portability among different architectures: different int/float/double size and different endianship.
For making it portable you need to define some endianship, floating point representation, and data size for your protocol. Then make each conversion one piece of data at the time. Using #pragma pack will help you only with data alignment in the structure but at the same time, not only it is compiler dependent but also is less efficient for the processor.
I implemented a UDP client-server with your code (but using sendto in the client), and except for the error above, it works OK. The code is not nice, I tried to put your snippets inside but it works.
Client:
typedef struct PacketPL2
{
PacketPos line[4];
} s_pp2;
typedef struct FinalPacket
{
PacketPL2 pl;
int seqno;
int frameno;
int fragno;
} s_fp;
int main()
{
int seqno = 214000098;
int frameno = 10;
seqno++;
frameno++;
int fragno=0;
s_fp fp;
s_pp2 temp;
int conn_socket;
struct sockaddr_in servaddr;
temp.line[0].x = 4.56;
temp.line[0].z = 3.56;
temp.line[1].x = 7.99;
temp.line[1].z = 5.99;
temp.line[2].x = 3.99;
temp.line[2].z = 4.59;
temp.line[3].x = 1.51;
temp.line[3].z = 2.33;
bzero(&servaddr,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr=inet_addr("127.0.0.1");
servaddr.sin_port=htons(32000);
conn_socket = socket(AF_INET, SOCK_DGRAM, 0);
using namespace std;
int k = 0;
for(int j=0;j<5;j++)
{
//Packetize-Fragment
fp.fragno=j;
//ERROR, buffer overflow: WHEN K > 3
fp.pl.line[0]=temp.line[k++];
fp.pl.line[1]=temp.line[k++];
fp.pl.line[2]=temp.line[k++];
fp.pl.line[3]=temp.line[k++];
fp.seqno = seqno;
fp.frameno = frameno;
// int retval = send(conn_socket, (char *)&fp, sizeof(fp), 0);
int retval = sendto(conn_socket,(char *)&fp, sizeof(fp),0, (struct sockaddr *)&servaddr,sizeof(servaddr));
cout << "RETVAL cli:" << retval << endl;
for (int i = 0; i < 4; i++)
{
cout << fp.seqno << " " << fp.frameno << " " << fp.fragno << " " << fp.pl.line[i].x << " " << fp.pl.line[i].y << " " << fp.pl.line[i].z << " " << fp.pl.line[i].ch << "\n";
}
}
cout << "\n\n";
//K IS INITIALIZED HERE
k=0;
return 0;
}
Server:
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <stdlib.h>
#include <strings.h>
typedef struct PacketPos
{
float x;
float y;
float z;
int ch;
} s_pp;
typedef struct PacketPL2
{
PacketPos line[4];
} s_pp2;
typedef struct FinalPacket
{
PacketPL2 pl;
int seqno;
int frameno;
int fragno;
} s_fp;
int main()
{
char * Buffer = (char *)malloc(1000);
int msgsock;
s_fp fp;
struct sockaddr_in servaddr, from;
socklen_t fromlen;
bzero(&from, sizeof(from));
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
servaddr.sin_port=htons(32000);
msgsock = socket(AF_INET, SOCK_DGRAM, 0);
bind(msgsock,(struct sockaddr *)&servaddr,sizeof(servaddr));
using namespace std;
while (1)
{
int retval = recvfrom(msgsock, Buffer, 10000, 0, (struct sockaddr *)&from, &fromlen);
cout << "RETVAL:" << retval << endl;
fp = *(FinalPacket*)Buffer;
for (int i = 0; i < 4; i++)
{
cout << fp.seqno << " " << fp.frameno << " " << fp.fragno << " " << fp.pl.line[i].x << " " << fp.pl.line[i].y << " " << fp.pl.line[i].z << " " << fp.pl.line[i].ch;
cout << "\n";
}
cout << "\n\n";
}
return 0;
}
See these links for floating point representation and size:
What is the size of float and double in C and C++?
Fixed-size floating point types
In your code : fp = *(FinalPacket*)Buffer will not be casted to Final Packet because sizeof(FinalPacket) is NOT what you expect.
For example:
Let's say we have a struct:
struct Point
{
int x;
int y;
}
Then sizeof(Point) is not 2 * sizeof(int) because of padding involved. Google for further info.
The solution to this is to use pragma pack
So in your case, you should surround your struct with pragma pack.
Example:
#pragma pack(push, 1)
typedef struct FinalPacket{
PacketPL2 pl;
int seqno;
int frameno;
int fragno;
};
#pragma pack(pop)
Now you will be able to cast the buffer directly to struct.
Your question is easy to solve. I have wrote a simple test for udp communication. Now I give your my codes, only key points:
//my struct:
struct TestCase
{
float x;
float y;
};
//client key points:
TestCase case_2;
case_2.x = 0.5;
case_2.y = 1.0;
if(-1 == sendto(sk_fd, (char*)&case_2, sizeof(case_2), 0, (struct sockaddr*)&remote, sizeof(remote)))
{
cout << "client send data failed, error is " << strerror(errno) << endl;
return 0;
}
//server key points:
TestCase server;
while(1)
{
struct sockaddr_in client;
memset(&server, 0, sizeof(server));
socklen_t client_len = sizeof(client);
const int result = recvfrom(sk_fd, &server, sizeof(server), 0, (struct sockaddr*)&client, &client_len);
if(result < 0)
cout << "server recv error is " << strerror(errno) << endl;
cout << server.x << ' ' << server.y << endl;
break;
}
After you see these above, I think you can know well. You only need to change your code: char * Buffer = (char *)malloc(1000). You should use the struct for receiving the data. Now Do you see it ? I hope this can help you.
I am trying to create a client server application in linux. Server is supposed to send one object to all connected clients.
Here is the code for it.
In this When server send object, everything remains ok at server side but Segmentation fault occurs on client server immediately it receive it.
Server:
#include "Question.h"
#include <iostream>
#include <string.h>
using namespace std;
#include<sys/socket.h>
#include<sys/types.h>
#include<stdio.h>
#include<arpa/inet.h>
#include<time.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <netinet/in.h>
int main() {
Question q1("Capital Of Pakistan?", "Lahore", "Karachi", "Quetaa", "Islamabad");
int socketID = 0, clientID[10] = {0}, totalClients = 3;
char sendBuffer[1024];
memset(sendBuffer, '0', sizeof(sendBuffer));
time_t time;
struct sockaddr_in servAddr;
cout << "Question is: \n" << q1.getQuestion()<<endl;
cout << q1.getOpt1() << endl << q1.getOpt2() << endl << q1.getOpt3() << endl << q1.getCorrect()<<endl;
cout << "\n\n --- Server starting up --- \n\n";
/*
* Creating Socket
*/
socketID = socket(AF_INET, SOCK_STREAM, 0);
if (socketID == -1) {
cerr << " Can't create Socket";
}
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(5000);
servAddr.sin_addr.s_addr = htonl(INADDR_ANY);
/*
* Binding IP
*/
int bindID;
bindID = bind(socketID, (struct sockaddr *) &servAddr, sizeof(servAddr)); // Casting sockaddr_in on sockaddr and binding it with socket id
if (bindID != -1) {
cout << " Bind SucessFull";
listen(socketID, 5);
cout << " Server Waiting for connections" << endl;
int i = 0;
while (1) {
clientID[i] = accept(socketID, (struct sockaddr *) NULL, NULL);
cout << "Got Client: " << i+1 << ","<<totalClients-(i+1)<<" to go" << endl;
cout << "ID: " << clientID[i]<<endl;
cout.flush();
snprintf(sendBuffer, sizeof(sendBuffer), "%.24s\n", ctime(&time));
write(clientID[i], sendBuffer, strlen(sendBuffer));
i++;
if (i >= totalClients)
break;
sleep(1);
}
cout << "Sending Question to All Clients...." << endl;
for(int j=0; j<totalClients; j++) {
cout << "Sending to ID " << clientID[j]<<endl;
write(clientID[j], &q1 , sizeof(q1));
cout << "Sent " << j << "...." << endl;
}
/*
* Closing all clients
*/
for (int k = 0; k < totalClients; k++) {
close(clientID[k]);
}
} else {
cerr << " Unable to Bind";
}
return 0;
}
Client:
#include "Question.h"
#include <iostream>
#include <string.h>
using namespace std;
#include<sys/socket.h>
#include<sys/types.h>
#include<stdio.h>
#include<arpa/inet.h>
#include<time.h>
#include <netinet/in.h>
#include <netdb.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
int main(int argc, char *argv[])
{
int socketID = 0 /*Socket Descriptor*/, n = 0;
char recvBuffer[1024];
memset(recvBuffer, '0',sizeof(recvBuffer));
struct sockaddr_in servAddr;
if(argc!=2){
cout << "\n Usage: %s <ip of server> \n",argv[0];
return 1;
}
socketID = socket(AF_INET, SOCK_STREAM, 0);
if(socketID == -1){
cerr << "\n Can't create socket \n";
return 1;
}
servAddr.sin_family = AF_INET;
servAddr.sin_port = htons(5000);
if(inet_pton(AF_INET, argv[1], &servAddr.sin_addr)==-1){
cerr << "\n Unable to convert given IP to Network Form \n inet_pton Error";
return 1;
}
int connectFlag;
connectFlag = connect(socketID, (struct sockaddr *)&servAddr, sizeof(servAddr));
if(connectFlag == -1){
cout << " Connection Failed" << endl;
return 1;
}
n = read(socketID, recvBuffer, sizeof(recvBuffer)-1);
recvBuffer[n] = 0;
cout << recvBuffer << endl;
if(n < 0){
cerr << "Buffer Read error\n";
}
Question q1;
cout << "Gonna Receive Connections"<<endl;
q1.setAll("0","0","0","0","0");
cout.flush();
n = read(socketID, &q1, sizeof(q1));
cout << n << endl;
cout << "Received Question " << endl;
cout << "Question is: \n" << q1.getQuestion()<<endl;
cout << q1.getOpt1() << endl << q1.getOpt2() << endl << q1.getOpt3() << endl << q1.getCorrect()<<endl;
cout.flush();
return 0;
}
Question.h
#ifndef QUESTION_H_
#define QUESTION_H_
#include <iostream>
using namespace std;
class Question {
private:
string question;
string opt1;
string opt2;
string opt3;
string correct;
public:
/*
* Constructors
*/
Question();
Question(string, string, string, string, string);
void setAll(string, string, string, string, string);
string getCorrect() const;
void setCorrect(string correct);
string getOpt1() const;
void setOpt1(string opt1);
string getOpt2() const;
void setOpt2(string opt2);
string getOpt3() const;
void setOpt3(string opt3);
void setQuestion(string question);
string getQuestion() const;
};
#endif /* QUESTION_H_ */
You will need to "serialize" your object. This usually involves making it into a string that can be read at the other "side" of whatever thing you are sending the object through.
It is exactly the same issue as if you were to write the data to a file, you don't want to store the OBJECT, you want to store the "payload" or "content" of that is inside the class.
You can use stringstream to form a long string of your data, and pass the string formed from that.
Something like:
class A
{
int x;
string s;
};
...
class A a;
stringstream ss;
ss << a.x << ", " << a.s << endl;
....
write(clientID[j], ss.str.c_str(), ss.str.length());
You will obviously need to parse the resulting string at the other end - and , may not be the ideal separator. Feel free to use something else...