PDA

View Full Version : socket problem


k_w_s_t_a_s
05-23-2003, 03:51 PM
I wrote the following progs:


/* prsref.c */

#include <sys/types.h> /* For sockets */
#include <sys/socket.h> /* For sockets */
#include <netinet/in.h> /* For Internet sockets */
#include <netdb.h> /* For gethostbyaddr */
#include <stdio.h>
#include <stdlib.h>
#define READ 0
#define WRITE 1

main(int argc,char** argv) {
int pid,fdp2c[2],fdc2p[2];

int port, sock, newsock, serverlen, clientlen; char buf[10];
struct sockaddr_in server, client;
struct sockaddr *serverptr, *clientptr;
struct hostent *rem;

if(argc!=3) {
printf("Usage : prsref <n> <port>\n");
exit(1);
}

if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { /* Create socket */
perror("socket"); exit(1); }
port = atoi(argv[2]); /* Convert port number to integer */
server.sin_family = AF_INET; /* Internet domain */
server.sin_addr.s_addr = htonl(INADDR_ANY); /* My Internet address */
server.sin_port = htons(port); /* The given port */
serverptr = (struct sockaddr *) &server;
serverlen = sizeof server;
if (bind(sock, serverptr, serverlen) < 0) {/* Bind socket to address */
perror("bind"); exit(1); }
if (listen(sock, 1) < 0) { /* Listen for connections */
perror("listen"); exit(1); }
clientptr = (struct sockaddr *) &client;
clientlen = sizeof client;

if(pipe(fdp2c)==-1) {
perror("pipe");
exit(1);
}
if(pipe(fdc2p)==-1) {
perror("pipe");
exit(1);
}
if((pid=fork())==-1) {
perror("fork");
exit(1);
}
if(pid) {
int i;
char move1[10],move2[10],score[2];
close(fdp2c[READ]);
close(fdc2p[WRITE]);
printf("I am referee with PID %d waiting for game request at port %d\n",getpid(),port);
if ((newsock = accept(sock, clientptr, &clientlen)) < 0) {
perror("accept"); exit(1); } /* Accept connection */
if ((rem = gethostbyaddr((char *) &client.sin_addr.s_addr,
sizeof client.sin_addr.s_addr, /* Find client's address */
client.sin_family)) == NULL) {
perror("gethostbyaddr"); exit(1);
}
printf("Player 2 connected\n");

score[0]=score[1]=48;
for(i=1;i<=atoi(argv[1]);i++) {
write(fdp2c[WRITE],"READY",6);
write(newsock,"READY",6);
//bzero(move1,sizeof move1);
if(read(fdc2p[READ],move1,sizeof(move1))<0) {
perror("read");
exit(1);
}
if(read(newsock,move2,sizeof(move2))<0) {
perror("read");
exit(1);
}
if(write(newsock,move1,sizeof move1)<0) {
perror("write");
exit(1);
}
printf("Player 1:%10s Player2:%10s\n",move1,move2);
if((!strcmp(move1,"PAPER") && !strcmp(move2,"ROCK")) || (!strcmp(move1,"ROCK") && !strcmp(move2,"SCISSORS" )) || (!strcmp(move1,"SCISSORS") && !strcmp(move2,"PAPER")))
score[0]++;
if((!strcmp(move2,"PAPER") && !strcmp(move1,"ROCK")) || (!strcmp(move2,"ROCK") && !strcmp(move1,"SCISSORS" )) || (!strcmp(move2,"SCISSORS") && !strcmp(move1,"PAPER")))
score[1]++;
}
write(fdp2c[WRITE],"STOP",5);
write(newsock,"STOP",5);
if(write(newsock,score,sizeof score)<0) {
perror("write");
exit(1);
}
printf("Score = %c - %c\n",score[0],score[1]);
if(score[0]==score[1]) printf("Nobody won\n");
else printf("Player %d won\n",(score[0]>score[1]?1:2));
close(fdp2c[WRITE]);
close(fdc2p[READ]);
close(newsock);
}
if(!pid) {
char message[10];
close(fdp2c[WRITE]);
close(fdc2p[READ]);
printf("I am player 1 with PID %d\n",getpid());
read(fdp2c[READ],message,sizeof(message));
srand((unsigned int)getpid());
while(strcmp(message,"STOP")) {
if(!strcmp(message,"READY")) {
switch(rand()%3) {
case 0:
write(fdc2p[WRITE],"PAPER",6);
break;
case 1:
write(fdc2p[WRITE],"ROCK",5);
break;
case 2:
write(fdc2p[WRITE],"SCISSORS",9);
break;
}
}
read(fdp2c[READ],message,sizeof(message));
}
close(fdp2c[READ]);
close(fdc2p[WRITE]);
}
}




/* prs.c */
#include <sys/types.h> /* For sockets */
#include <sys/socket.h> /* For sockets */
#include <netinet/in.h> /* For Internet sockets */
#include <netdb.h> /* For gethostbyname */
#include <stdio.h> /* For I/O */
main(int argc, char *argv[]) { /* Client with Internet stream sockets */
int port, sock, serverlen; char buf[10],move1[10],move2[10],score[2],choice;
int n=1;
struct sockaddr_in server;
struct sockaddr *serverptr;
struct hostent *rem;
if (argc < 3) { /* Are server's host name and port number given? */
printf("Please give host name and port number\n"); exit(1); }
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { /* Create socket */
perror("socket"); exit(1); }
if ((rem = gethostbyname(argv[1])) == NULL) { /* Find server address */
perror("gethostbyname"); exit(1); }
port = atoi(argv[2]); /* Convert port number to integer */
server.sin_family = AF_INET; /* Internet domain */
bcopy((char *) rem -> h_addr, (char *) &server.sin_addr,
rem -> h_length);
server.sin_port = htons(port); /* Server's Internet address and port */
serverptr = (struct sockaddr *) &server;
serverlen = sizeof server;
if (connect(sock, serverptr, serverlen) < 0) { /* Request connection */
perror("connect"); exit(1); }
while(1) {
bzero(buf, sizeof buf); /* Initialize buffer */
if(read(sock, buf, sizeof buf) < 0) {
perror("write");
exit(1);
}
if(!strcmp(buf,"READY")) {
printf("Give round %d play: ",n);
choice=getchar();getchar();
switch(choice) {
case 'p':
if(write(sock,"PAPER",6) < 0) {
perror("read");
exit(1);
}
strcpy(move2,"PAPER");
break;
case 'r':
if(write(sock,"ROCK",5) < 0) {
perror("read");
exit(1);
}
strcpy(move2,"ROCK");
break;
case 's':
if(write(sock,"SCISSORS",9)<0) {
perror("read");
exit(1);
}
strcpy(move2,"SCISSORS");
break;
default:
printf("Invalid choice\n");
}
}
bzero(move1,sizeof move1);
if(read(sock,move1,sizeof move1)<0) {
perror("read");
exit(1);
}
if(!strcmp(buf,"STOP")) break;
printf("Player 1:%10s Player2:%10s\n",move1,move2);
n++;
}
bzero(score,sizeof score);
if(read(sock,score,sizeof score)<0) {// Here is the problem
perror("read");
exit(1);
}
printf("Score = %c - %c\n",score[0],score[1]);
if(score[0]==score[1]) printf("Nobody won\n");
else printf("You %s\n",(score[0]>score[1]?"lost":"won"));
close(sock); /* Close socket */
exit(0);
}



The first prog is a referee between its child and the second prog(prs) which play a game.The parent communicates through pipes with the child and through internet sockets with the second player(prs).
The prsref gets as its arguments the number of rounds and the port.The client prog,prs,takes as its arguments the server name and the port.
In every round the parent in prsref,which is the referee,sends a message "READY" to his child and to the client,prs.Then the child and the client send back their choices.The referee evaluates the score and when the game is finished sends a message "STOP" to the child and the client.
Both progs work perfectly apart from the point where the client reads the score from the referee.It doesn't read anything.
Why is that?

Thank you in advance.

RobSeace
05-23-2003, 06:42 PM
Almost certainly because it's reading the score along with the
"STOP" message that you send... Remember, TCP is just a
stream-based protocol, not a message/packet/datagram-based
one like UDP... So, every separate write()/send() does NOT
necessarily translate into a separate read()/recv() on the remote
side... Depending on various things, the remote may require
multiple read()/recv()'s to read a single sent message, or it may
read multiple messages with a single read()/recv()... In this case,
it looks like you don't set TCP_NOWAIT, so the TCP/IP stack on
the server/referee side is almost certainly holding onto the "STOP"
message, then merging it with the score message that you write
immediately afterward... However, note: just setting TCP_NOWAIT
is no guarantee the same thing won't happen, either... (But, you
still probably want to do it, anyway... I almost always set it,
myself...) Your client might STILL be delayed enough that by
the time it tries to read the "STOP" message, it's also got the
score message in its receive queue, so it'll read that too (since
you specify that it should attempt to read upto 10 bytes, so it
reads the 5-byte "STOP"+null, then the 2-byte score)...

Your best approach would probably be to prefix every message
sent with a length value, then read that fixed-size length, and then
from that, you'll know exactly how much to read to completely
read in a single message... Alternatively, you could use an EOL
marker of some sort, and split up the read-in data appropriately...
(You could actually use the nulls you're already sending, I
suppose, if you like... Just change it to recognize that it read
more bytes than the "STOP" message consists of, so look
after that in the buffer for the score... Or, at least PART of it,
anyway... Ie: it's also possible you might only read one byte
of the score, but get the second byte later... I would rank the
likelihood of that as "extremely unprobable", though... ;-) But,
still, you should code for it...)