6.3.1 Network Programming in Linux
The following listings show network usage in Linux. The general method of network connection is different for servers and clients.
For servers.....
For clients....
Listing X.1 - network_io.cpp
#include "network_io.h"
#include <sys/poll.h>
#include <sys/ioctl.h>
#include <linux/tcp.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
network_io::network_io(){
type = _READ;
level = 0;
}
network_io::~network_io(){
if(type == _READ){
if(level > 1){
if(level > 2){
end_read_connection();
}
deinit_read();
}
} else if (type == _WRITE){
}
}
int network_io::set_remote_host(char *_host_name, int host_socket){
static int error;
static int nm_a,
nm_b,
nm_c,
nm_d;
struct hostent *hp;
unsigned char address[4];
error = NO_ERROR;
strcpy(host_name, _host_name);
host_socket_number = host_socket;
// Set up server descriptor, get host reference and error trap
write_connection.sin_family = AF_INET;
if((host_name[0] > ’9’) ||(host_name[0]<’0’)){
hp = gethostbyname(host_name);
} else {
sscanf(host_name, "%d.%d.%d.%d",
&nm_a, &nm_b, &nm_c, &nm_d);
address[0] = (unsigned char)(nm_a);
address[1] = (unsigned char)(nm_b);
address[2] = (unsigned char)(nm_c);
address[3] = (unsigned char)(nm_d);
hp = gethostbyaddr((char *)address, 4, AF_INET);
}
if(hp != 0){
/* complete descriptor set up. */
bcopy((char *)hp->h_addr,
(char *)&(write_connection.sin_addr),
hp->h_length);
} else {
error_log(MINOR, "ERROR: unknown network host");
error = ERROR;
}
return error;
}
int network_io::set_local_host(int socket_num){
static int error;
error = NO_ERROR;
socket_number = socket_num;
return error;
}
int network_io::writer(char *string){
static int error;
error = NO_ERROR;
// Open a new socket for the write, and check for errors.
error = init_write();
if(error == NO_ERROR) error = open_write_connection();
if(error == NO_ERROR) error = write_to_connection(string);
if(error == NO_ERROR) error = end_write_connection();
if(deinit_write() == ERROR) error = ERROR;
return(error);
}
int network_io::reader(char *buf, int length){
static int error; // Error return variable
error = NO_ERROR;
// Wait for a socket connection request, then get its reference.
buf[0] = 0;
if(wait_read_connection() == NO_ERROR){
if(read_from_connection(buf, length) == ERROR){
}
// Close socket connection to remote write client.
end_read_connection();
}
return(error);
}
int network_io::init_write(){
static int error;
struct hostent *gethostbyname();
error = NO_ERROR;
/* Open a new socket for the write, and check for errors. */
if((rw_socket = socket(AF_INET, SOCK_STREAM, 0)) >= 0){
write_connection.sin_port = htons(host_socket_number);
} else {
error_log(MINOR, "ERROR: opening stream socket");
error = ERROR;
}
return error;
}
int network_io::open_write_connection(){
static int error;
error = NO_ERROR;
if(connect(rw_socket, (struct sockaddr *) &(write_connection), sizeof(write_connection)) < 0){
error = ERROR;
error_log(MINOR, "ERROR: Connecting stream Socket");
}
return error;
}
int network_io::write_to_connection(char *text){
static int error;
error = NO_ERROR;
if(write(rw_socket, text, strlen(text) /* +1 */) < 0){
error_log(MINOR, "ERROR: writing on stream socket");
error = ERROR;
}
return error;
}
int network_io::write_stuff_done(){
int error;
error = NO_ERROR;
return error;
}
int network_io::check_connection(){
int error;
int count;
struct pollfd ufds;
error = NO_ERROR;
ufds.fd = rw_socket;
ufds.events = POLLOUT | POLLIN | POLLPRI | POLLERR | POLLHUP | POLLNVAL;
count = poll(&ufds, 1, 0);
if((ufds.revents & 16) != 0) error = ERROR;
return error;
}
int network_io::end_write_connection(){
static int error;
error = NO_ERROR;
return error;
}
int network_io::deinit_write(){
static int error;
error = NO_ERROR;
close(rw_socket);
rw_socket = ANY;
return error;
}
int network_io::init_read(){
static int error; // low level socket number
unsigned length; // temporary work variable
static struct sockaddr_in server; // read socket descriptor
static struct hostent *hp;
char text[100];
// Open internet socket, and check for error.
error = ERROR;
gethostname(text, 100); /* who are we? */
hp = gethostbyname(text);
if((hp != NULL) && (read_socket = socket(AF_INET, SOCK_STREAM, 0)) >= 0){
// Set up server descriptor for binding name.
memset(&server, 0, sizeof(struct sockaddr_in));
server.sin_family = hp->h_addrtype;
server.sin_port = htons(socket_number);
// Bind the socket, and check for error.
level = 1;
int flag = 1;
setsockopt(read_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&flag, sizeof(int));
if(bind(read_socket, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) >= 0){
// Check for valid socket binding
length = sizeof(server);
if(getsockname(read_socket, (struct sockaddr *)&server, &length) >= 0){
error = NO_ERROR;
// Set up variables for success
// Zero because anything higher would allow
// messages to arrive out of sequence.
listen(read_socket, 0);
} else {
error_log(MINOR, "ERROR: getting socket name");
}
} else {
error_log(MINOR, "ERROR: binding stream socket");
}
} else {
error_log(MINOR, "ERROR: opening stream socket");
}
return(error);
}
int network_io::read_stuff_waiting(){
int error,
count;
struct pollfd ufds;
error = ERROR;
ufds.fd = read_socket;
ufds.events = POLLIN | POLLPRI | POLLOUT;
count = poll(&ufds, 1, 0);
if((ufds.revents & 1) > 0){
error = NO_ERROR;
}
return error;
}
int network_io::wait_read_connection(){
static int error;
unsigned size;
static struct sockaddr addr;
error = NO_ERROR;
size = sizeof(struct sockaddr);
fcntl(read_socket, F_SETFL, O_NONBLOCK);
rw_socket = accept(read_socket, &addr, &size);
level = 2;
if(rw_socket < 0){
error = ERROR;
// error_log("ERROR: warning:accept");
}
return error;
}
int network_io::read_from_connection(char *buf, int length){
int error;
int len;
error = NO_ERROR;
// Empty input buffer
buf[0] = 0;
// Read string into buffer from socket
fcntl(rw_socket, F_SETFL, O_NONBLOCK);
len = read(rw_socket, buf, length);
if(len < 0){
// error_log("ERROR: reading stream message");
// error = ERROR;
if(errno != 11){
printf("errno=%d ", errno);
}
} else {
buf[len] = 0;
}
return error;
}
int network_io::end_read_connection(){
int error;
int a;
error = NO_ERROR;
a = close(rw_socket);
level = 1;
return error;
}
int network_io::deinit_read(){
int error;
int a;
error = NO_ERROR;
a = close(read_socket);
level = 0;
return error;
}
char *network_io::get_address(){
static char work[MAXIMUM_HOST_NAME_LENGTH];
struct sockaddr_in address;
int i,
addr[4];
long int_address;
#ifndef SGI
// Sun Version
get_myaddress(&address);
int_address = address.sin_addr.s_addr;
#else
// SGI Version
int_address = gethostid();
#endif
// SUN & SGI version
for(i = 0; i < 4; i++){
addr[i]=int_address & 0xFF;
int_address >>= 8;
}
#ifdef OTHER_UNIX
sprintf(work, "%d.%d.%d.%d",
(int)addr[3], (int)addr[2], (int)addr[1], (int)addr[0]);
#else
// This is for linux
sprintf(work, "%d.%d.%d.%d",
(int)addr[0], (int)addr[1], (int)addr[2], (int)addr[3]);
#endif
return work;
}
char *network_io::get_remote_client(){
staticchar work[100];
struct sockaddr address;
socklen_t len;
len = sizeof(address);
if(getpeername(rw_socket, &address, &len) == 0){
sprintf(work, "%u.%u.%u.%u", address.sa_data[2], address.sa_data[3], address.sa_data[4], address.sa_data[5]);
// printf("Got address [%s]\n", work);
//strcpy(work, address);
} else {
strcpy(work, "unknown network address");
}
return work;
}
Listing X.2 - network_io.h
#ifndef _NETWORK_IO
#define _NETWORK_IO
#include <errno.h>
#include <linux/socket.h> // sys/socket.h may be needed here
#include <sys/types.h>
#include <netdb.h>
#include <rpc/rpc.h>
#include <unistd.h>
#define ANY 0 // Indicates no socket number prechosen
#define MAXIMUM_HOST_NAME_LENGTH 100
class network_io{
public:
int socket_number;
int rw_socket;
// int read_connection;
char host_name[MAXIMUM_HOST_NAME_LENGTH];
int host_socket_number;
int read_socket;
struct sockaddr_in write_connection;
// char incoming_string[MAXIMUM_STRING_SIZE];
int type;
int level;
#define _READ 200
#define _WRITE 201
network_io();
~network_io();
int set_remote_host(char*, int);
int set_local_host(int);
int reader(char*, int);
int writer(char*);
int init_write();
int open_write_connection();
int write_to_connection(char*);
// int write_to_read_connection(char*);
int end_write_connection();
int deinit_write();
int init_read();
int wait_read_connection();
int read_from_connection(char*, int);
// int read_from_write_connection(char*, int);
int end_read_connection();
int deinit_read();
int read_stuff_waiting();
int write_stuff_done();
int check_connection();
char* get_remote_client();
char *get_address();
};
#endif
Listing X.3 - network.cpp
#include "../network_io/network_io.h"
#include <time.h>
network_io *network;
int mode;
char *params;
char received_temp[200];
int connect_flag;
time_t now_time;
time_t last_time;
int timeout;
int main(){
timeout = 5; // the default timeout
return 1;
}
int deinit(){
int error;
error = NO_ERROR;
if(network != NULL) delete network;
return error;
}
int process_command();
int check_network();
int step(){
int error;
error = NO_ERROR;
error = check_network();
if(error == NO_ERROR) error = process_command();
return error;
}
int check_network(){
int error;
error = NO_ERROR;
if(*state == WAITINGFORCONNECTION){
if(connect_flag == TRUE){
if(mode == LISTENER){
if(network->wait_read_connection() == NO_ERROR){
char text[200];
*state = CONNECTIONESTABLISHED;
*received_flag = FALSE;
*send_flag = FALSE;
sprintf(text, "Got a connection from %s", network->get_remote_client());
error_log(WARNING, text);
time(&last_time);
}
} else if(mode == TALKER){
if(network->open_write_connection() == NO_ERROR){
*state = CONNECTIONESTABLISHED;
*received_flag = FALSE;
*send_flag = FALSE;
time(&last_time);
}
}
}
} else if(*state == CONNECTIONESTABLISHED){
if(*send_flag == TRUE){
network->write_to_connection(send_buf);
// printf("Sending String [%s]\n", send_buf);
send_buf[0] = 0;
*send_flag = FALSE;
// time(&last_time); // You can keep the connection alive by writing
// but this doesn’t guarantee that the remote client is still there
}
if(*received_flag == FALSE){
if(network->read_from_connection(received_temp, 199) != ERROR){
if(strlen(received_temp) > 0){
strcpy(received, received_temp);
*received_flag = TRUE;
time(&last_time);
// printf("Got String [%s]\n", received);
}
}
}
time(&now_time);
if((network->check_connection() == ERROR) || ((mode == LISTENER) && (difftime(now_time, last_time) > timeout))){
if(mode == LISTENER){
network->end_read_connection();
} else if(mode == TALKER){
network->end_write_connection();
}
*state = WAITINGFORCONNECTION;
connect_flag = FALSE;
}
}
return error;
}
int process_command(){
int error;
int i, len;
int port;
error = NO_ERROR;
if(*change_lock == TRUE){
if(*command == INITIALIZE){
if(*state == NOTINITIALIZED){
if(mode == LISTENER){
port = atoi(params);
if(port > 0){
network = new network_io();
network->set_local_host(port);
network->init_read();
//now ready to listen
*state = WAITINGFORCONNECTION;
connect_flag = FALSE;
} else {
error_log(MINOR, "Parameter did not hold a valid port");
*error_flag = ERROR;
}
} else if(mode == TALKER){
len = strlen(params);
for(i = 0; i < len; i++){
if(params[i] == ’:’){
params[i] = 0;
port = atoi(&(params[i+1]));
break;
}
}
if((i < len) && (port > 0)){
network = new network_io();
network->set_remote_host(params, port);
network->init_write();
*state = WAITINGFORCONNECTION;
connect_flag = FALSE;
} else {
error_log(MINOR, "Address string was not properly formed");
*error_flag = ERROR;
}
} else {
error_log(MINOR, "ERROR: Mode not defined yet");
*error_flag = ERROR;
}
} else {
error_log(MINOR, "Network talker initialize command in wrong state");
*error_flag = ERROR;
}
} else if(*command == CONNECT){
if(*state == WAITINGFORCONNECTION){
connect_flag = TRUE;
}
} else if(*command == DISCONNECT){
if(*state == CONNECTIONESTABLISHED){
if(mode == TALKER){
network->end_write_connection();
} else if (mode == LISTENER){
network->end_read_connection();
}
*state = WAITINGFORCONNECTION;
connect_flag = FALSE;
} else {
error_log(MINOR, "Cannot disconnect network unless connected");
*error_flag = ERROR;
}
} else if(*command == UNINITIALIZE){
if(*state == WAITINGFORCONNECTION){
if(mode == TALKER){
network->deinit_write();
} else if (mode == LISTENER){
network->deinit_read();
}
delete network;
network = NULL;
*state = NOTINITIALIZED;
} else {
error_log(MINOR, "Cannot uninitialize network unless waitingforconnection");
*error_flag = ERROR;
}
} else if(*command == SET){
if(*operand1 == MODE){
if(*state == NOTINITIALIZED){
mode = *operand2;
} else {
error_log(MINOR, "Can’t set network mode after initialization");
*error_flag = ERROR;
}
} else if(*operand1 == PARAM){
if(*state == NOTINITIALIZED){
if(params != NULL) delete params;
params = new char[strlen(operand3)+1];
strcpy(params, operand3);
} else {
error_log(MINOR, "Can’t set network parameters, in wrong state");
*error_flag = ERROR;
}
} else if(*operand1 == TIMEOUT){
timeout = *operand2;
} else {
error_log(MINOR, "Network SET type not recognized");
*change_lock = FALSE;
*error_flag = ERROR;
}
} else {
error_log(MINOR, "Network command not recognized");
*error_flag = ERROR;
}
}
*change_lock = FALSE;
return error;
}
To use the network program as a server the basic flow of control is outlined below,
char temp_in[200]; // an input string
char temp_out[200]; // an output string
// set up the network connection
network_io test = new network_io(); // create a new instance of network
test->set_local_host(1000); // set the program to listen on port 1000
point1: test->init_read(); // start waiting for a client to connect
point2: val = test->wait_read_connection(); // check for a connection
if(val == ERROR) goto point2;
val = test->check_connection();
if(val == ERROR){
test->end_read_connection();
goto point1;
}
// now connected, interact with client
point3: val = test->read_from_connection(temp_in, 199); // get an input string
if(val == NO_ERROR){
// do anything needed for the received string
}
// do anything for writing strings here
if(there is a string to send){
test->write_to_connection(temp_out);
}
goto point3;
A makefile for the program follows.
all: network
CC=c++
CFLAGS=
network: network.cpp
$(CC) $(CFLAGS) network.cpp -o network network_io.o
network_io.o: network_io.cpp network_io.h
$(CC) $(CFLAGS) -c network_io.cpp
6.4 Summary
• Networks come in a variety of topologies, but buses are most common on factory floors.
• The OSI model can help when describing network related hardware and software.
• Networks can be connected with a variety of routers, bridges, gateways, etc.
• Ethernet is common, and can be used for high speed communication.
• The internet can be use to monitor and control shop floor activities.
6.5 Practice Problems
1. Explain why networks are important in manufacturing controls.
(ans. These networks allow us to pass data between devices so that individually controlled systems can be integrated into a more complex manufacturing facility. An example might be a serial connection to a PLC so that SPC data can be collected as product is made, or recipes downloaded as they are needed.)
2. Is the OSI model able to describe all networked systems?
(ans. The OSI model is just a model, so it can be used to describe parts of systems, and what their functions are. When used to describe actual networking hardware and software, the parts may only apply to one or two layers. Some parts may implement all of the layers in the model.)
3. What are the different methods for resolving collisions on a bus network?
(ans. When more than one client tries to start talking simultaneously on a bus network they interfere, this is called a collision. When this occurs they both stop, and will wait a period of time before starting again. If they both wait different amounts of time the next one to start talking will get priority, and the other will have to wait. With CSMA/CD the clients wait a random amount of time. With CSMA/BA the clients wait based upon their network address, so their priority is related to their network address. Other networking methods prevent collisions by limiting communications. Master-slave networks require that client do not less talk, unless they are responding to a request from a master machine. Token passing only permits the holder of the token to talk.)
6.6 Laboratory - Networking
Purpose: To expose you to the architecture and components of a modern computer network.
Objectives: To be able to set up a switch and computers to communicate over an Intranet network.
Background:
Computers can be connected via a network. At a minimum this requires a network card in at least two computers and a connecting cable between them. These computers can then pass packets of information back and forth for basic communication. This type of connection is commonly used by people playing games such as Quake at home. A more mature network, like that found in a factory, must be more sophistocated.
The most fundamental concept in a network is the data packets and the protocol for exchanging them. The current Internet protocol is called IPV4 (this will be replaced by IPV6 in the near future). In this protocol each client on a network has a 4 byte (0-255) address, normally shown in the form ‘aaa.bbb.ccc.ddd’. In our case the university is a ‘class B’, so it owns all addresses that start with ‘148.61.ccc.ddd’. Most engineering students use a ‘class C’ network with the addresses ‘148.61.104.ddd’. In theory there are up to 256 clients on the engineering network. In practice some of these addresses are used for network housekeeping. For example the following addresses are used,
148.61.104.1 - this is the router/switch to other networks
148.61.104.254 - this is the gateway to other networks
If a network address is not used, it can be used by a normal network device, such as a computer or printer. There are two ways to assign these statically or dynamically. In a static connection the address can only be used by one machine. In a dynamic connection the addresses are assigned and release semi-randomly to network clients as the connect and disconnect from the network. Static IP addresses are primarily designed for computers that are always on, and are acting as servers on the network. Dynamic IP addresses are primarily used for computers that are only clients on the network. Some examples of static IP addresses on the network are:
148.61.104.215 - claymore.engineer.gvsu.edu
148.61.104.226 - excalibur.engineer.gvsu.edu
148.61.104.??? - falcon.engineer.gvsu.edu
etc..
Most computers on the network are named, such as ‘gvsu.edu’. When a user enters this name into the computer, it must be converted to a network number. This is done by a ‘domain name server’ (DNS). There are two DNS servers at GVSU (148.61.1.10 and 148.61.1.15). These servers keep all of the names for computers at GVSU, and also provide links to computers at other sites so that their names and numbers can also be searched.
Although the engineering student network is ‘148.61.104.ddd’, it is actually a collection of smaller networks (sub-nets). The smaller networks are connected together with network devices called switches/hubs/routers. These are basically small computers with multiple network connections (often 24). Each computer is connected to the hub. The hub then looks at each network packet coming from a computer. If it is going to another computer connected to the same hub, it will be sent there directly. Otherwise it is sent ‘up-stream’ to a router that will send it to another sub-net if it is available, or up upstream again. In our lab we will use a Linksys 10/100 Managed 24-Port GigaSwitch (EG24M).
The Switch in the lab is used to connect computers together, and connect to a network gateway. ....
The server for the lab is.....
Network Components:
Lab Server - Claymore
Dell Poweredge 1300 Server
Intel Pro/100 network card (internet side)
3Com 590 network card (infranet side)
Adaptec SCSI card 2940U2W (2 cards) for SCSI hard disks
Tape drive -
Uninterruptable Power Supply - APC Smart-UPS 120V OL103
Firewall....