首页 > 代码库 > Socket网络编程--聊天程序(3)

Socket网络编程--聊天程序(3)

  上一小节,已经讲到可以每个人多说话,而且还没有限制,简单的来说,我们已经完成了聊天的功能了,那么接下来我们要实现什么功能呢?一个聊天程序至少应该支持一对多的通讯吧,接下来就实现多个客户端往服务器发送数据,和服务器向多个客户端发送数据。

  多对一,单向,各个客户端都可以向服务器发送数据

  close函数

  #include <unistd.h>

  int close(int sockfd); //用于关闭所打开的Socket套接字 返回值:如果为0表示成功,-1表示失败

  处理的办法是每一个客户端连接到服务器,此时服务器每个客户端对伊一个进程进行处理。

  client.c

  这里的client.c总体没有修改太多,就修改了几个bug而已。

  server.c

  1 #include <stdio.h>  2 #include <stdlib.h>  3 #include <errno.h>  4 #include <string.h>  5 #include <netdb.h>  6 #include <sys/types.h>  7 #include <sys/socket.h>  8 #include <sys/time.h>  9 #include <sys/un.h> 10 #include <sys/ioctl.h> 11 #include <sys/wait.h> 12 #include <netinet/in.h> 13 #include <arpa/inet.h> 14  15  16 #define SERVER_PORT 12138 17 #define BACKLOG 20 18 #define MAX_CON_NO 10 19 #define MAX_DATA_SIZE 4096 20  21 int main(int argc,char *argv[]) 22 { 23     struct sockaddr_in serverSockaddr,clientSockaddr; 24     char sendBuf[MAX_DATA_SIZE],recvBuf[MAX_DATA_SIZE]; 25     int sendSize,recvSize; 26     int sockfd,clientfd; 27     int on=1; 28     socklen_t sinSize=0; 29     char username[32]; 30     int pid; 31  32     if(argc != 2) 33     { 34         printf("usage: ./server [username]\n"); 35         exit(1); 36     } 37     strcpy(username,argv[1]); 38     printf("username:%s\n",username); 39  40     /*establish a socket*/ 41     if((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1) 42     { 43         perror("fail to establish a socket"); 44         exit(1); 45     } 46     printf("Success to establish a socket...\n"); 47  48     /*init sockaddr_in*/ 49     serverSockaddr.sin_family=AF_INET; 50     serverSockaddr.sin_port=htons(SERVER_PORT); 51     serverSockaddr.sin_addr.s_addr=htonl(INADDR_ANY); 52     bzero(&(serverSockaddr.sin_zero),8); 53  54     setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)); 55  56     /*bind socket*/ 57     if(bind(sockfd,(struct sockaddr *)&serverSockaddr,sizeof(struct sockaddr))==-1) 58     { 59         perror("fail to bind"); 60         exit(1); 61     } 62     printf("Success to bind the socket...\n"); 63  64     /*listen on the socket*/ 65     if(listen(sockfd,BACKLOG)==-1) 66     { 67         perror("fail to listen"); 68         exit(1); 69     } 70  71     sinSize=sizeof(clientSockaddr);//注意要写上,否则获取不了IP和端口 72     while(1)//多次accept 73     { 74         /*accept a client‘s request*/ 75         if((clientfd=accept(sockfd,(struct sockaddr  *)&clientSockaddr, &sinSize))==-1) 76         { 77             perror("fail to accept"); 78             exit(1); 79         } 80         printf("Success to accpet a connection request...\n"); 81         printf(" %s:%d join in!\n",inet_ntoa(clientSockaddr.sin_addr),ntohs(clientSockaddr.sin_port)); 82  83         if((pid=fork())<0) 84         { 85             perror("fork error\n"); 86         } 87         else if(pid==0)/*child*/ 88         { 89             while(1) 90             { 91                 /*receive datas from client*/ 92                 if((recvSize=recv(clientfd,recvBuf,MAX_DATA_SIZE,0))==-1) 93                 { 94                     perror("fail to receive datas"); 95                     exit(1); 96                 } 97                 printf("Client:%s\n",recvBuf); 98                 memset(recvBuf,0,MAX_DATA_SIZE); 99             }100         }101         /*send datas to client*/102         /* 本程序不发送103         while(1)104         {105             fgets(sendBuf,MAX_DATA_SIZE,stdin);106             if((sendSize=send(clientfd,sendBuf,strlen(sendBuf),0))!=strlen(sendBuf))107             {108                 perror("fail to send datas");109                 exit(1);110             }111             printf("Success to send datas\n");112             memset(sendBuf,0,MAX_DATA_SIZE);113         }114         */115     }116     close(sockfd);117 118     return 0;119 }

  下面截取运行结果

  

  一对多,server端向各个客户端发送数据(广播)

  接下来就趁热打铁吧,随便完成服务器向各个客户端发送数据,进而实现相互通讯。实现的技术细节是使用一个数组保存每次客户端连接的套接字。然后如果要服务器发送数据,就遍历数组中的所有客户端套接字,然后对每个套接字进行send数据。

  出现的问题,由于使用的是阻塞方式,所以要创建多进程,然而使用fork创建的进程是完整的拷贝父进程,所以其他进程accept一个新的连接后修改保存套接字的数组是不影响其他进程的数据的。查了一下,说有个vfork函数可以是父进程和子进程共享数据,但是最后发现,子进程是优先于父进程执行的,而且要子进程执行完后才会执行父进程。所以这个办法不行。一想进程间通信那么多办法总有可以的。就想到了使用信号处理。

  signal函数

  #include <signal.h>

  void (*signal(int signo, void (*func)(int)))(int); //signo是信号,func是捕获该信号后要处理的函数,称为“信号处理程序 signal handler” 我习惯叫这个函数为 注册函数,它就好像是对signo定义一个处理函数

  kill函数(kill函数是将信号发送给进程或进程组,一个相似的函数raise是给进程本身发送信号的)

  #include <signal.h>

  int kill(pid_t pid, int signo);    //成功返回0,出错返回-1 。pid>0 将该信号发送给进程ID为pid的进程, pid==0 将信号发给与本身进程属于同一个进程组的所有进程。 pid<0 将信号发送给其进程组ID等于pid的绝对值。

  int raise(int signo);  //成功返回0,出错返回-1

  好了,废话不说了,直接给代码。

  client.c

  这里的client代码与前面的基本相同。

  server.c

  1 #include <stdio.h>  2 #include <stdlib.h>  3 #include <errno.h>  4 #include <string.h>  5 #include <netdb.h>  6 #include <sys/types.h>  7 #include <sys/socket.h>  8 #include <sys/time.h>  9 #include <sys/un.h> 10 #include <sys/ioctl.h> 11 #include <sys/wait.h> 12 #include <netinet/in.h> 13 #include <arpa/inet.h> 14  15  16 #define SERVER_PORT 12138 17 #define BACKLOG 20 18 #define MAX_CON_NO 10 19 #define MAX_DATA_SIZE 4096 20  21 static void sig_usr1(int singno) 22 { 23     exit(1); 24 } 25  26 int main(int argc,char *argv[]) 27 { 28     struct sockaddr_in serverSockaddr,clientSockaddr; 29     char sendBuf[MAX_DATA_SIZE],recvBuf[MAX_DATA_SIZE]; 30     int sendSize,recvSize; 31     int sockfd,clientfd; 32     int on=1; 33     socklen_t sinSize=0; 34     char username[32]; 35     int pid; 36     int Queue[MAX_CON_NO+1]; 37     int queue_ptr; 38     int i; 39  40     if(argc != 2) 41     { 42         printf("usage: ./server [username]\n"); 43         exit(1); 44     } 45     strcpy(username,argv[1]); 46     printf("username:%s\n",username); 47  48     /*establish a socket*/ 49     if((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1) 50     { 51         perror("fail to establish a socket"); 52         exit(1); 53     } 54     printf("Success to establish a socket...\n"); 55  56     /*init sockaddr_in*/ 57     serverSockaddr.sin_family=AF_INET; 58     serverSockaddr.sin_port=htons(SERVER_PORT); 59     serverSockaddr.sin_addr.s_addr=htonl(INADDR_ANY); 60     bzero(&(serverSockaddr.sin_zero),8); 61  62     setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)); 63  64     /*bind socket*/ 65     if(bind(sockfd,(struct sockaddr *)&serverSockaddr,sizeof(struct sockaddr))==-1) 66     { 67         perror("fail to bind"); 68         exit(1); 69     } 70     printf("Success to bind the socket...\n"); 71  72     /*listen on the socket*/ 73     if(listen(sockfd,BACKLOG)==-1) 74     { 75         perror("fail to listen"); 76         exit(1); 77     } 78  79     sinSize=sizeof(clientSockaddr);//注意要写上,否则获取不了IP和端口 80     queue_ptr=0; 81     while(1)//多次accept 82     { 83         /*accept a client‘s request*/ 84         if((clientfd=accept(sockfd,(struct sockaddr  *)&clientSockaddr, &sinSize))==-1) 85         { 86             perror("fail to accept"); 87             exit(1); 88         } 89         printf("Success to accpet a connection request...\n"); 90         printf(">>>>>> %s:%d join in!\n",inet_ntoa(clientSockaddr.sin_addr),ntohs(clientSockaddr.sin_port)); 91         Queue[queue_ptr++]=clientfd; 92  93         if(pid!=0) 94         { 95             kill(pid,SIGUSR1); 96         } 97  98         if((pid=fork())<0) 99         {100             perror("fork error\n");101         }102         else if(pid==0)/*child*/103         {104             while(1)105             {106                 /*receive datas from client*/107                 if((recvSize=recv(clientfd,recvBuf,MAX_DATA_SIZE,0))==-1)108                 {109                     perror("fail to receive datas");110                     exit(1);111                 }112                 printf("Client:%s\n",recvBuf);113                 memset(recvBuf,0,MAX_DATA_SIZE);114             }115         }116 117         if((pid=fork())<0)118         {119             perror("fork error");120         }121         else if(pid==0)//child122         {123             /*send datas to client*/124             signal(SIGUSR1,sig_usr1);125             printf("现在有%d个人\n",queue_ptr);//没有考虑断开的问题126             while(1)127             {128                 fgets(sendBuf,MAX_DATA_SIZE,stdin);129                 for(i=0;i<queue_ptr;i++)130                 {131                     if((sendSize=send(Queue[i],sendBuf,strlen(sendBuf),0))!=strlen(sendBuf))132                     {133                         perror("fail to send datas");134                         exit(1);135                     }136                     else137                     {138                         printf("Success to send datas\n");139                     }140 141                 }142                 memset(sendBuf,0,MAX_DATA_SIZE);143             }144 145         }146     }147     close(sockfd);148 149     return 0;150 }

  贴一张运行时的截图

  一些关闭的操作没有处理好,反正功能是实现了,容错处理以后再慢慢改吧,下一节将要讲用select来代替这个处理操作。  

 

  本文地址: http://www.cnblogs.com/wunaozai/p/3870258.html

Socket网络编程--聊天程序(3)