نقل قول نوشته اصلی توسط vasighi نمایش پست ها
سلام

من دو برنامه کلاینت c.c وسرور chatserver.c را به همراه برنامه ای که از لیست پیوندی استفاده کرده را دارم و می خوام با استفاده از پروتکل خودم تغییراتی روی آنها بدم.
سمت سرور به این صورته که اول که سرور اجرا میشه ./chatserver) ) می تونه با چند کلاینت ارتباط برقرار کنه و پیامهایی رو که هر کلاینت می فرسته برای همه کلاینت های دیگه هم می فرسته .
و سمت کلاینت(./c 127.0.0.1 9000) اینطوریه که اولا هر کلاینت تا پیامی نفرسته هیچ پیامی دریافت نمی کنه .یعنی recv() به شرط send() امکان پذیر هست و به محض اینکه اولین پیام را فرستاد تمام پیامهایی را که دیگران فرستاده اند را دریافت می کند این ارتباط همچنان هست تا وقتی که یه پیام bye\n دریافت کنه اونوقت connection بسته میشه و برنامه کلاینت تموم میشه.

اما پروتکلی که من فکر کردم:

برنامه کلاینت به این صورته که هنگامی که کاربر میخواد به سرور کانکت بشه اسم خودش رو به عنوان آرگومان ورودی وارد میکنه( ./c 127.0.0.1 9000 myname) وبعد از connect()شدن این آرگومان را( به عنوان فیلد name استراکچر UsersConnections) به سرور میفرسته و سروربعد از accept() کردن آن کاربر, توسط تابع insert() آن را داخل فیلد name به همراه اطلاعات دیگه کاربر کانکت شده(sockfd,ip,.. )به لیست پیوندی اضافه میکنه.(یا داخل آرایه ای از استراکچر UsersConnections )
کد HTML:
[//client side
[msg.payload=argv[3];
[Send(clientsockfd,msg);

("تقریبا" برنامه سرور با استفاده از لیست پیوندی)


//server side

کد HTML:
[left]#include <sys/time.h>
#include <sys/select.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
[right] [/right][/left][right][/right]
[left]#define PORT 9000
#define MAXDATASIZE 100
 
struct UsersConnections;
typedef struct UsersConnections UsersConnections;
typedef UsersConnections *UsersConnectionsPTR;
// List of users connections to server
struct UsersConnections {
int sockfd;
char IP[16];
char name[100];
struct UsersConnections *Prev;
struct UsersConnections *Next;
} UsersConnectionsPTR UsersConnectionsStart;
****************************************************
int Quit(UsersConnectionsPTR *sConnection, char *sReason) {
char buffer[1024];
//UsersPTR From;
UsersConnectionsPTR Connection;
Connection = *sConnection;
//From = (*sConnection)->User;
// close file descriptor
close(Connection->sockfd);
// Delete 
//UsersDelete(&From);
// Delete connection 
UsersConnectionsDelete(&Connection);
}
*****************
void handle_signal(int signum) {
// This will require you to research more on signal handling
printf("SIGNAL %d\n", signum);
if (signum == 11 || signum == 2) exit(0);
}
********************
/*int CheckSocketWrite(int sockfd) {
int rc;
struct timeval tv;
fd_set write_fd;
tv.tv_sec=0;
tv.tv_usec=1;
FD_ZERO(&write_fd);
FD_SET(sockfd, &write_fd);
rc = select(sockfd+1, NULL, &write_fd, NULL, &tv);
FD_ZERO(&write_fd);
return rc;
}*/
*********************
UsersConnections *UsersConnectionsInsert(UsersConnectionsPTR *sPtr,int sFd,char *sIP,char *sNick)
 {
UsersConnectionsPTR newPtr, previousPtr, currentPtr, nextPtr;
newPtr = (UsersConnections *) malloc(sizeof(UsersConnections));
if (newPtr != NULL) {
newPtr->sockfd=sFd;
strcpy(newPtr->IP, sIP);
strcpy(newPtr->Nick,sNick);
currentPtr = *sPtr;
nextPtr = currentPtr->Next;
newPtr->Next = currentPtr;
newPtr->Prev = NULL;
if (currentPtr != NULL) {
currentPtr->Prev = newPtr;
}
*sPtr = newPtr;
return newPtr;
} else {
printf("\n\nmalloc error: out of memory...\n\n");
return NULL;
}
}
***********************
void UsersConnectionsDelete(UsersConnectionsPTR *sPtr)
{
UsersConnectionsPTR previousPtr, nextPtr, currentPtr, tempPtr;
tempPtr = *sPtr;
previousPtr = (*sPtr)->Prev;
nextPtr = (*sPtr)->Next;
if (previousPtr == NULL) {
if (nextPtr != NULL) {
*sPtr = nextPtr;
(*sPtr)->Prev = NULL;
} else {
*sPtr = NULL;
}
} else {
if (nextPtr != NULL) {
previousPtr->Next = nextPtr;
nextPtr->Prev = previousPtr;
} else {
previousPtr->Next = NULL;
}
}
free(tempPtr);
}
************************
UsersConnections *Search(UsersConnectionsPTR *sPtr,char *sNick)
 {
  UsersConnectionsPTR currentPtr;
  currentPtr=*sPtr;
  while (currentPtr != NULL) {
   if (strcasecmp(currentPtr->Nick,sNick) == 0) {
     return currentPtr;
     }  
   currentPtr=currentPtr->Next;
  }
  return NULL;
}
************************
/*List data in our linked list:
 
void List(UsersConnectionsPTR *sPtr) {
  UsersConnectionsPTR currentPtr;
  printf("\n\nListing users\n\n");
  currentPtr = *sPtr;
  while (currentPtr != NULL) {
    printf("Name: %s\n",currentPtr->name);
    currentPtr = currentPtr->Next;
  }
}*/
 
****************************************************
int main()
 {
int clientfd;
pid_t pid;
int rc, max;
struct timeval tv;
char output[1024];
fd_set usersfd;
tv.tv_sec=0;
tv.tv_usec=0;
int newfd, sinsize, sockfd;
struct sockaddr_in clientaddr;
struct sockaddr_in myaddr;
UsersConnectionsPTR Connection, Next;
UsersConnectionsStart = NULL;
 
signal(SIGSEGV,handle_signal);
signal(SIGINT,handle_signal);
signal(SIGUSR1,handle_signal);
signal(SIGUSR2,handle_signal);
signal(SIGPIPE,SIG_IGN);
 
sinsize = sizeof(struct sockaddr_in);
myaddr.sin_family = AF_INET;
myaddr.sin_addr.s_addr = INADDR_ANY;
myaddr.sin_port = htons(PORT);
memset(myaddr.sin_zero, 0, sizeof(myaddr.sin_zero));
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
perror("socket");
exit(1);
}
if (bind(sockfd, (struct sockaddr *)&myaddr, sizeof(struct sockaddr)) == -1) {
perror("bind");
exit(1);
}
if (listen(sockfd, 100) == -1) {
perror("listen");
exit(1);
}
if(!fork())
 {
while(1)
 {
int rc;
struct timeval tv;
fd_set read_fd;
tv.tv_sec=0;
tv.tv_usec=1;
FD_ZERO(&read_fd);
FD_SET(sockfd, &read_fd);
rc = select(sockfd+1, &read_fd, NULL, NULL, &tv);
FD_ZERO(&read_fd);
If(rc>0)
newfd = accept(sockfd,(struct sockaddr *)&clientaddr,&sinsize);
//sockfd:listen socket
if (newfd == -1) {
perror("accept");
exit(1);
}
else if (newfd) {
printf("New CLIENT connection from IP %s on socket %d\n",
inet_ntoa(clientaddr.sin_addr), newfd);
fcntl(newfd,F_SETFL,O_NONBLOCK);
Connection = UsersConnectionsStart;
while (Connection != NULL) {
Next = Connection->Next;
FD_ZERO(&usersfd);
FD_SET(Connection->sockfd, &usersfd);
errno = 0;
rc = select(Connection->sockfd+1, &usersfd, NULL, NULL, &tv);
if (rc > 0) {
recv(Connection->sockfd,msg,MAXDATASIZE,0);
[right]nick=msg.payload;[/right][/left][right][/right]
[left]// Add user to connection list 
Connection=UsersConnectionsInsert(&UsersConnectionsStart,newfd,(char*)inet_ntoa(clientaddr.sin_addr),nick);
 
 
} else if (rc == -1) {
sprintf(output,"%m\n",errno);
Quit(&Connection, output);
}
Connection = Next;
}// while (Connection != NULL) 
usleep(200);
}// while(1)
}//if
}//main
********
[/left]
و سپس اسم کاربر تازه وارد را به همه کاربران آنلاین اعلام میکنه .((sendtoall(argv[3] is now connected)

تو قسمت آخر تابع chatserver میتونیم به جای buff,nick که بالا تعریف شد (اسم کلاینت کانکت شده )را بگذاریم

کد HTML:
[left].. .. .. .. .. .. .. .. 
if((nbytes=recv(i,buf,sizeof(buf),0))<=0)
                { if(nbytes==0)      
                 { printf("select server:socket %d hang up\n",i);
                  } else{ perror("recv error"); 
                       } close(i);
                 FD_CLR(i,&master);
              } else {for(j=0;j<=fdmax;j++)
                        { if(FD_ISSET(j,&master))
                           {if(j!=listener&&j!=i) 
                             { if(send(j,strcat(buf,"is now connected\n",nbytes,0)==-1)
                                 {perror("send error");
 
.. .. .. …. .. .. .. .. .. .. 
[/left]
و همینطور نام کاربران آنلاین رابرای کاربر تازه واردلیست میکندو براش میفرسته (با استفاده از تابع( list(UsersConnectionsPTR *user که روی لیست پیوندی اعمال میشه)(این تابع میتونه اینطوری تغییر کنه که بجای printf از sprintf استفاده کنه و اسم کاربرها رو داخل آرایه nick (که داخل تابع main تعریف شده) قرار بده

کد HTML:
[left][right]Char nick[][];[/right][/left][right][/right]
[left][right] sprintf (nick,"%s",UsersConnections.name)[/right][/left][right][/right]
و سپس این آرایه را به کلاینت بفرستدو کلاینت تک تک خانه های آرایه را چاپ کند
[left][right]For(i=0;nick[i]!=NULL;i++)[/right][/left][right][/right]
[left][right]   Printf("%s is Online…", nick[i]);[/right][/left][right][/right]
[left][right]*****************************[/right][/left][right][/right]
حالا کاربر برای شروع گفتگو باید درخواست خود را به یکی از این کاربران آنلاین بفرستد
پیامی که با دستورات recv() و  send() ردوبدل میشه رو به صورت یه استراکچر به صورت زیر تعریف کنیم:
[left][right]Struct msg {[/right][/left][right][/right]
[left][right]  char  Payload[256]; [/right][/left][right][/right]
[left][right]  int sockfd;[/right][/left][right][/right]
[left][right]} msg;[/right][/left][right][/right]
[left][/left]
که فیلد payload خود پیام و sockfd آدرس گیرنده.
کلاینت نام طرف گفتگوی خودش رو(داخل فیلد payload استراکچر msg قرار می ده )و به سرور می فرسته و سرور یه سرچ روی لیست پیوندی روی این اسم انجام می ده تا آدرسش رو (UsersConnections.sockfd ) بدست بیاره .این میشه آدرس گیرنده .

حالا این آدرس گیرنده را داخل فیلد sockfdmsg. قرار میده و به کلاینت می فرسته و کلاینت پیام درخواست گفتگو را به سرور می فرسته( ضمن اینکه فیلد آدرس دارای آدرس گیرنده است )و سرور تو یه حلقه for() کلاینت های(سوکت های accept() شده) رو که برا ی دریافت پیام آماده اند ((select(read_fd) رو چک میکنه بعد تو همین حلقه Recv(selectsockfd,msg) رو انجام میده بعد از دریافت پیام (msg) آن را به آدرس گیرنده که داخل خود پیام هست می فرسته .Send(msg.sockfd, msg,);)

.این سرچ روی یه لیست پیوندی از استراکچر UsersConnections (که یه متغیر سراسری تعریف شده و هر وقت سرور یه کلاینت جدید رو accept() می کنه اونو به این لیست insert() میکنه)انجام میشه که به صورت زیر تعریف شده:

کد HTML:
[left][font=Tahoma][left]struct UsersConnections {[/left][/font][left]
[font=Tahoma]int sockfd;[/font]
[font=Tahoma]char IP[16];[/font]
[font=Tahoma]char name[100];[/font]
[font=Tahoma]struct UsersConnections *Prev;[/font]
[font=Tahoma]struct UsersConnections *Next;[/font]

[font=Tahoma]} UsersConnectionsPTR UsersConnectionsStart[/font][/left][font=Tahoma][/font][/left]
<DIV align=left>

گفتگو تا وقتی ادامه دارد که یکی از طرفین کاراکتر$ را وارد کند آنگاه دوباره به کاربر اجازه ایجاد گفتگوی جدید با فراخوانی تابع talk() داده می شود.
و اگر کاربر کلمه quit را وارد کند آنگاه disconnect می شود(close(clientsockfd) در سمت سرور سوکت مربوط به کلاینتی که پیام quit را فرستاده می بندد)

در واقع کد سمت کلاینت بعد از connect() برای گفتگو باید به صورت زیر باشه :
......................
کد HTML:
 
//chatserver 
 
 
[left]#include <stdio.h>
 
 
برنامه سمت سرور :


[left]#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>[/left]
 
[left]#define PORT 9000
int main(void)
{ 
fd_set master;
fd_set read_fds;
struct sockaddr_in myaddr;
struct sockaddr_in remoteaddr;
int fdmax;
int listener;
int newfd;
char buf[256];
int nbytes;
int yes=1;
int addrlen;
int i,j;
FD_ZERO(&master);
FD_ZERO(&read_fds);[/left]
 
[left]if((listener=socket(AF_INET,SOCK_STREAM,0))==-1)
{ perror("socket");
exit(1);
}
if(setsockopt(listener,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(int))==-1)
{ perror("setsockopt");
exit(1);
}
myaddr.sin_family=AF_INET;
myaddr.sin_addr.s_addr=INADDR_ANY;
myaddr.sin_port=htons(PORT);[/left]
 
[left]memset(&(myaddr.sin_zero),'\0',8);[/left]
 
[left]if(bind(listener,(struct sockaddr *)&myaddr,sizeof(myaddr))==-1)
{ perror("bind error");
exit(1);
}
if(listen(listener,10)==-1)
{ perror("listen error");
exit(1);
}
FD_SET(listener,&master);
fdmax=listener;[/left]
 
[left]for(;;)
{
read_fds=master;
if(select(fdmax+1,&read_fds,NULL,NULL,NULL)==-1)
{ perror("select error");
exit(1); 
}
for(i=0;i<=fdmax;i++)
{ if(FD_ISSET(i,&read_fds))
{if(i==listener)
{addrlen=sizeof(remoteaddr);
if((newfd=accept(listener,(struct sockaddr *)&remoteaddr,&addrlen))==-1)
perror("accept error");
else{ FD_SET(newfd,&master);
if(newfd>fdmax) 
fdmax=newfd;
printf("select server:new connection from %s on socket %d\n",inet_ntoa(remoteaddr.sin_addr),newfd); 
}
} else {if((nbytes=recv(i,buf,sizeof(buf),0))<=0)
{ if(nbytes==0) 
{ printf("select server:socket %d hang up\n",i);
} else{ perror("recv error"); 
} close(i);
FD_CLR(i,&master);
} else {for(j=0;j<=fdmax;j++)
{ if(FD_ISSET(j,&master))
{if(j!=listener&&j!=i) 
{ if(send(j,buf,nbytes,0)==-1)
{perror("send error");
}
}
}
}
}
}
}
}
}[/left]
 
} 
[/left]
برنامه سمت کلاینت(c.c )

کد HTML:
 
[left]/*client*/ 
 


[left]#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h> 
 
void error(char *msg)
{
    perror(msg);
    exit(0);
}
 
int main(int argc, char *argv[])
{
    int sockfd, portno, n;
    struct sockaddr_in serv_addr;
    struct hostent *server;
 
    char buffer[256];
    if (argc < 3) {
       fprintf(stderr,"usage %s hostname port\n", argv[0]);
       exit(0);
    }
    portno = atoi(argv[2]);
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) 
        error("ERROR opening socket");
    server = gethostbyname(argv[1]);
    if (server == NULL) {
        fprintf(stderr,"ERROR, no such host\n");
        exit(0);
    }
    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    bcopy((char *)server->h_addr, 
         (char *)&serv_addr.sin_addr.s_addr,
         server->h_length);
    serv_addr.sin_port = htons(portno);
    if (connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(struct sockaddr)) < 0) 
        error("ERROR connecting");
    
    //start of chat
    bzero(buffer,256);
     while(strcasecmp(buffer,"bye")!=0)   {
    bzero(buffer,256);
    printf("Please enter the message: ");
    fgets(buffer,255,stdin);
    n = write(sockfd,buffer,strlen(buffer));
    if (n < 0) 
         error("ERROR writing to socket");
    bzero(buffer,256);
    n = read(sockfd,buffer,255);
    if (n < 0) 
         error("ERROR reading from socket");
    printf("Here is the message: %s\n",buffer);
     
}//while
}

[/left][/left]

کد HTML:
در واقع کد سمت کلاینت بعد از connect() برای گفتگو باید به صورت زیر باشه :
 
[LEFT][RIGHT]montazere daryafte msg bara chat//[/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT][RIGHT]msg.payloade=null;[/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT][RIGHT]if (recv(clientsockfd,msg)>0)[/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT][RIGHT]  if(strcasecmp(msg.payload,"talk")==0)   then [/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT][RIGHT]    {  printf(msg.payload);[/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT][RIGHT]       printf("your answer?yes/no");[/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT][RIGHT]       gets(msg.payload);[/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT][RIGHT]       send(clientsockfd, msg);[/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT][RIGHT]       }[/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT][RIGHT]else {//start chat [/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT][RIGHT]talk();[/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT][RIGHT]…[/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT][RIGHT] [/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT][RIGHT] [/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT][RIGHT]int talk();[/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT][RIGHT]}[/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT][RIGHT]printf("insert your friend name…");[/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT][RIGHT]gets(name);[/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT][RIGHT]msg.payload=name;[/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT][RIGHT]send(clientsockfd, msg);[/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT][RIGHT]recv(clientsockfd,msg);[/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT][RIGHT]strcpy(msg.payloade, " I want to talk to you…");[/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT][RIGHT]send(clientsockfd, msg);[/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT][RIGHT]recv(clientsockfd, msg);[/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT][RIGHT]if(strcasecmp(msg.payload, "yes")==0)  [/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT][RIGHT]  printf("send your message …\n at the end type '$' …");[/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT][RIGHT]else talk();[/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT][RIGHT]while (1)[/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT][RIGHT]}[/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT][RIGHT]bzero(msg.payload,256);[/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT][RIGHT]printf("Please enter the message: ");[/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT][RIGHT]fgets(msg.payload,255,stdin);[/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT]n = send(clientsockfd,buffer,strlen(buffer));
if (n < 0) 
         error("ERROR writing to socket");
[RIGHT]if(strcasecmp(msg.payload, "$")!=0) break;[/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT]bzero(msg.payload,256);
n = recv(clientsockfd, msg.payload,255);
    if (n < 0) 
         error("ERROR reading from socket");
printf("Here is the message: %s\n", msg.payload);
[RIGHT]if(strcasecmp(msg.payload, "$")!=0) break;[/RIGHT][/LEFT][RIGHT][/RIGHT]
[LEFT]}//while
talk()
}//talk    
[/LEFT]