利用socket搭建一个多客户端/服务器的框架
2013-05-15 10:06
399 查看
在设计分布式系统或者涉及到及时通信的相关内容时,我们常常需要利用socket(套接字)来进行网络通信及文件传输,如果只是一个客户端跟一个服务器端进行通信,那么这将是一件很好办的事情,但是实际情况往往是有多个客户端需要跟服务器端进行通信,那么在这种情况下我们如何实现呢?
我们先从原理出发:客户端和服务器端一旦建立连接,套接字连接的行为就类似于打开的底层文件描述符,而且在很多方面类似于双向管道。当考虑到多个客户同时连接一个服务器的时候,我们可以看到,服务器程序在接受来自客户的一个新的连接的时候,会创建出一个新的套接字,而原先的监听套接字将被保留以继续监听以后的连接。如果服务器不能立刻接受后来的连接,他们将被放到队列中以等待处理。
原先的套接字仍然可用并且套接字的行为就像文件描述符,这一事实给我们提供了一种同时服务多个客户的方法,如果服务器调用fork()为自己创建第二份副本,打开的套接字就将被新的子进程所继承。新的子进程可以和连接的客户进行通信,而服务器进程可以继续接受以后的客户连接,这是我们所说的第一种方式:创建子进程。这里要注意的是当我们创建好子进程之后,我们并不等待它们完成,所以必须安排服务器忽略SIGCHLD信号以避免出现僵尸进程。
这里我们着重要描述的是第二种方式:通过select系统调用来同时处理多个客户从而避免依赖于子进程。服务器可以让select系统调用同时检查监听套接字和客户的连接套接字。一旦select调用指示有活动发生,就可以用FD_ISSET来遍历所有可能的文件描述符,以检查是那个上面有活动发生。
如果是监听套接字可读,这说明正有一个客户试图建立连接,此时就可以调用accept而不用担心发生阻塞的可能。如果是某个客户描述符准备好了,这说明该描述符上有一个客户请求需要我们读取和处理。如果读操作返回零字节,这表示有一个客户进程已经结束,你可以关闭该套接字并把它从描述符集合中删除。
下面给出完整的可运行C程序示例,我在中间做了英文注释:
服务器端代码:server.cpp
客户端:client.c
在linux下使用gcc编译成可执行文件后,运行./server启动服务端
然后使用./client & ./client & ./client 来模拟三个客户端访问时的操作情况。
我们先从原理出发:客户端和服务器端一旦建立连接,套接字连接的行为就类似于打开的底层文件描述符,而且在很多方面类似于双向管道。当考虑到多个客户同时连接一个服务器的时候,我们可以看到,服务器程序在接受来自客户的一个新的连接的时候,会创建出一个新的套接字,而原先的监听套接字将被保留以继续监听以后的连接。如果服务器不能立刻接受后来的连接,他们将被放到队列中以等待处理。
原先的套接字仍然可用并且套接字的行为就像文件描述符,这一事实给我们提供了一种同时服务多个客户的方法,如果服务器调用fork()为自己创建第二份副本,打开的套接字就将被新的子进程所继承。新的子进程可以和连接的客户进行通信,而服务器进程可以继续接受以后的客户连接,这是我们所说的第一种方式:创建子进程。这里要注意的是当我们创建好子进程之后,我们并不等待它们完成,所以必须安排服务器忽略SIGCHLD信号以避免出现僵尸进程。
这里我们着重要描述的是第二种方式:通过select系统调用来同时处理多个客户从而避免依赖于子进程。服务器可以让select系统调用同时检查监听套接字和客户的连接套接字。一旦select调用指示有活动发生,就可以用FD_ISSET来遍历所有可能的文件描述符,以检查是那个上面有活动发生。
如果是监听套接字可读,这说明正有一个客户试图建立连接,此时就可以调用accept而不用担心发生阻塞的可能。如果是某个客户描述符准备好了,这说明该描述符上有一个客户请求需要我们读取和处理。如果读操作返回零字节,这表示有一个客户进程已经结束,你可以关闭该套接字并把它从描述符集合中删除。
下面给出完整的可运行C程序示例,我在中间做了英文注释:
服务器端代码:server.cpp
#include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <netinet/in.h> #include <sys/time.h> #include <sys/ioctl.h> #include <unistd.h> #include <stdlib.h> int main() { int server_socketfd, client_socketfd; int server_len, client_len; struct sockaddr_in server_address; struct sockaddr_in client_address; int result; fd_set readfds, testfds; //create a socket for server server_socketfd = socket(AF_INET, SOCK_STREAM, 0); server_address.sin_family = AF_INET; server_address.sin_addr.s_addr = htonl(INADDR_ANY); server_address.sin_port = htons(atoi("9092")); server_len = sizeof(server_address); bind(server_socketfd, (struct sockaddr *)&server_address, server_len); //create a connection queue, init readfds to process the input of server_sockfd listen(server_socketfd, 5); FD_ZERO(&readfds); FD_SET(server_socketfd,&readfds); //wait for the ask of clients while(1) { char ch; int fd; int nread; testfds = readfds; printf("server waiting...\n"); result = select(FD_SETSIZE, &testfds, (fd_set *)0, (fd_set *)0, (struct timeval *)0); if(result < 1) { perror("server"); exit(1); } //once you know the activity happen,use FD_ISSET to inspect every fd to find which one did the activity happen for(fd = 0; fd < FD_SETSIZE; fd++) { if(FD_ISSET(fd, &testfds)) { //if the activity is happened in server_socketfd, it means it is a new connection if(fd == server_socketfd){ client_len = sizeof(client_address); client_socketfd = accept(server_socketfd, (struct sockaddr *)&client_address, &client_len); FD_SET(client_socketfd, &readfds); printf("adding client on fd %d\n", client_socketfd); } //if the activity is not happened in server_socketfd, it means the client has left else { ioctl(fd, FIONREAD, &nread); if(nread == 0) { close(fd); FD_CLR(fd, &readfds); printf("removeing client on fd %d\n", fd); } else { read(fd, &ch, 1); sleep(5); printf("serving client on fd %d\n", fd); ch++; write(fd, &ch, 1); } } } } } }
客户端:client.c
#include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdlib.h> int main() { char ch = 'A'; int sockfd; struct sockaddr_in skaddr; if ((sockfd =socket(AF_INET, SOCK_STREAM, 0)) <0 ) { printf("Problem creating socket \n"); return -1; } skaddr.sin_family = AF_INET; inet_aton("127.0.0.1", &skaddr.sin_addr); skaddr.sin_port = htons (atoi("9092"));//port number if (connect (sockfd, (struct sockaddr *) &skaddr, sizeof(skaddr))<0) { printf("Problem connecting socket \n"); return -1; } write(sockfd, &ch, 1); read(sockfd, &ch, 1); printf("char from server = %c\n", ch); close(sockfd); exit(0); }
在linux下使用gcc编译成可执行文件后,运行./server启动服务端
然后使用./client & ./client & ./client 来模拟三个客户端访问时的操作情况。
相关文章推荐
- C++利用socket的客户端和服务器之间传输文件
- [PHP]利用XAMPP搭建本地服务器, 然后利用iOS客户端上传数据到本地服务器中(二.配置MySQL数据库)
- iOS--SocketRocket框架的使用及测试服务器的搭建
- Linux C利用Socket套接字进行服务器与多个客户端进行通讯
- [PHP]利用XAMPP搭建本地服务器, 然后利用iOS客户端上传数据到本地服务器中(三. PHP端代码实现)
- 利用Python中SocketServer 实现客户端与服务器间非阻塞通信
- Android开发——本机Tomcat搭建服务器,客户端socket网络连接不上问题以及D-LINK端口转发设置
- 局域网LanQQ聊天项目(二)服务器和客户端整体框架的搭建
- 利用 Socket 实现服务器客户端聊天
- [PHP]利用XAMPP搭建本地服务器, 然后利用iOS客户端上传数据到本地服务器中(四. iOS端代码实现)
- workerman高性能socket服务器框架搭建
- Java 利用套接字Socket实现简单的服务器与客户端通信
- 利用Python中SocketServer 实现客户端与服务器间非阻塞通信
- 搭建一个服务器框架,进程间利用管道通信,线程处理数据
- Java 利用套接字Socket实现简单的服务器与客户端通信
- Android 使用mina框架 搭建socket客户端,进行与服务器端通信
- 利用Flash作为桥梁连接WEB页面和服务器端Socket通信,达到服务器主动"推"客户端的效果
- Linux C利用Socket套接字进行服务器与多个客户端进行通讯
- [PHP]利用XAMPP搭建本地服务器, 然后利用iOS客户端上传数据到本地服务器中(一.安装XAMPP)
- 利用xutils框架在Android上传多个文件到Struts搭建的java服务器