poll聊天室程序
客户端:
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <sys/epoll.h>
#include <pthread.h>
#include <sys/poll.h>
#include <iostream>#pragma clang diagnostic push
#pragma ide diagnostic ignored "EndlessLoop"int main(int argc, char *argv[])
{if (argc <= 3){printf("please enter right arg!");return -1;}const char *ip = argv[1];int port = atoi(argv[2]);sockaddr_in server_address;bzero(&server_address, sizeof(server_address));server_address.sin_family = AF_INET;inet_pton(AF_INET, ip, &server_address.sin_addr);server_address.sin_port = htons(port);int sockfd = socket(PF_INET, SOCK_STREAM, 0);assert(sockfd > 0);if (connect(sockfd, (sockaddr *) &server_address, sizeof(sockaddr)) < 0){perror("connect");close(sockfd);return -1;}pollfd fds[2];//register fd of stdout and sockfdfds[0].fd = 0;fds[0].events = POLLIN;fds[0].revents = 0;fds[1].fd = sockfd;fds[1].events = POLLIN | POLLRDHUP;fds[1].revents = 0;char read_buf[1024];int pipefd[2];int ret = pipe(pipefd);assert(ret != -1);while (true){ret = poll(fds, 2, -1);if (ret < 0){printf("poll failure");close(sockfd);break;}if (fds[1].revents & POLLRDHUP){printf("server close connection ");break;} else if (fds[1].revents & POLLIN){memset(read_buf, 0, 1024);recv(fds[1].fd, read_buf, 1024 - 1, 0);printf("%s\n",read_buf); //iostream 真是C++一个败笔,真不好用}if (fds[0].revents & POLLIN){ret = splice(0, nullptr, pipefd[1], nullptr, 32768, SPLICE_F_MORE | SPLICE_F_MOVE);ret = splice(pipefd[1], nullptr, sockfd, nullptr, 32768, SPLICE_F_MOVE | SPLICE_F_MORE);}}close(sockfd);return 0;
}#pragma clang diagnostic pop
服务端:
#define _GNU_SOURCE 1
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <poll.h>#define USER_LIMIT 5
#define BUFFER_SIZE 64
#define FD_LIMIT 65535struct client_data
{sockaddr_in address;char* write_buf;char buf[ BUFFER_SIZE ];
};int setnonblocking( int fd )
{int old_option = fcntl( fd, F_GETFL );int new_option = old_option | O_NONBLOCK;fcntl( fd, F_SETFL, new_option );return old_option;
}int main( int argc, char* argv[] )
{if( argc <= 2 ){printf( "usage: %s ip_address port_number\n", basename( argv[0] ) );return 1;}const char* ip = argv[1];int port = atoi( argv[2] );int ret = 0;struct sockaddr_in address;bzero( &address, sizeof( address ) );address.sin_family = AF_INET;inet_pton( AF_INET, ip, &address.sin_addr );address.sin_port = htons( port );int listenfd = socket( PF_INET, SOCK_STREAM, 0 );assert( listenfd >= 0 );ret = bind( listenfd, ( struct sockaddr* )&address, sizeof( address ) );assert( ret != -1 );ret = listen( listenfd, 5 );assert( ret != -1 );client_data* users = new client_data[FD_LIMIT];pollfd fds[USER_LIMIT+1];int user_counter = 0;for( int i = 1; i <= USER_LIMIT; ++i ){fds[i].fd = -1;fds[i].events = 0;}fds[0].fd = listenfd;fds[0].events = POLLIN | POLLERR;fds[0].revents = 0;while( 1 ){ret = poll( fds, user_counter+1, -1 );if ( ret < 0 ){printf( "poll failure\n" );break;}for( int i = 0; i < user_counter+1; ++i ){if( ( fds[i].fd == listenfd ) && ( fds[i].revents & POLLIN ) ){struct sockaddr_in client_address;socklen_t client_addrlength = sizeof( client_address );int connfd = accept( listenfd, ( struct sockaddr* )&client_address, &client_addrlength );if ( connfd < 0 ){printf( "errno is: %d\n", errno );continue;}if( user_counter >= USER_LIMIT ){const char* info = "too many users\n";printf( "%s", info );send( connfd, info, strlen( info ), 0 );close( connfd );continue;}user_counter++;users[connfd].address = client_address;setnonblocking( connfd );fds[user_counter].fd = connfd;fds[user_counter].events = POLLIN | POLLRDHUP | POLLERR;fds[user_counter].revents = 0;printf( "comes a new user, now have %d users\n", user_counter );}else if( fds[i].revents & POLLERR ){printf( "get an error from %d\n", fds[i].fd );char errors[ 100 ];memset( errors, '\0', 100 );socklen_t length = sizeof( errors );if( getsockopt( fds[i].fd, SOL_SOCKET, SO_ERROR, &errors, &length ) < 0 ){printf( "get socket option failed\n" );}continue;}else if( fds[i].revents & POLLRDHUP ){users[fds[i].fd] = users[fds[user_counter].fd];close( fds[i].fd );fds[i] = fds[user_counter];i--;user_counter--;printf( "a client left\n" );}else if( fds[i].revents & POLLIN ){int connfd = fds[i].fd;memset( users[connfd].buf, '\0', BUFFER_SIZE );ret = recv( connfd, users[connfd].buf, BUFFER_SIZE-1, 0 );printf( "get %d bytes of client data %s from %d\n", ret, users[connfd].buf, connfd );if( ret < 0 ){if( errno != EAGAIN ){close( connfd );users[fds[i].fd] = users[fds[user_counter].fd];fds[i] = fds[user_counter];i--;user_counter--;}}else if( ret == 0 ){printf( "code should not come to here\n" );}else{for( int j = 1; j <= user_counter; ++j ){if( fds[j].fd == connfd ){continue;}fds[j].events |= ~POLLIN;fds[j].events |= POLLOUT;users[fds[j].fd].write_buf = users[connfd].buf;}}}else if( fds[i].revents & POLLOUT ){int connfd = fds[i].fd;if( ! users[connfd].write_buf ){continue;}ret = send( connfd, users[connfd].write_buf, strlen( users[connfd].write_buf ), 0 );users[connfd].write_buf = NULL;fds[i].events |= ~POLLOUT;fds[i].events |= POLLIN;}}}delete [] users;close( listenfd );return 0;
}