本地socket的select用法
来源:互联网 发布:bilibili 知乎 编辑:程序博客网 时间:2024/06/05 16:07
学习socket的select用法,下面代码大部分是从网上借鉴的,只不过把网络部分的socket改为了本地socket,并加了一些自己测试用的语句,如果有冒犯之处请联系我,我立马删除,谢谢。
client端
#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <netinet/in.h>#include <sys/types.h>#include <sys/socket.h>#include <arpa/inet.h>#include <sys/select.h>#include <sys/un.h>#include <sys/time.h>#define MAXLINE 5#define SERV_PORT 6000//!> 注意输入是由stdin,接受是由server发送过来//!> 所以在client端也是需要select进行处理的void send_and_recv( int connfd ){ FILE * fp =stdin; int lens; char send[MAXLINE]; char recv[MAXLINE]; fd_set rset; FD_ZERO(&rset ); int maxfd =( fileno( fp ) > connfd ? fileno( fp ) :connfd + 1); //!> 输入和输出的最大值 int n; int byte_size; int i; int nRet; struct timeval tmout; tmout.tv_sec = 0; tmout.tv_usec = 0; while( 1) { FD_ZERO(&rset); FD_SET(fileno( fp ), &rset ); FD_SET(connfd, &rset); //!> 注意不要把rset看作是简单的一个变量 //!> 注意它其实是可以包含一组套接字的哦, //!> 相当于是封装的数组!每次都要是新的哦! if( select(maxfd, &rset, NULL, NULL, &tmout ) == -1 ) { printf("Client Select Error..\n"); exit(EXIT_FAILURE ); } //!> if 连接口有信息 if(FD_ISSET( connfd, &rset )) //!> if 连接端口有信息 { printf("client get from server ...\n" ); memset(recv, 0, sizeof( recv ) ); n = read(connfd, recv, MAXLINE ); if( n == 0) { printf("Recvok...\n"); break; } else if( n== -1 ) { printf("Recverror...\n"); break; } else { lens =strlen( recv ); recv[lens] ='\0'; //!> 写到stdout write(STDOUT_FILENO, recv, MAXLINE ); printf("\n"); byte_size = ( strlen(recv) ) * sizeof(char); printf("Client recv bytes : %d\n",byte_size); } } //!> if 有stdin输入 if(FD_ISSET( fileno( fp ), &rset )) //!> if 有输入 { //!> printf("client stdin ...\n"); memset(send, 0, sizeof( send ) ); if( fgets(send, MAXLINE, fp ) == NULL ) { printf("End...\n"); exit(EXIT_FAILURE ); } else { //!>if( str ) lens =strlen( send ); send[lens-1]= '\0'; //!> 减一的原因是不要回车字符 //!> 经验值:这一步非常重要的哦!!!!!!!! if( strcmp(send, "q" ) == 0 ) { printf("Bye..\n" ); return; } for(i=0; i<5; i++) //测试,连续发五次,看server的返回情况 { printf("Client send : %s\n", send); byte_size = ( strlen(send) ) * sizeof(char); printf("Client send bytes : %d\n",byte_size); write(connfd, send, strlen( send )); } memset(send, 0, sizeof( send ) ); } } } }int main( int argc, char ** argv ){ //!> char * SERV_IP = "10.30.97.188"; char buf[MAXLINE]; int connfd; struct sockaddr_un servaddr; // if( argc !=2 ) { // printf("Input server ip !\n"); // exit(EXIT_FAILURE ); } //!> 建立套接字 if( ( connfd= socket( AF_UNIX, SOCK_STREAM, 0 ) ) == -1 ) { printf("Socket Error...\n" , errno ); exit(EXIT_FAILURE ); } //!> 套接字信息 // bzero(&servaddr, sizeof(servaddr)); servaddr.sun_family = AF_UNIX; strcpy(servaddr.sun_path,"selectfile"); // servaddr.sin_port = htons(SERV_PORT); // inet_pton(AF_INET, argv[1],&servaddr.sin_addr); //!> 链接server if( connect(connfd, ( struct sockaddr * )&servaddr, sizeof( servaddr ) ) < 0) { printf("Connect error..\n"); exit(EXIT_FAILURE); } //!> //!> send and recv send_and_recv( connfd ); //!> close(connfd ); printf("Exit\n"); return 0;}这个代码有个地方需要注意:send[lens-1]= '\0';这一句让我测试的时候吃了点苦头,我定义的数据大小为5字节,但我每次发送3字节数据的时候,server端可以正常返回,但发送4字节或5字节的时候,server只返回前三个字节,举个例子,我输入123,client会连续发送五次123,server也给返回五次123,;如果我输入12345,client发送五次123,server返回五次123,但马上我又按下回车,这时client会先连续发五次5,然后再发五次0,server端给回五个5,却不是分五次回(两次、三次?次数不一定,很是郁闷),我认为这是缓冲区堆积的原因导致的(若果认识错误请指摘,谢谢)。
server端
#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <netinet/in.h>#include <sys/select.h>#include <sys/types.h>#include <sys/socket.h>#include <sys/un.h> #include <sys/time.h>#define BUF_LEN 3#define SERV_PORT 6000#define FD_SIZE 100#define MAX_BACK 100#define SOC_FD "selectfile"int main( int argc, char ** argv ){ unlink(SOC_FD); int listenfd,connfd, sockfd, maxfd, maxi, i; int nready,client[FD_SIZE]; //!> 接收select返回值、保存客户端套接字 int lens; ssize_t n; //!> read字节数 fd_set rset,allset; //!> 不要理解成就只能保存一个,其实fd_set有点像封装的数组 char buf[BUF_LEN]; socklen_t clilen; struct sockaddr_un servaddr, chiaddr; int j,nRet; struct timeval tmout; tmout.tv_sec = 0; tmout.tv_usec = 0; if( (listenfd = socket( AF_UNIX, SOCK_STREAM, 0 ) ) == -1 ) { printf("Create socket Error : %d\n", errno ); exit(EXIT_FAILURE ); } //!> //!> 下面是接口信息 // bzero(&servaddr, sizeof( servaddr ) ); servaddr.sun_family = AF_UNIX; strcpy(servaddr.sun_path, SOC_FD); //servaddr.sin_addr.s_addr =htonl( INADDR_ANY); // servaddr.sin_port = htons( SERV_PORT ); //!> //!> 绑定 if( bind(listenfd, ( struct sockaddr * )&servaddr, sizeof(servaddr ) ) == -1 ) { printf("BindError : %d\n", errno); exit(EXIT_FAILURE ); } //!> //!> 监听 if( listen(listenfd, MAX_BACK ) == -1 ) { printf("Listen Error : %d\n", errno ); exit(EXIT_FAILURE ); } //!> 当前最大的感兴趣的套接字fd maxfd =listenfd; //!> 当前可通知的最大的fd maxi =-1; //!> 仅仅是为了client数组的好处理 for( i = 0;i < FD_SIZE; i++) //!> 首先置为全-1 { client[i] =-1; //!> 首先client的等待队列中是没有的,所以全部置为-1 } FD_ZERO(&allset); //!> 先将其置为0 FD_SET(listenfd, &allset ); //!> 说明当前我对此套接字有兴趣,下次select的时候通知我! while( 1) { rset =allset;//!> 由于allset可能每次一个循环之后都有变化,所以每次都赋值一次 if( (nready= select( maxfd + 1, &rset, NULL, NULL, &tmout )) ==-1) { //!> if 存在关注 printf("Select Erorr : %d\n", errno ); exit(EXIT_FAILURE ); } if( nready<= 0) //!> if 所有的感兴趣的没有就接着回去select { continue; } if(FD_ISSET( listenfd, &rset )) //!> if 是监听接口上的“来电” { //!> //!> printf("server listen ...\n"); clilen =sizeof( chiaddr ); printf("Start doing... \n"); if( (connfd = accept( listenfd, (struct sockaddr*)&chiaddr, &clilen ) ) == -1) { //!> accept 返回的还是套接字 printf("Accept Error : %d\n", errno ); continue; } for( i = 0;i < FD_SIZE; i++) //!> 注意此处必须是循环,刚开始我认 //!> 为可以直接设置一个end_i来直接处 //!> 理,实质是不可以的!因为每个套接 { //!> 字的退出时间是不一样的,后面的 if(client[i] < 0) //!> 可能先退出,那么就乱了,所以只 { //!> 有这样了! client[i] =connfd; //!> 将client的请求连接保存 break; } } if( i ==FD_SIZE ) //!> The last one { printf( "Tomany ... " ); close(connfd ); //!> if 满了那么就不连接你了,关闭吧 continue; //!> 返回 } //!> listen的作用就是向数组中加入套接字! FD_SET(connfd, &allset); //!> 说明现在对于这个连接也是感兴趣的! //!> 所以加入allset的阵容 if( connfd> maxfd) //!> 这个还是为了解决乱七八糟的数组模型 //!> 的处理 { maxfd =connfd; } if( i> maxi) //!> 同上 { maxi =i; } } //!> 下面就是处理数据函数( 其实说本质的select还是串行 ) for( i = 0;i <= maxi; i++) //!> 对所有的连接请求的处理 { if( ( sockfd= client[i] ) > 0) //!> 还是为了不规整的数组 { //!> 也就说client数组不是连续的全正数或者-1,可能是锯齿状的 if(FD_ISSET( sockfd, &rset )) //!> if 当前这个数据套接字有要读的 { memset( buf,0, sizeof( buf )); //!> 此步重要,不要有时候出错 sleep(1); n = read(sockfd, buf, BUF_LEN); if( n< 0 ) { printf("Error!\n"); close(sockfd ); //!> 说明在这个请求端口上出错了! FD_CLR(sockfd, &allset ); client[i] =-1; continue; } if( n == 0) { printf("nodata\n"); close(sockfd ); //!> 说明在这个请求端口上读完了! FD_CLR(sockfd, &allset ); client[i] =-1; continue; } buf[n]= '\0'; printf("Server Recv: %s\n", buf); if( strcmp(buf, "q" ) == 0) //!> 客户端输入“q”退出标志 { close(sockfd ); FD_CLR(sockfd, &allset ); client[i] =-1; continue; } printf("Server send : %s\n", buf); write(sockfd, buf, n); //!> 读出来的写进去 printf("\n"); memset( buf,0, sizeof( buf )); //!> 此步重要,不要有时候出错 } } } } return 0;}
server端也有改动:sleep(1);有人会说,select的用法就是要让消息立马返回,不阻塞线程,但这里是因为我的这个小demo在client端有个for循环(连续发送五次数据),上面也说了,当发送快于接收,缓冲区堆积,还有,网络(本地感觉也一样,可以把server端看作是一种网络服务端,总感觉这么理解怪怪的,希望有高手能告诉我一下应该怎么解释比较正确)写函数是不负责将数据全部写完之后再返回的。基于以上两点,我就加了sleep,如果不加,client连续发五次消息(假设是123),server会给回5个123,但不一定是分五次回,有可能是两次、三次或五次等。
最后,推荐一份资料,《Linux程序设计(第4版)》,其实第几版无所谓,内容差不多,这本资料的第15章套接字,里面很详细的介绍了socket的用法,建议想要了解socket的兄弟们去看下,最后的最后,欢迎各路大神指摘我的博客,有错改之才能进步,谢谢。
阅读全文
2 0
- 本地socket的select用法
- Socket select的用法
- Socket中select()的用法
- 非阻塞式socket的select()用法
- socket select()用法
- socket select()用法
- socket select()用法
- Socket select()用法
- socket select()用法
- socket select()用法
- socket select()用法
- socket select()用法
- socket select()用法
- linux socket中的select用法
- socket编程:select 用法 例子
- linux socket中的select用法
- TCP socket select用法分析
- Socket编程中select函数的用法详解
- [IOS APP]遇见未知的自己-现代修心有声小说
- 网易编程-最大的奇数约数c++实现
- 偏导数
- Java开发中JDBC连接数据库
- 关于java连接redis数据库报: Connection refused: connect 的问题
- 本地socket的select用法
- 算法作业8.9
- 2015年蓝桥杯决赛 C/C++大学A组第一题
- AI会不会取代人类?
- ACdream 1055 Crayon (离散化+树状数组)
- 在主方法中创建map集合中存储3个学生对象.key使用字符串表示编号(不能重复)value是学生对象。然后调用以下两个方法;
- Struts2_获取servletAPI
- Servlet
- c++ 大小写转换&&字符转数字