zoukankan      html  css  js  c++  java
  • 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]
    ");
     35         exit(1);
     36     }
     37     strcpy(username,argv[1]);
     38     printf("username:%s
    ",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...
    ");
     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...
    ");
     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...
    ");
     81         printf(" %s:%d join in!
    ",inet_ntoa(clientSockaddr.sin_addr),ntohs(clientSockaddr.sin_port));
     82 
     83         if((pid=fork())<0)
     84         {
     85             perror("fork error
    ");
     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
    ",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
    ");
    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]
    ");
     43         exit(1);
     44     }
     45     strcpy(username,argv[1]);
     46     printf("username:%s
    ",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...
    ");
     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...
    ");
     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...
    ");
     90         printf(">>>>>> %s:%d join in!
    ",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
    ");
    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
    ",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)//child
    122         {
    123             /*send datas to client*/
    124             signal(SIGUSR1,sig_usr1);
    125             printf("现在有%d个人
    ",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                     else
    137                     {
    138                         printf("Success to send datas
    ");
    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

  • 相关阅读:
    MySQL主从复制-指定数据库复制
    linux下如何添加一个用户并且让用户获得root权限
    vim加密文件
    nginx_upstream_check_module-master对nginx的后端机器进行健康状态检查报403错误【转】
    Nginx配置SSL证书部署HTTPS网站
    MySQL数据备份之mysqldump使用
    nginx location语法使用说明
    Linux 使用 iptables屏蔽IP段
    nginx中获取真实ip
    在nginx中配置如何防止直接用ip访问服务器web server及server_name特性讲解
  • 原文地址:https://www.cnblogs.com/wunaozai/p/3870258.html
Copyright © 2011-2022 走看看