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

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

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

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

  close函数

  #include

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

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

  client.c

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

  server.c

  1 #include 
  2 #include 
  3 #include 
  4 #include <string.h>
  5 #include 
  6 #include 
  7 #include 
  8 #include 
  9 #include 
 10 #include 
 11 #include 
 12 #include in.h>
 13 #include 
 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

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

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

  #include

  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 
  2 #include 
  3 #include 
  4 #include <string.h>
  5 #include 
  6 #include 
  7 #include 
  8 #include 
  9 #include 
 10 #include 
 11 #include 
 12 #include in.h>
 13 #include 
 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)
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

更多相关:

  • 关于点云的分割算是我想做的机械臂抓取中十分重要的俄一部分,所以首先学习如果使用点云库处理我用kinect获取的点云的数据,本例程也是我自己慢慢修改程序并结合官方API 的解说实现的,其中有很多细节如果直接更改源程序,可能会因为数据类型,或者头文件等各种原因编译不过,会导致我们比较难得找出其中的错误,首先我们看一下我自己设定的一个场景,...

  • /* 使用正态分布变换进行配准的实验 。其中room_scan1.pcd room_scan2.pcd这些点云包含同一房间360不同视角的扫描数据 */ #include #include #include #include

  • #include #include #include #include ...

  • #include #include #include #include #include #include...

  • #include #include #include #include int main (int argc,...

  •         Apache POI是一个开源的利用Java读写Excel,WORD等微软OLE2组件文档的项目。        我的需求是对Excel的数据进行导入或将数据以Excel的形式导出。先上简单的测试代码:package com.xing.studyTest.poi;import java.io.FileInputSt...

  • 要取得[a,b)的随机整数,使用(rand() % (b-a))+ a; 要取得[a,b]的随机整数,使用(rand() % (b-a+1))+ a; 要取得(a,b]的随机整数,使用(rand() % (b-a))+ a + 1; 通用公式:a + rand() % n;其中的a是起始值,n是整数的范围。 要取得a到b之间的...

  • 利用本征图像分解(Intrinsic Image Decomposition)算法,将图像分解为shading(illumination) image 和 reflectance(albedo) image,计算图像的reflectance image。 Reflectance Image 是指在变化的光照条件下能够维持不变的图像部分...

  • 题目:面试题39. 数组中出现次数超过一半的数字 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。 你可以假设数组是非空的,并且给定的数组总是存在多数元素。 示例 1: 输入: [1, 2, 3, 2, 2, 2, 5, 4, 2] 输出: 2 限制: 1 <= 数组长度 <= 50000 解题: cl...

  • 题目:二叉搜索树的后序遍历序列 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。 参考以下这颗二叉搜索树:      5     /    2   6   /  1   3示例 1: 输入: [1,6,3,2,5] 输出...

  • We have ZZIPlib installed.My command configure line looks like :./configure ?with-apxs ?with-curl ?with-curl-dir=/usr/local/lib ?with-gd ?with-gd-dir=/usr/local ?with-g...

  • asar Whether to package the application’s source code into an archive, using Electron’s archive format. Defaults to true. Node modules, that must be unpacked, will be d...

  • 1.      今天遇到一问题,在sles11/vxworks下编译通过,但是在hpux下失败 2.      编译错误: /usr/ccs/bin/ld:DP relative code in file /projects/xxx/DERIVED/tfa_pa32-hpux.a(tfa02_pa32-hpux.o) -share...

  •         最近买个了小本lenovo x100e,结果发现这小本没有大小写指示灯,在windows用也无妨,不过我常常用这本在ubuntu中调试linux代码,vi 常用的编辑器,熟悉的都知道,大小写很关键的,用google搜了一下,发现可以用如下方法解决:        1.  “sudo apt-get install l...

  •   修改Ubuntu的启动logo 原文链接: https://my.oschina.net/jmjoy/blog/380262     内容:   Plymouth splash screen is the initial splash screen at boot-up.Ubuntu 10.04 uses Plym...