基于linux poll模型的tcp服务器------一个服务器如何与多个客户端进行通信?
2017-06-24 16:06
906 查看
在之前的博文中, 我们一起玩了Windows下的select函数, 实现了一个服务器与多个客户端进行通信。 如果一直到linux, 那几乎是大同小异的。 现在, 我们不说select, 而来接续说说linux epoll.
在前面的博文中, 我们也介绍了linux poll检测键盘终端输入, 主要是想在网络编程之前, 对epoll的用法有个基本的了解。现在, 来玩一下基于linux poll模型的tcp服务器------一个服务器与多个客户端进行通信。
服务端代码如下:
客户端代码如下:
编译链接后, 先启动服务端, 然后分别在同一linux机器的三个窗口上启动三个客户端, 此时, 服务端的结果为:
好了, 实现了简单的一个服务器与多个客户端进行通信。 随后, 这三个客户端可以继续与服务端进行同行, 同时, 服务端也允许更多的新客户端来连接。 当然, 客户端也可以选择断开连接(ctrl c). 多的不说, 玩一下程序就明白了, 上述poll的用法值得好好体会, 可以看到, 跟linux几乎没有太大的差别, 上述程序如果转成select模型,那也是易如反掌的。
最后, 思考一个有意思的小问题, 为什么上述的fd是4, 5, 6 ?
在前面的博文中, 我们也介绍了linux poll检测键盘终端输入, 主要是想在网络编程之前, 对epoll的用法有个基本的了解。现在, 来玩一下基于linux poll模型的tcp服务器------一个服务器与多个客户端进行通信。
服务端代码如下:
#include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <errno.h> #include <malloc.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/ioctl.h> #include <stdarg.h> #include <fcntl.h> #include <fcntl.h> #include <sys/poll.h> #define BACKLOG 100 #if 0 struct pollfd { int fd; //文件描述符 short events; //请求的事件 short revents; //返回的事件 }; #endif int main() { int iListenSock = socket(AF_INET, SOCK_STREAM, 0); int arrayConnSock[BACKLOG] = {0}; // 这个初始化并不好,但其实无大碍 sockaddr_in addr; memset(&addr, 0, sizeof(addr)); inet_aton("0.0.0.0", &addr.sin_addr); addr.sin_family = AF_INET; addr.sin_port = htons(8888); int iOpt = 1; setsockopt(iListenSock, SOL_SOCKET, SO_REUSEADDR, &iOpt, sizeof(iOpt)); // 标配 bind(iListenSock, (sockaddr*)&addr, sizeof(addr)); listen(iListenSock, 100); int flag = -1; pollfd fds[BACKLOG + 1]; // BACKLOG个通信socket和1个监听socket memset(fds, flag, sizeof(fds)); // 设置一个标志 fds[0].fd = iListenSock; // 监听socket fds[0].events = POLLIN; int ndfs = 1; int timeoutMS = -1; // 永不超时 int i = 0; for(int i = 1; i <= BACKLOG; i++) { int iRet = poll(fds, ndfs, timeoutMS); if (iRet < 0) { printf("poll error, iRet %d\n", iRet); continue; } if (iRet < 0) { printf("poll error, iRet %d\n", iRet); continue; } if (fds[0].revents & POLLIN) // fds[0]对应iListenSock, 此处表明检测到有新客户端连接过来 { arrayConnSock[i - 1] = accept(iListenSock, NULL, NULL); // arrayConnSock是通信socket fds[i].fd = arrayConnSock[i - 1]; fds[i].events = POLLIN; ndfs++; printf("new client came, local fd is [%d]\n", arrayConnSock[i - 1]); } for (int j = 0; j < BACKLOG; j++) { char szBuf[1024] = {0}; if (arrayConnSock[j] > 0 && (fds[j + 1].revents & POLLIN)) // 此处检测的是已经链接的socket的读事件 { int recvLen = recv(arrayConnSock[j], szBuf, sizeof(szBuf) - 1, 0); if (recvLen > 0) { printf("recv data [%s] from local fd [%d] \n", szBuf, arrayConnSock[j]); } else if(0 == recvLen) { close(arrayConnSock[j]); memset(&fds[j + 1], flag, sizeof(pollfd)); printf("connection closed fd [%d]\n", arrayConnSock[j]); arrayConnSock[j] = 0; } else { close(arrayConnSock[j]); memset(&fds[j + 1], flag, sizeof(pollfd)); printf("recv error, recvLen is %d", recvLen); arrayConnSock[j] = 0; } } } } close(iListenSock); return 0; }
客户端代码如下:
#include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netdb.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <errno.h> #include <malloc.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/ioctl.h> #include <stdarg.h> #include <fcntl.h> #include <fcntl.h> int main() { int sockClient = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addrSrv; addrSrv.sin_addr.s_addr = inet_addr("127.0.0.1"); addrSrv.sin_family = AF_INET; addrSrv.sin_port = htons(8888); connect(sockClient, ( const struct sockaddr *)&addrSrv, sizeof(struct sockaddr_in)); char szSendBuf[100] = "this is me"; while(1) { send(sockClient, szSendBuf, strlen(szSendBuf) + 1, 0); scanf("%s", szSendBuf); } close(sockClient); return 0; }makefile代码如下:
all: server client server: server.o g++ -o server server.o client: client.o g++ -o client client.o server.o: server.cpp g++ -c server.cpp client.o:client.cpp g++ -c client.cpp clean: rm -f server client *.o
编译链接后, 先启动服务端, 然后分别在同一linux机器的三个窗口上启动三个客户端, 此时, 服务端的结果为:
xxxxxx:~/network> ./server new client came, local fd is [4] recv data [this is me] from local fd [4] new client came, local fd is [5] recv data [this is me] from local fd [5] new client came, local fd is [6] recv data [this is me] from local fd [6]
好了, 实现了简单的一个服务器与多个客户端进行通信。 随后, 这三个客户端可以继续与服务端进行同行, 同时, 服务端也允许更多的新客户端来连接。 当然, 客户端也可以选择断开连接(ctrl c). 多的不说, 玩一下程序就明白了, 上述poll的用法值得好好体会, 可以看到, 跟linux几乎没有太大的差别, 上述程序如果转成select模型,那也是易如反掌的。
最后, 思考一个有意思的小问题, 为什么上述的fd是4, 5, 6 ?
相关文章推荐
- 基于select模型的tcp服务器------一个服务器如何与多个客户端进行通信?
- 基于非阻塞socket的多线程服务器的实现------一个服务器如何与多个客户端进行通信?
- 初玩linux epoll------一个服务器如何与多个客户端进行通信? (笔试面试常考)
- LINUX下如何创建TCP客户端和服务器,实现通信
- linux环境下搭建一个基于tcp的客户端和服务器
- 一个简单的基于node.js的TCP服务器和基于C++的TCP客户端通信示例程序
- 如何使用TCP/IP与服务器进行通信-一个简单的聊天程序
- 如何使用TCP/IP与服务器进行通信-一个简单的聊天程序
- Linux系统tcp模式下服务器与客户端通信(仅限一个客户端)
- Linux基于TCP/IP简单的客户端、服务器通信程序实例
- Linux基于TCP/IP简单的客户端、服务器通信程序实例
- Android_TCP/IP开发——聊天室模型(客户端与服务器进行通信)
- linux下网络编程2:服务端和客户端进行TCP通信实例
- C#基于TCP&UDP实现服务器与多个客户端之间的通信(客户端之间直接通信,不靠服务器端转发消息)
- 基于TCP网络通信的自动升级程序源码分析-客户端连接服务器
- 基于TCP网络通信的自动升级程序源码分析-客户端请求服务器上的升级信息
- Linux系统编程:客户端-服务器用FIFO进行通信
- 笔试题14:用TCP通信模型创建一个Web服务器(源码)
- 基于TCP网络通信的自动升级程序源码分析-客户端请求服务器上的升级信息
- C++基于TCP/IP简单的客户端、服务器通信程序实例