/ Forside / Teknologi / Udvikling / C/C++ / Nyhedsindlæg
Login
Glemt dit kodeord?
Brugernavn

Kodeord


Reklame
Top 10 brugere
C/C++
#NavnPoint
BertelBra.. 2425
pmbruun 695
Master_of.. 501
jdjespers.. 500
kyllekylle 500
Bech_bb 500
scootergr.. 300
gibson 300
molokyle 287
10  strarup 270
Programmering af telnet klient
Fra : Morton Christiansen


Dato : 24-04-02 18:31

Jeg er igang med at lave en simpel telnet klient, men er rendt ind
hele 3 problemer, som jeg håber at kunne få lidt feed-back på her i
gruppen:

1. Nedennævnte kode kan godt returnere noget velformet tekst under
Suse, men under Red Hat går den amok og udskriver kun enkelte
underlige tegn!

2. getch() er i mit system sat til først at registere et tegn efter
tryk på return. Udover at dette er irriterende betyder det også at man
ikke kan sende tegnet return til serveren. Hvordan kan man på en
system uafhængig måde sikre at systemet altid læser et tegn straks man
trykker på tastaturet?

3. Af ukendte årsager kommer output fra serveren et par linier
forsinkede, og først efter tryk på return el. hvis man i ens kode har
lavet printf("\n") - but why?!

På forhånd tak for enhver hjælp!

-- Morton



#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

int main()
{
struct sockaddr_in address;
int result, nread, len, sockfd;
fd_set inputs, fdTest;
char ch;
struct timeval timeout;

FD_ZERO(&inputs);
FD_SET(0, &inputs);

/* Create a socket for the client. */

sockfd = socket(AF_INET, SOCK_STREAM, 0);

/* Name the socket, as agreed with the server. */

address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr("192.168.1.1");
address.sin_port = htons(23);
len = sizeof(address);

/* Now connect our socket to the server's socket. */

result = connect(sockfd, (struct sockaddr *)&address, len);
FD_SET(sockfd, &inputs);

if(result == -1) {
perror("oops: client");
exit(1);
}

/* We can now read/write via sockfd. */

while(1){
fdTest = inputs;
result = select(FD_SETSIZE, &fdTest, (fd_set *)NULL, (fd_set
*)NULL, (struct timeval *) 0);
if(result == -1) {
perror("select");
exit(1);
}

if(FD_ISSET(0, &fdTest)) { // input fra tastatur
ioctl(0, FIONREAD, &nread);
if(nread == 0) {
close(sockfd);
exit(0);
}
ch = getchar();
// read(0, &ch, nread);
write(sockfd, &ch, 1);
}
else { // input fra socket
ioctl(sockfd, FIONREAD, &nread);
if(nread == 0) {
close(sockfd);
exit(0);
}
read(sockfd, &ch, 1);
printf("%c", ch);
}
}
}
)

 
 
Kent Friis (24-04-2002)
Kommentar
Fra : Kent Friis


Dato : 24-04-02 18:56

Den 24 Apr 2002 10:30:40 -0700 skrev Morton Christiansen:
>Jeg er igang med at lave en simpel telnet klient, men er rendt ind
>hele 3 problemer, som jeg håber at kunne få lidt feed-back på her i
>gruppen:
>
>1. Nedennævnte kode kan godt returnere noget velformet tekst under
>Suse, men under Red Hat går den amok og udskriver kun enkelte
>underlige tegn!

Hvad skal dine ioctl()s gøre godt for?

>2. getch() er i mit system sat til først at registere et tegn efter
>tryk på return. Udover at dette er irriterende betyder det også at man
>ikke kan sende tegnet return til serveren. Hvordan kan man på en
>system uafhængig måde sikre at systemet altid læser et tegn straks man
>trykker på tastaturet?

Det kan man ikke.

Du kan bruge tcgetattr(), tcsetattr og cfmakeraw(), men så er du ikke
længere systemuafhængig. De virker nemlig kun på *nix.

>3. Af ukendte årsager kommer output fra serveren et par linier
>forsinkede, og først efter tryk på return el. hvis man i ens kode har
>lavet printf("\n") - but why?!

Du mangler fflush()

Mvh
Kent
--
Object orientation: the idea, that humans find it easier to understand
"you.car.engine.start" than "start your car engine".

Morton P. Christians~ (26-04-2002)
Kommentar
Fra : Morton P. Christians~


Dato : 26-04-02 02:00

Først og fremmest tak for gode pointers i den rigtige retning.

Status for telnet klienten er nu at den mod visse telnet servere virker helt
perfekt. Til gengæld er der forsat problemer med at den mod andre blot
udskriver enkelte underlige tegn (ÿ i særdeleshed) og derefter låser.
Problemet har således vist sig at afhænge af hvilke servere programmet er
oppe mod, og ikke det anvendte operativ system som jeg først antog.

> Hvad skal dine ioctl()s gøre godt for?
Disse anvendes i programmet blot for at checke om den socket man
kommunikerer med har lagt på/"EOF" fra keyboard. Har prøvet at udkommenterer
dem, men gør ingen forskel mht. omtalte problem. Faktisk har jeg prøvet at
udkommentere stort set alt undtagen den read kommando der læser fra socket,
og problemet er der stadig! Har prøvet på både Red Hat og Suse med samme
resultat.

Anyone???

-- Morton

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <stdio.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <termios.h>

void init_terminal(FILE *);
struct termios initial_settings, new_settings;

int main(int argc, char *argv[])
{
FILE *input;
struct sockaddr_in address;
int result, nread, len, sockfd;
fd_set inputs, fdTest;
char ch;

if(argc != 2){
printf("Usage: %s ip_address\n", argv[0]);
exit(1);
}

input = fopen("/dev/tty", "r");
if(!input) {
perror("Unable to open /dev/tty\n");
exit(1);
}

tcgetattr(fileno(input),&initial_settings); // backup terminal settings

FD_ZERO(&inputs);
FD_SET(0, &inputs);

sockfd = socket(AF_INET, SOCK_STREAM, 0); // create TCP/IP socket

/* Make connection according to stated protocol, remote IP and port */
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr(argv[1]);
address.sin_port = htons(23);
len = sizeof(address);
result = connect(sockfd, (struct sockaddr *)&address, len);

FD_SET(sockfd, &inputs);

if(result == -1) {
perror("connect");
exit(1);
}

init_terminal(input);

while(1){
fdTest = inputs;
result = select(FD_SETSIZE, &fdTest, (fd_set *)NULL, (fd_set *)NULL,
(struct timeval *) 0);
if(result == -1) {
perror("select");
exit(1);
}

if(FD_ISSET(0, &fdTest)) { // on event from keyboard
ioctl(0, FIONREAD, &nread);
if(nread == 0) { // clean up on closure
close(sockfd);
tcsetattr(fileno(input),TCSANOW,&initial_settings);
exit(0);
}
ch = fgetc(input);
if(ch == 10) // convert internal LF to CR
ch = 13;
write(sockfd, &ch, 1);
}
else { // on event from socket
ioctl(sockfd, FIONREAD, &nread);
if(nread == 0) {
close(sockfd);
tcsetattr(fileno(input),TCSANOW,&initial_settings);
exit(0);
}
read(sockfd, &ch, 1);
printf("%c", ch);
fflush(NULL); // flush on each character read
}
}
}

void init_terminal(FILE *input){
new_settings = initial_settings;
new_settings.c_lflag &= ~ICANON; // go to non-canonical mode
new_settings.c_lflag &= ~ECHO; // turn local echo off

/* return characters immediately */
new_settings.c_cc[VMIN] = 0;
new_settings.c_cc[VTIME] = 0;

if(tcsetattr(fileno(input), TCSANOW, &new_settings) != 0) {
fprintf(stderr,"could not set attributes\n");
}
}




Kent Friis (26-04-2002)
Kommentar
Fra : Kent Friis


Dato : 26-04-02 08:30

Den Fri, 26 Apr 2002 03:00:22 +0200 skrev Morton P. Christiansen:
>Først og fremmest tak for gode pointers i den rigtige retning.
>
>Status for telnet klienten er nu at den mod visse telnet servere virker helt
>perfekt. Til gengæld er der forsat problemer med at den mod andre blot
>udskriver enkelte underlige tegn (ÿ i særdeleshed) og derefter låser.
>Problemet har således vist sig at afhænge af hvilke servere programmet er
>oppe mod, og ikke det anvendte operativ system som jeg først antog.

Aha, nu forstår jeg problemet. Prøv at bruge port 25 (SMTP) i stedet
for.

Det du løber ind i er sandsynligvis telnet negotiation - serveren
spørger dit program hvad den kan. Prøv at kigge efter telnet rfc'en,
hvordan den protokol fungerer.

>> Hvad skal dine ioctl()s gøre godt for?
>Disse anvendes i programmet blot for at checke om den socket man
>kommunikerer med har lagt på/"EOF" fra keyboard.

fgetc() returnerer -1 ved EOF, og read() returnerer 0. Disse er mere
portable end ioctl(), og folk har nemmere ved at gennemskue hvad
programmet gør-

Linier uden ">" er ændringsforslag:

>int main(int argc, char *argv[])
>{
> FILE *input;
> struct sockaddr_in address;
> int result, nread, len, sockfd;
> fd_set inputs, fdTest;
int ch; /* fgetc returns int */
>
> if(argc != 2){
> printf("Usage: %s ip_address\n", argv[0]);
> exit(1);
> }
>
> input = fopen("/dev/tty", "r");
> if(!input) {
> perror("Unable to open /dev/tty\n");
> exit(1);
> }

hvorfor bruger du ikke stdin i stedet for at åbne /dev/tty? Du kan bare
fjerne disse 5 linier, og så skrive "stdin" i stedet for input alle
steder i programmet (og fjerne FILE * input).

> tcgetattr(fileno(input),&initial_settings); // backup terminal settings
>
> FD_ZERO(&inputs);
> FD_SET(0, &inputs);
>
> sockfd = socket(AF_INET, SOCK_STREAM, 0); // create TCP/IP socket
>
> /* Make connection according to stated protocol, remote IP and port */
> address.sin_family = AF_INET;
> address.sin_addr.s_addr = inet_addr(argv[1]);
> address.sin_port = htons(23);
> len = sizeof(address);
> result = connect(sockfd, (struct sockaddr *)&address, len);
>
> FD_SET(sockfd, &inputs);
>
> if(result == -1) {
> perror("connect");
> exit(1);
> }
>
> init_terminal(input);
>
> while(1){
> fdTest = inputs;
> result = select(FD_SETSIZE, &fdTest, (fd_set *)NULL, (fd_set,NULL, (struct timeval *) 0);
> if(result == -1) {
> perror("select");
> exit(1);
> }
>
> if(FD_ISSET(0, &fdTest)) { // on event from keyboard

> ch = fgetc(input);

/* ioctl(0, FIONREAD, &nread); */
if(ch < 0) { // clean up on closure
close(sockfd);
tcsetattr(fileno(input),TCSANOW,&initial_settings);
exit(0);
}

> if(ch == 10) // convert internal LF to CR
> ch = 13;
> write(sockfd, &ch, 1);
> }
> else { // on event from socket

nread = read(sockfd, &ch, 1);

/* ioctl(sockfd, FIONREAD, &nread); */
if(nread == 0) {
close(sockfd);
tcsetattr(fileno(input),TCSANOW,&initial_settings);
exit(0);
}

> printf("%c", ch);
> fflush(NULL); // flush on each character read
> }
> }
>}
>
>void init_terminal(FILE *input){
> new_settings = initial_settings;
> new_settings.c_lflag &= ~ICANON; // go to non-canonical mode
> new_settings.c_lflag &= ~ECHO; // turn local echo off

Her burde du kunne bruge cfmakeraw() i stedet for.

> /* return characters immediately */
> new_settings.c_cc[VMIN] = 0;
> new_settings.c_cc[VTIME] = 0;
>
> if(tcsetattr(fileno(input), TCSANOW, &new_settings) != 0) {
> fprintf(stderr,"could not set attributes\n");
> }
>}

Mvh
Kent
--
"A computer is a state machine.
Threads are for people who can't program state machines."
- Alan Cox

Søg
Reklame
Statistik
Spørgsmål : 177558
Tips : 31968
Nyheder : 719565
Indlæg : 6408925
Brugere : 218888

Månedens bedste
Årets bedste
Sidste års bedste