|
| Gode råd søges Fra : Morten Winther |
Dato : 04-04-03 12:00 |
|
Hej
Jeg har nu langt om længe fået skrevet en chat i C++. Det er menigen at man
telnet'er ind på en port og så chatter - altså intet fancy.
Jeg er dog total newbie så jeg vil lige høre om nogen vil komme med lidt
feedback til min kode? Ingen grund til at tillægge sig dårlige vaner her fra
start. Især tror jeg at jeg har blandet C og C++ sammen, hvad burde jeg
ændre?
Nu hører man jo også så meget om buffer-overflow o.s.v. Er der nogen
sikkerhedsbrister i min kode?
Jeg var også meget i tvivl om de pointers jeg bruger. Er de rigtige?
Det er lidt besværligt at overføre mit map til de forskellige funktioner.
Kan jeg sætte map<int, Deltager_info> alle uden for main, og så bruge det
direkte i alle funktioner?
Lidt tunge spørgsmål da i jo nok skal læse hele koden igennem for at svare,
men jeg håber i vil give et par gode råd.
Hilsen Morten
#include <map>
#include <iostream>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
using namespace std;
// port we're listening on
#define PORT 7913
// fildescripters
fd_set master; // master file descriptor list
fd_set read_fds;
// struct
struct Deltager_info
{
int fd_socket;
string nick;
int mode;
string buffer;
};
// hent til næste \r
string hent(string *s)
{
string temp;
// find første \n
unsigned int f = s->find("\r",0);
if(f != string::npos)
{
// temp
temp = s->substr(0, f);
// resten
*s = s->substr(f+1, s->size() - f -1);
}
else
{
temp = "";
//return 0;
}
//return
return temp;
}
// send til alle
void send_til_alle(map<int, Deltager_info> *al, string besked)
{
besked += "\r\n";
// loop
for(map<int, Deltager_info>::iterator it = (*al).begin(); it !=
(*al).end(); ++it)
{
//kun til dem der er helt inde
if(it->second.mode == 2)
{
if (FD_ISSET(it->first, &master))
{
send(it->first, besked.c_str(), strlen(besked.c_str()), 0);
}
}
}
}
// sæt nick
void hentnick(map<int, Deltager_info> *al, int afsender, string navn)
{
// burde checke om navn er ok samt ledig
// gem i map
(*al)[afsender].nick = navn;
(*al)[afsender].mode = 2; // chatmode
// fortæl til verden
send_til_alle(&(*al), "** Server: " + navn + " er kommet ind i chatten");
}
// behandle
void behandle(map<int, Deltager_info> *al, int id)
{
string denne;
//cout << (*al)[id].buffer << endl;
while ((denne = hent(&(*al)[id].buffer)) != "")
{
if((*al)[id].mode == 1)
{
//nick
hentnick(&(*al), id, denne);
}
else if((*al)[id].mode == 2)
{
// chat
send_til_alle(&(*al), ((*al)[id].nick + ": " + denne));
}
// serverpromt
cout << "Buffer " << id << ": " << denne << endl;
}
}
int main()
{
// erklær
string ind;
map<int, Deltager_info> alle;
struct sockaddr_in myaddr; // server address
struct sockaddr_in remoteaddr; // client address
int listener; // listening socket descriptor
int newfd; // newly accept()ed socket descriptor
char buf[256];
int nbytes;
int yes=1; // for setsockopt() SO_REUSEADDR, below
socklen_t addrlen;
int fdmax;
string besked_start = "Velkommen til TelnetChat v1.0\r\nIndtast nick\r\n";
// start
cout << "Server startet!" << endl;
// clear the master and temp sets
FD_ZERO(&master);
FD_ZERO(&read_fds);
// get the listener
if((listener = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
// lose the pesky "address already in use" error message
if(setsockopt(listener, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int))
== -1) {
perror("setsockopt");
exit(1);
}
// bind
myaddr.sin_family = AF_INET;
myaddr.sin_addr.s_addr = INADDR_ANY;
myaddr.sin_port = htons(PORT);
memset(&(myaddr.sin_zero), '\0', 8);
if(bind(listener, (struct sockaddr *)&myaddr, sizeof(myaddr)) == -1) {
perror("bind");
exit(1);
}
// listen
if (listen(listener, 10) == -1) {
perror("listen");
exit(1);
}
// add the listener to the master set
FD_SET(listener, &master);
fdmax = listener; // so far, it's this one
// listener -> map
alle[listener].nick = "lytter";
alle[listener].mode = 100;
alle[listener].buffer = "";
// hovedloop
while(1) {
// copy it
read_fds = master;
// cout << alle.size() << endl;
// select
if(select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) {
perror("select");
exit(1);
}
// loop gennem sockets
for(map<int, Deltager_info>::iterator it = alle.begin(); it !=
alle.end(); ++it)
{
// test om socket er ok
if (FD_ISSET(it->first, &read_fds))
{
// er det lytteren
if (it->first == listener)
{
// handle new connections
addrlen = sizeof(remoteaddr);
// hent ny socket - udskriv hvis fejl
if ((newfd = accept(listener, (struct sockaddr *)&remoteaddr,
&addrlen)) == -1) {
perror("accept");
}
else // ny socket ok
{
// add to master set
FD_SET(newfd, &master);
// keep track of the maximum
if (newfd > fdmax) {
fdmax = newfd;
}
// indsæt i map
alle[newfd].mode = 1; // 1 = venter på brugernavn
// indtast handle
send(newfd, besked_start.c_str(), strlen(besked_start.c_str()), 0);
printf("selectserver: new connection from %s on socket %d\n",
inet_ntoa(remoteaddr.sin_addr), newfd);
}
}
else // det er ikke lytteren = alm client
{
// handle data from a client
if ((nbytes = recv(it->first, buf, sizeof(buf), 0)) <= 0)
{
// got error or connection closed by client
if (nbytes == 0)
{
// connection closed
printf("selectserver: socket %d hung up\n", it->first);
}
else
{
perror("recv");
}
// farvel
close(it->first); // bye!
// remove from master set
FD_CLR(it->first, &master);
// fortæl verden
if(it->second.mode == 2)
{
send_til_alle(&alle, ("** Server: " + it->second.nick + " har
forladt chatten"));
}
// fjern fra map
alle.erase(it->first);
}
else // rigtig data fra client
{
// gem ny tekst i buffer
alle[it->first].buffer += *buf;
// find ud af om input er afsluttet
unsigned int f = alle[it->first].buffer.find("\r",0);
if(f != string::npos)
{
behandle(&alle, it->first);
}
} // data fra client
} // ikke lytteren
} // if socket ok
} // socket loop
} // hovedloop
// slut
return 0;
}
--
/ morten
"There are only 10 types of people in the world: Those who understand
binary, and those who don't"
| |
Lasse Westh-Nielsen (04-04-2003)
| Kommentar Fra : Lasse Westh-Nielsen |
Dato : 04-04-03 13:45 |
|
"Morten Winther" <mail@is.invalid> wrote in message
news:b6jofv$co4$1@sunsite.dk...
> Hej
>
> jeg vil lige høre om nogen vil komme med lidt feedback til min kode?
Jeg synes du kunne bruge lidt mere OO, du praktiserer vist ren og skær
procedurel programmering. Det kunne hjælpe med at modularisere din kode ud
på flere, mere overskuelige filer, og dermed gøre det nemmere at udvide og
vedligeholde:
/* Fil: DeltagerInfo.h */
class DeltagerInfo
{
public:
DeltagerInfo(string nick, ...) /* contructor */
string getNick() { return this->nick; };
void setNick(string nick) { this->nick = nick; };
...
private:
string nick;
...
};
> Det er lidt besværligt at overføre mit map til de forskellige funktioner.
> Kan jeg sætte map<int, Deltager_info> alle uden for main, og så bruge det
> direkte i alle funktioner?
Et lille fif er typedef:
typedef MyCoolMap map<int, Deltager_info>;
Så kan du skrive såden her, som jeg synes ser pænere ud:
MyCoolMap* coolMap = new MyCoolMap();
void send_til_alle(MyCoolMap* al, string besked)...
Der giver ikke ekstra performance, men det kan gøre din kode "pænere".
Mvh Lasse
--
<signature>
Lasse Westh-Nielsen
lasse@daimi.au.dk
</signature>
| |
Robert Larsen (04-04-2003)
| Kommentar Fra : Robert Larsen |
Dato : 04-04-03 14:04 |
|
Lasse Westh-Nielsen wrote:
> "Morten Winther" <mail@is.invalid> wrote in message
> news:b6jofv$co4$1@sunsite.dk...
>
>>Det er lidt besværligt at overføre mit map til de forskellige funktioner.
>>Kan jeg sætte map<int, Deltager_info> alle uden for main, og så bruge det
>>direkte i alle funktioner?
>
>
> Et lille fif er typedef:
>
> typedef MyCoolMap map<int, Deltager_info>;
Næsten rigtigt
typedef map<int, Deltager_info> MyCoolMap;
> Så kan du skrive såden her, som jeg synes ser pænere ud:
>
> MyCoolMap* coolMap = new MyCoolMap();
>
> void send_til_alle(MyCoolMap* al, string besked)...
>
> Der giver ikke ekstra performance, men det kan gøre din kode "pænere".
>
> Mvh Lasse
>
>
> --
VH
Robert
| |
Robert Larsen (04-04-2003)
| Kommentar Fra : Robert Larsen |
Dato : 04-04-03 13:58 |
|
Morten Winther wrote:
> Hej
>
> Jeg har nu langt om længe fået skrevet en chat i C++. Det er menigen at man
> telnet'er ind på en port og så chatter - altså intet fancy.
>
> Jeg er dog total newbie så jeg vil lige høre om nogen vil komme med lidt
> feedback til min kode? Ingen grund til at tillægge sig dårlige vaner her fra
> start. Især tror jeg at jeg har blandet C og C++ sammen, hvad burde jeg
> ændre?
>
> Nu hører man jo også så meget om buffer-overflow o.s.v. Er der nogen
> sikkerhedsbrister i min kode?
>
> Jeg var også meget i tvivl om de pointers jeg bruger. Er de rigtige?
>
> Det er lidt besværligt at overføre mit map til de forskellige funktioner.
> Kan jeg sætte map<int, Deltager_info> alle uden for main, og så bruge det
> direkte i alle funktioner?
>
> Lidt tunge spørgsmål da i jo nok skal læse hele koden igennem for at svare,
> men jeg håber i vil give et par gode råd.
>
>
>
Well.....jeg så ikke umiddelbart nogen fejl men jeg har da et par
kommentarer til din programmeringsstil. Brug mange flere og mindre
funktioner (eller endnu bedre..indkapsling i objekter). En god regel er
at en funktion ikke må fylde mere en én skærm og allerhøjst have to
nestede scopes:
while(something)
{
if(somethingElse)
{
//Ikke flere if's, case's eller løkker herunder.
//Hvis yderligere er nødvendige så læg
//dem i en funktion for sig selv og kald den.
}
else
{
}
}
Ellers bliver det for uoverskueligt. Din main slutter med:
}
} // data fra client
} // ikke lytteren
} // if socket ok
} // socket loop
} // hovedloop
// slut
return 0;
}
Lige i overkanten efter min mening.
Med små funktioner er det meget lettere at spotte mulige fejlkilder og
senere at udvidde koden.
En anden ting er at holde sig til ét sprog (menneskesprog). Du har
variabler som 'listener', 'newfd' og 'yes', men også 'alle' og
funktioner som 'send_til_alle'. Der er forstyrrende at læse, så hold dig
til engelsk (så får du heller ikke problemer med æøå).
Sidst men ikke mindst. Dit program er meget funktions orienteret. Du
bruger godt nok C++ her og der men du definerer ikke dine egne objekter
og programmerer ikke objekt orienteret. At bruge C++ objekter er ikke
nok. Derimod kan man godt programmere objekt orienteret selvom man
bruger almindelig C (gtk+ biblioteket er et godt eksempel). Det er lidt
smag og behag om man vil bruge objekter men det er noget nemmere at
organisere sin kode hvis man programmerer objekt orienteret.
VH
Robert
| |
Anders J. Munch (04-04-2003)
| Kommentar Fra : Anders J. Munch |
Dato : 04-04-03 15:43 |
|
"Robert Larsen" <Xrcl@ttpcom.com> wrote:
>
> En anden ting er at holde sig til ét sprog (menneskesprog). Du har
> variabler som 'listener', 'newfd' og 'yes', men også 'alle' og
> funktioner som 'send_til_alle'. Der er forstyrrende at læse, så hold dig
> til engelsk (så får du heller ikke problemer med æøå).
Ja, jeg kender ikke nogen programmører, der ikke programmerer i
dangelsk eller har gjort det tidligere, men det er nu noget rod.
Man skal vælge et sprog. Mere specifikt, man skal vælge det sprog man
forstår bedst. Uanset typografiske småirritationer.
mvh. Anders
| |
Bertel Lund Hansen (04-04-2003)
| Kommentar Fra : Bertel Lund Hansen |
Dato : 04-04-03 16:31 |
|
Anders J. Munch skrev:
>Ja, jeg kender ikke nogen programmører, der ikke programmerer i
>dangelsk eller har gjort det tidligere, men det er nu noget rod.
Det gør jeg nu, bl.a. fordi en del gode varabelnavne er
reserverede, f.eks. "new". Så tager jeg "ny" i stedet for. Jeg
bruger endda også f.eks. "naeste".
>Man skal vælge et sprog. Mere specifikt, man skal vælge det sprog man
>forstår bedst. Uanset typografiske småirritationer.
Hvis jeg skulle vælge ét sprog til variabelnavne, blev det
engelsk fordi æøå giver ballade.
--
Bertel
http://bertel.lundhansen.dk/ FIDUSO: http://fiduso.dk/
| |
Robert Larsen (04-04-2003)
| Kommentar Fra : Robert Larsen |
Dato : 04-04-03 18:20 |
|
Bertel Lund Hansen wrote:
> Anders J. Munch skrev:
>
>
>>Ja, jeg kender ikke nogen programmører, der ikke programmerer i
>>dangelsk eller har gjort det tidligere, men det er nu noget rod.
>
>
> Det gør jeg nu, bl.a. fordi en del gode varabelnavne er
> reserverede, f.eks. "new". Så tager jeg "ny" i stedet for. Jeg
> bruger endda også f.eks. "naeste".
>
>
>>Man skal vælge et sprog. Mere specifikt, man skal vælge det sprog man
>>forstår bedst. Uanset typografiske småirritationer.
>
>
> Hvis jeg skulle vælge ét sprog til variabelnavne, blev det
> engelsk fordi æøå giver ballade.
>
De fleste firmaer har også en code convention som specificerer, hvordan
man programmerer i firmaet, f.eks. hvordan man skal skrive variabel
navne, funktions navne, klasse navne, om der skal være mellemrum mellem
operatorer og operander, osv. osv. De steder jeg har arbejdet har det
også været et krav, at alle navne og kommentarer var på engelsk, så det
er en god skik at få indøvet.
VH
Robert
| |
Anders J. Munch (05-04-2003)
| Kommentar Fra : Anders J. Munch |
Dato : 05-04-03 12:35 |
|
"Bertel Lund Hansen" <nospamfor@lundhansen.dk> skrev:
> >Man skal vælge et sprog. Mere specifikt, man skal vælge det sprog man
> >forstår bedst. Uanset typografiske småirritationer.
>
> Hvis jeg skulle vælge ét sprog til variabelnavne, blev det
> engelsk fordi æøå giver ballade.
Jeg har prøvet alle tre, dansk, dangelsk og engelsk. Dansk er den
klare vinder. Det er lettere at skrive, og det er lettere at
læse. Klarhed og præcision stiger.
Da først jeg havde besluttet mig for at de var en pris værd at betale,
så holdt ae, oe og aa op med at genere mig, nær så meget. I
dangelsk-årene var jeg konstant irriteret over dem og substituerede
ofte med danske og engelske synonymer for at slippe for dem. Nu hvor
jeg skriver dansk, vælger jeg bare det rigtige ord, uanset.
- Anders
| |
Robert Larsen (06-04-2003)
| Kommentar Fra : Robert Larsen |
Dato : 06-04-03 13:40 |
|
Anders J. Munch wrote:
> "Bertel Lund Hansen" <nospamfor@lundhansen.dk> skrev:
>
>>>Man skal vælge et sprog. Mere specifikt, man skal vælge det sprog man
>>>forstår bedst. Uanset typografiske småirritationer.
>>
>>Hvis jeg skulle vælge ét sprog til variabelnavne, blev det
>>engelsk fordi æøå giver ballade.
>
>
> Jeg har prøvet alle tre, dansk, dangelsk og engelsk. Dansk er den
> klare vinder. Det er lettere at skrive, og det er lettere at
> læse. Klarhed og præcision stiger.
>
> Da først jeg havde besluttet mig for at de var en pris værd at betale,
> så holdt ae, oe og aa op med at genere mig, nær så meget. I
> dangelsk-årene var jeg konstant irriteret over dem og substituerede
> ofte med danske og engelske synonymer for at slippe for dem. Nu hvor
> jeg skriver dansk, vælger jeg bare det rigtige ord, uanset.
>
> - Anders
>
>
>
Derhjemme har man heldigvis den frihed, at man kan gøre hvad man vil,
men langt de fleste firmaer kræver, at man skriver på engelsk, så hvis
man har tænkt sig at gå prof., så er det en god idé at vænne sig til det
engelske. Firmaer bruger jo outsourcing og måske integration af deres
software med udenlandske firmaer/kunder, så der må helst ikke være nogle
sprogmæssige problemer med koden.
VH
Robert
| |
Anders J. Munch (06-04-2003)
| Kommentar Fra : Anders J. Munch |
Dato : 06-04-03 14:56 |
|
"Robert Larsen" <Xrobert@the-playground.dk> skrev:
>Firmaer bruger jo outsourcing og måske integration af deres
> software med udenlandske firmaer/kunder, så der må helst ikke være nogle
> sprogmæssige problemer med koden.
YAGNI.
mvh. Anders
| |
|
|