Bad address in my write-function - c++

I have to code this little task and can´t find my mistake. It should just read some data from a file and copy it in opposite order into another one. The first part seems to work, but the while-part gives me "Bad Address" for every time the write function is used. I´m grateful for every idea!
#include <iostream>
#include <cerrno>
#include <fcntl.h>
#include <cstdio>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#define TRY(cmd,msg) { \
if ((cmd) < 0) { \
std::cerr << (msg) << ": " << strerror(errno) << std::endl; \
} \
}
int main(int argc, char *argv[]){
int fd_in, fd_out, rest;
off_t map_size, offset, length;
char * addr;
struct stat sb;
if (argc != 3) {
std::cerr << "Usage: kopfstand in out" << std::endl;
return -1;
}
if ((fd_in = open(argv[1],O_RDONLY)) == -1){
perror("open");
return -1;
}
if ((fd_out = open(argv[2],O_WRONLY|O_CREAT|O_TRUNC,S_IRUSR|S_IWUSR)) == -1){
close(fd_in);
perror("open");
return -1;
}
fstat(fd_in, &sb);
length = sb.st_size;
map_size = sysconf(_SC_PAGESIZE);
rest = length % map_size;
offset = length -rest;
if(rest != 0){
addr = (char*)mmap(NULL, rest, PROT_READ, MAP_PRIVATE, fd_in, offset);
if(addr == MAP_FAILED){
perror("Error mmaping the File");
}
for (int off = rest-1; off >= 0; --off){
TRY(write(fd_out, addr+off, 1),"write");
}
if(munmap((char*)addr, rest)== -1){
perror("munmap");
}
}
while(offset > 0){
offset =- map_size;
addr = (char*)mmap(NULL, map_size, PROT_READ, MAP_SHARED, fd_in, offset);
if(addr == MAP_FAILED){
perror("Error mmaping the File");
}
for(int off = map_size-1; off >= 0; --off){
TRY(write(fd_out, addr+off, 1),"write");
}
if(munmap((char*)addr, map_size) == -1){
perror("munmap");
}
}
close(fd_in);
close(fd_out);
return 0;
}

You're going to kick yourself! (I kicked myself when I finally spotted the problem!)
This line:
offset =- map_size;
should be:
offset -= map_size;
See also What does =+ mean in C?
One of the comments to the question by Andrew Medico observes that clang identifies the problem immediately — and it does:
$ clang -O3 -g -std=c++11 -Wall -Wextra -Werror badadd.cpp -o badadd
badadd.cpp:65:16: error: use of unary operator that may be intended as compound assignment (-=)
[-Werror]
offset =- map_size;
G++ (GCC) 4.9.1 (home-built on Mac OS X 10.9 and running on 10.10.1) with the same compiler options does not identify the problem.
JFTR: I found the problem the hard way (with print statements tracing the value of offset), and only read Andrew's comment after finding and posting the answer.

Related

Segmentation fault using DS18B20 Temperature Sensor

I am trying to run a code in C++ that takes the reading from raspberry pi3 using DS18B20 . The code is compiled without errors but when i try to run it . It gives an error of segmentation fault .
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <wiringPi.h>
#include <iostream>
#define BUFSIZE 128
using namespace std;
int main(void)
{
float temp;
int i, j;
size_t ret;
FILE *fd;
char buf[BUFSIZE];
char tempBuf[5];
char *buffer;
buffer = (char*) malloc (sizeof(char)*BUFSIZE);
while(1){
fd = fopen("/sys/bus/w1/devices/28-0000085c3551/w1_slave", O_RDONLY);
if(fd != NULL){
perror("open device file error");
break;
}
while(1){
ret = fread(buffer, sizeof(char), BUFSIZE, fd);
if(0 == ret){
break;
}
if(-1 == ret){
if(errno == EINTR){
continue;
}
std::cout<<"Read Error";
fclose(fd);
break;
}
}
for(i=0;i<sizeof(buf);i++){
if(buf[i] == 't'){
for(j=0;j<sizeof(tempBuf);j++){
tempBuf[j] = buf[i+2+j];
}
}
}
temp = (float)atoi(tempBuf) / 1000;
std::cout<< "%.3f C\n" << temp;
fclose(fd);
//delay(500);
}
}
`
In compiling no errors occcurs .
it(stackoverflow site) kept on asking to to add more details i have no more details to add this is the only problem i am facing and the above code is compiling but not running .Hopefully some of you can help
The second argument to the fopen() call must be a string, in your case it should be "r" to open the file in read-only mode
fopen() returns NULL if the file couldn't be opened, and a non-NULL pointer otherwise; your code that checks if (fd != NULL) should check if (fd == NULL) instead
As pointed out in a comment to your question, the expression buf[i+2+j] could access a location outside the buf array
tempBuf isn't guaranteed to have a string terminator character in it, so when you call atoi(tempBuf) this function could access past the end of the tempBuf array

Can't invoke g++ with redirected stdout

I'm looking to invoke g++ and get the output. Here's my code:
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <iostream>
#include <boost/optional.hpp>
#include <vector>
#include <string>
namespace Util
{
template<typename T>
using optional = boost::optional<T>;
}
namespace Wide
{
namespace Driver
{
struct ProcessResult
{
std::string std_out;
int exitcode;
};
ProcessResult StartAndWaitForProcess(std::string name, std::vector<std::string> args, Util::optional<unsigned> timeout);
}
}
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <iostream>
#include <fcntl.h>
Wide::Driver::ProcessResult Wide::Driver::StartAndWaitForProcess(std::string name, std::vector<std::string> args, Util::optional<unsigned> timeout) {
int filedes[2];
pipe(filedes);
pid_t pid = fork();
if (pid == 0) {
while ((dup2(filedes[1], STDOUT_FILENO) == -1) && (errno == EINTR)) {}
auto fd = open("/dev/null", O_RDWR);
while ((dup2(fd, STDIN_FILENO) == -1) && (errno == EINTR)) {}
//freopen("/dev/null", "rw", stdin);
//freopen("/dev/null", "rw", stderr);
//close(filedes[1]);
close(filedes[0]);
std::vector<const char*> cargs;
cargs.push_back(name.c_str());
for (auto&& arg : args)
cargs.push_back(arg.c_str());
cargs.push_back(nullptr);
execv(name.c_str(), const_cast<char* const*>(&cargs[0]));
}
std::string std_out;
close(filedes[1]);
char buffer[4096];
while (1) {
ssize_t count = read(filedes[0], buffer, sizeof(buffer));
if (count == -1) {
if (errno == EINTR) {
continue;
} else {
perror("read");
exit(1);
}
} else if (count == 0) {
break;
} else {
std_out += std::string(buffer, buffer + count);
}
}
close(filedes[0]);
int status;
ProcessResult result;
result.std_out = std_out;
waitpid(pid, &status, 0);
if (!WIFEXITED(status))
result.exitcode = 1;
else {
result.exitcode = WEXITSTATUS(status);
if (result.exitcode != 0) {
std::cout << name << " failed with code " << result.exitcode << "\n";
}
}
return result;
}
int main()
{
auto r = Wide::Driver::StartAndWaitForProcess("g++", { "-std=c++14", "main.cpp" }, 150);
std::cout << r.std_out << "!!!!\n!!!!\n" << r.exitcode << "\n";
}
The output:
read: Bad file descriptor
g++ failed with code 1
!!!!
!!!!
1
Just invoke g++ main.cpp -std=c++14 && ./a.out.
I've used strace but it doesn't really give any more interesting details- the process runs, then fork/exec, then the above error. I can invoke other processes with the above code so I don't know what's so different about g++. I can invoke GCC with popen without problems so I don't know what's so different here.
The error here is really not very helpful. How can I invoke g++ and get the output?
The problem here is that you call execv which requires a full path to the executable as its first argument.
What you need is execvp which uses the contents of the PATH environment variable to find the executable, and thus only requires a name like g++.

Get list of alive IP on network (Linux)

I Have this nice small program which gives me a vector of all alive ip on the network to which system is connected...but works only on windows
std::vector<std::string> resultIPList;
PMIB_IPNET_TABLE2 pipTable = NULL;
unsigned long status = GetIpNetTable2(AF_INET, &pipTable);
if(status != NO_ERROR)
{
LOG_ERROR("Error in getting ip Table")
return resultIPList;
}
for(unsigned i = 0; i < pipTable->NumEntries; i++)
{
char* ip = inet_ntoa(pipTable->Table[i].Address.Ipv4.sin_addr);
std::string str = std::string(ip);
resultIPList.push_back(str);
}
FreeMibTable(pipTable);
pipTable = NULL;
return resultIPList;
is there any way i can do same in Linux (Replacement of GetIpNetTable Function). i am using RHEL
getifaddrs FTW
#include <ifaddrs.h>
#include <net/if.h>
#include <netinet/in.h>
#include <cstdlib>
ifaddrs* pAdapter = NULL;
ifaddrs* pList = NULL;
int result = getifaddrs(&pList);
if (result == -1)
return;
pAdapter = pList;
while (pAdapter)
{
if ((pAdapter->ifa_addr != NULL) && (pAdapter->ifa_flags & IFF_UP))
{
if (pAdapter->ifa_addr->sa_family == AF_INET)
{
sockaddr_in* addr = (sockaddr_in*)(pAdapter->ifa_addr);
}
else if (pAdapter->ifa_addr->sa_family == AF_INET6)
{
sockaddr_in6* addr6 = (sockaddr_in6*)(pAdapter->ifa_addr);
}
}
pAdapter = pAdapter->ifa_next;
}
freeifaddrs(pList);
pList = NULL;
I would take a look at rtnetlink and getifaddrs on RHEL there is also the NetworkManager all three should allow what you are looking for but I don't think either of the three is a direct replacement for the windows specific API. You will also have to have some way to crach your code so that the relevant functions are compiled. I normally use `#ifndef' to split the code out, that way you can have the same code returning the same things but utilising different code bodies dependant upon the target OS you are compiling for.
I recently answered another question where windows/linux compilation was an issue (for finding the runtime version of boost) here, the principle remains the same and can easily be tailored to your needs.
As an aside I suspect that Boost.Asio may help you out too for a single point of call in both Windows and Linux. You can also get it as an integrated-with-Boost version or as standalone to achieve a smaller footprint.
Let me know how you get on, or if you need any more information :)
Addendum
On Linux you could use something along the lines of:
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <ifaddrs.h>
#include <stdio.h>
void
print_sockaddr(struct sockaddr* addr,const char *name)
{
char addrbuf[128] ;
addrbuf[0] = 0;
if(addr->sa_family == AF_UNSPEC)
return;
switch(addr->sa_family) {
case AF_INET:
inet_ntop(addr->sa_family,&((struct sockaddr_in*)addr)->sin_addr,addrbuf,sizeof(addrbuf));
break;
case AF_INET6:
inet_ntop(addr->sa_family,&((struct sockaddr_in6*)addr)->sin6_addr,addrbuf,sizeof(addrbuf));
break;
default:
sprintf(addrbuf,"Unknown (%d)",(int)addr->sa_family);
break;
}
printf("%-16s %s\n",name,addrbuf);
}
void
print_ifaddr(struct ifaddrs *addr)
{
char addrbuf[128] ;
addrbuf[0] = 0;
printf("%-24s %s\n","Interface Name:",addr->ifa_name);
if(addr->ifa_addr != NULL)
print_sockaddr(addr->ifa_addr,"Interface Address:");
if(addr->ifa_netmask != NULL)
print_sockaddr(addr->ifa_netmask,"Netmask:");
if(addr->ifa_broadaddr != NULL)
print_sockaddr(addr->ifa_broadaddr,"Broadcast address:");
if(addr->ifa_dstaddr != NULL)
print_sockaddr(addr->ifa_dstaddr,"Peer address:");
puts("");
}
int main(int argc,char *argv[])
{
struct ifaddrs *addrs,*tmp;
if(getifaddrs(&addrs) != 0) {
perror("getifaddrs");
return 1;
}
for(tmp = addrs; tmp ; tmp = tmp->ifa_next) {
print_ifaddr(tmp);
}
freeifaddrs(addrs);
return 0;
}
For other derivatives (Unix, BSD etc) this more basic set of code may be more usefult, though I vaguely remember some problems under BSD (Sorry cant remember what exactly at the moment, think its to do with fail scenarios where certain conditions will render it non-operational)
#include <stdio.h>
#include <net/if.h>
#include <netinet/in.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>
#define INT_TO_ADDR(_addr) \
(_addr & 0xFF), \
(_addr >> 8 & 0xFF), \
(_addr >> 16 & 0xFF), \
(_addr >> 24 & 0xFF)
int main()
{
struct ifconf ifc;
struct ifreq ifr[10];
int sd, ifc_num, addr, bcast, mask, network, i;
// Create a socket then use ioctl on the file descriptor to retrieve the interface information.
sd = socket(PF_INET, SOCK_DGRAM, 0);
if (sd > 0)
{
ifc.ifc_len = sizeof(ifr);
ifc.ifc_ifcu.ifcu_buf = (caddr_t)ifr;
if (ioctl(sd, SIOCGIFCONF, &ifc) == 0)
{
ifc_num = ifc.ifc_len / sizeof(struct ifreq);
printf("%d interfaces found.\n", ifc_num);
for (i = 0; i < ifc_num; ++i)
{
if (ifr[i].ifr_addr.sa_family != AF_INET)
{
continue;
}
/* display the interface name */
printf("%d) Interface Name: %s\n", i+1, ifr[i].ifr_name);
/* Retrieve the IP address, broadcast address, and subnet mask. */
if (ioctl(sd, SIOCGIFADDR, &ifr[i]) == 0)
{
addr = ((struct sockaddr_in *)(&ifr[i].ifr_addr))->sin_addr.s_addr;
printf("%d) Interface address: %d.%d.%d.%d\n", i+1, INT_TO_ADDR(addr));
}
if (ioctl(sd, SIOCGIFBRDADDR, &ifr[i]) == 0)
{
bcast = ((struct sockaddr_in *)(&ifr[i].ifr_broadaddr))->sin_addr.s_addr;
printf("%d) Broadcast: %d.%d.%d.%d\n", i+1, INT_TO_ADDR(bcast));
}
if (ioctl(sd, SIOCGIFNETMASK, &ifr[i]) == 0)
{
mask = ((struct sockaddr_in *)(&ifr[i].ifr_netmask))->sin_addr.s_addr;
printf("%d) Netmask: %d.%d.%d.%d\n", i+1, INT_TO_ADDR(mask));
}
/* Compute the current network value from the address and netmask. */
network = addr & mask;
printf("%d) Current Network: %d.%d.%d.%d\n", i+1, INT_TO_ADDR(network));
}
}
close(sd);
}
return 0;
}
Obviously you would need to tailor the code to suit your needs, but perhaps this will help a bit:)

Why is MD5Sum so fast

I've been studying hashing in C/C++ and tried to replicate the md5sum command in Linux. After analysing the source code, it seems that md5sum relies on the md5 library's md5_stream. I've approximated the md5_stream function from the md5.h library into the code below, and it runs in ~13-14 seconds. I've tried to call the md5_stream function directly and got ~13-14 seconds. The md5sum runs in 4 seconds. What have the GNU people done to get the speed out of the code?
The md5.h/md5.c code is available in the CoreUtils source code.
#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>
#include <iostream>
#include <iomanip>
#include <fstream>
#include "md5.h"
#define BLOCKSIZE 32784
int main()
{
FILE *fpinput, *fpoutput;
if ((fpinput = fopen("/dev/sdb", "rb")) == 0) {
throw std::runtime_error("input file doesn't exist");
}
struct md5_ctx ctx;
size_t sum;
char *buffer = (char*)malloc (BLOCKSIZE + 72);
unsigned char *resblock = (unsigned char*)malloc (16);
if (!buffer)
return 1;
md5_init_ctx (&ctx);
size_t n;
sum = 0;
while (!ferror(fpinput) && !feof(fpinput)) {
n = fread (buffer + sum, 1, BLOCKSIZE - sum, fpinput);
if (n == 0){
break;
}
sum += n;
if (sum == BLOCKSIZE) {
md5_process_block (buffer, BLOCKSIZE, &ctx);
sum = 0;
}
}
if (n == 0 && ferror (fpinput)) {
free (buffer);
return 1;
}
/* Process any remaining bytes. */
if (sum > 0){
md5_process_bytes (buffer, sum, &ctx);
}
/* Construct result in desired memory. */
md5_finish_ctx (&ctx, resblock);
free (buffer);
for (int x = 0; x < 16; ++x){
std::cout << std::setfill('0') << std::setw(2) << std::hex << static_cast<uint16_t>(resblock[x]);
std::cout << " ";
}
std::cout << std::endl;
free(resblock);
return 0;
}
EDIT: Was a default mkspec problem in Fedora 19 64-bit.
fread() is convenient, but don't use fread() if you care about performance. fread() will copy from the OS to a libc buffer, then to your buffer. This extra copying cost CPU cycles and cache.
For better performance use open() then read() to avoid the extra copy. Make sure your read() calls are multiples of the block size, but lower than your CPU cache size.
For best performance use mmap() map the disk directly to RAM.
If you try something like the below code, it should go faster.
// compile gcc mmap_md5.c -lgcrypt
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <gcrypt.h>
#include <linux/fs.h> // ioctl
#define handle_error(msg) \
do { perror(msg); exit(EXIT_FAILURE); } while (0)
int main(int argc, char *argv[])
{
char *addr;
int fd;
struct stat sb;
off_t offset, pa_offset;
size_t length;
ssize_t s;
unsigned char digest[16];
char digest_ascii[32+1] = {0,};
int digest_length = gcry_md_get_algo_dlen (GCRY_MD_MD5);
int i;
if (argc < 3 || argc > 4) {
fprintf(stderr, "%s file offset [length]\n", argv[0]);
exit(EXIT_FAILURE);
}
fd = open(argv[1], O_RDONLY);
if (fd == -1)
handle_error("open");
if (fstat(fd, &sb) == -1) /* To obtain file size */
handle_error("fstat");
offset = atoi(argv[2]);
pa_offset = offset & ~(sysconf(_SC_PAGE_SIZE) - 1);
if (sb.st_mode | S_IFBLK ) {
// block device. use ioctl to find length
ioctl(fd, BLKGETSIZE64, &length);
} else {
/* offset for mmap() must be page aligned */
if (offset >= sb.st_size) {
fprintf(stderr, "offset is past end of file size=%zd, offset=%d\n", sb.st_size, (int) offset);
exit(EXIT_FAILURE);
}
if (argc == 4) {
length = atoi(argv[3]);
if (offset + length > sb.st_size)
length = sb.st_size - offset;
/* Canaqt display bytes past end of file */
} else { /* No length arg ==> display to end of file */
length = sb.st_size - offset;
}
}
printf("length= %zd\n", length);
addr = mmap(NULL, length + offset - pa_offset, PROT_READ,
MAP_PRIVATE, fd, pa_offset);
if (addr == MAP_FAILED)
handle_error("mmap");
gcry_md_hash_buffer(GCRY_MD_MD5, digest, addr + offset - pa_offset, length);
for (i=0; i < digest_length; i++) {
sprintf(digest_ascii+(i*2), "%02x", digest[i]);
}
printf("hash=%s\n", digest_ascii);
exit(EXIT_SUCCESS);
}
It turned out to be an error in the Qt mkspecs regarding an optimization flag not being set properly.

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.