linux 网络编程---->多路复用:select实例!
2012-03-20 11:55
411 查看
好吧,我承认找了好久,网上都没有像样的完整的实例,然后自己参照书自己写一个吧!
//!> server端代码
//!>server.c
#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>
#define BUF_LEN 1024
#define SERV_PORT 6000
#define FD_SIZE 100
#define MAX_BACK 100
int main( int argc, char ** argv )
{
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;
structsockaddr_in servaddr, chiaddr;
if( (listenfd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 )
{
printf("Create socket Error : %d\n", errno );
exit(EXIT_FAILURE );
}
//!>
//!> 下面是接口信息
bzero(&servaddr, sizeof( servaddr ) );
servaddr.sin_family = AF_INET;
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, NULL )) ==-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 )); //!> 此步重要,不要有时候出错
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;
}
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); //!> 读出来的写进去
}
}
}
}
return0;
}
//!> client端代码
//!>client.c
#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>
#define MAXLINE 1024
#define SERV_PORT 6000
//!> 注意输入是由stdin,接受是由server发送过来
//!> 所以在client端也是需要select进行处理的
void send_and_recv( int connfd )
{
FILE * fp =stdin;
int lens;
charsend[MAXLINE];
charrecv[MAXLINE];
fd_setrset;
FD_ZERO(&rset );
int maxfd =( fileno( fp ) > connfd ? fileno( fp ) :connfd + 1);
//!> 输入和输出的最大值
int n;
while( 1)
{
FD_SET(fileno( fp ), &rset );
FD_SET(connfd, &rset); //!> 注意不要把rset看作是简单的一个变量
//!> 注意它其实是可以包含一组套接字的哦,
//!> 相当于是封装的数组!每次都要是新的哦!
if( select(maxfd, &rset, NULL, NULL, NULL ) == -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");
}
}
//!> 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;
}
printf("Client send : %s\n", send);
write(connfd, send, strlen( send ) );
}
}
}
}
int main( int argc, char ** argv )
{
//!> char * SERV_IP = "10.30.97.188";
char buf[MAXLINE];
int connfd;
structsockaddr_in servaddr;
if( argc !=2 )
{
printf("Input server ip !\n");
exit(EXIT_FAILURE );
}
//!> 建立套接字
if( ( connfd= socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 )
{
printf("Socket Error...\n" , errno );
exit(EXIT_FAILURE );
}
//!> 套接字信息
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
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");
return0;
}
编译+运行:
gcc -o server server.c
gcc -o client client.c
./server
./client10.80.1.251 (注意参数你指定的server的IP哦 )
如果要多个一起运行,给个脚本:
#!/bin/sh
index=10
while [ $index -gt 0 ]
do
./client 10.80.1.251& # 注意在后台哦,所以只能看到在server下的现象,可以自己改进
$index = `expr $index - 1`
done
exit 0
//!> server端代码
//!>server.c
#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>
#define BUF_LEN 1024
#define SERV_PORT 6000
#define FD_SIZE 100
#define MAX_BACK 100
int main( int argc, char ** argv )
{
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;
structsockaddr_in servaddr, chiaddr;
if( (listenfd = socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 )
{
printf("Create socket Error : %d\n", errno );
exit(EXIT_FAILURE );
}
//!>
//!> 下面是接口信息
bzero(&servaddr, sizeof( servaddr ) );
servaddr.sin_family = AF_INET;
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, NULL )) ==-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 )); //!> 此步重要,不要有时候出错
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;
}
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); //!> 读出来的写进去
}
}
}
}
return0;
}
//!> client端代码
//!>client.c
#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>
#define MAXLINE 1024
#define SERV_PORT 6000
//!> 注意输入是由stdin,接受是由server发送过来
//!> 所以在client端也是需要select进行处理的
void send_and_recv( int connfd )
{
FILE * fp =stdin;
int lens;
charsend[MAXLINE];
charrecv[MAXLINE];
fd_setrset;
FD_ZERO(&rset );
int maxfd =( fileno( fp ) > connfd ? fileno( fp ) :connfd + 1);
//!> 输入和输出的最大值
int n;
while( 1)
{
FD_SET(fileno( fp ), &rset );
FD_SET(connfd, &rset); //!> 注意不要把rset看作是简单的一个变量
//!> 注意它其实是可以包含一组套接字的哦,
//!> 相当于是封装的数组!每次都要是新的哦!
if( select(maxfd, &rset, NULL, NULL, NULL ) == -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");
}
}
//!> 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;
}
printf("Client send : %s\n", send);
write(connfd, send, strlen( send ) );
}
}
}
}
int main( int argc, char ** argv )
{
//!> char * SERV_IP = "10.30.97.188";
char buf[MAXLINE];
int connfd;
structsockaddr_in servaddr;
if( argc !=2 )
{
printf("Input server ip !\n");
exit(EXIT_FAILURE );
}
//!> 建立套接字
if( ( connfd= socket( AF_INET, SOCK_STREAM, 0 ) ) == -1 )
{
printf("Socket Error...\n" , errno );
exit(EXIT_FAILURE );
}
//!> 套接字信息
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
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");
return0;
}
编译+运行:
gcc -o server server.c
gcc -o client client.c
./server
./client10.80.1.251 (注意参数你指定的server的IP哦 )
如果要多个一起运行,给个脚本:
#!/bin/sh
index=10
while [ $index -gt 0 ]
do
./client 10.80.1.251& # 注意在后台哦,所以只能看到在server下的现象,可以自己改进
$index = `expr $index - 1`
done
exit 0
相关文章推荐
- linux 网络编程---->多路复用:select实例!
- Linux网络通信编程(套接字模型TCP\UDP与IO多路复用模型select\poll\epoll)
- linux 网络编程---->简单的UDP实例
- linux网络编程常用函数详解与实例(socket-->bind-->listen-->accept)
- Linux网络通信编程(套接字模型TCP\UDP与IO多路复用模型select\poll\epoll)
- linux网络编程常用函数详解与实例(socket-->bind-->listen-->accept)
- linux网络编程常用函数详解与实例(socket-->bind-->listen-->accept)
- linux网络编程常用函数详解与实例(socket-->bind-->listen-->accept)
- 小试牛刀TCP 网络编程模式,单线程多路复用实例
- Linux网络编程---I/O多路复用之epoll
- linux 网络编程 I/O复用 select,poll ,epoll
- Linux应用编程基础之多路复用:select和poll的简单使用示例
- Linux下的socket编程实践(七) I/O多路复用技术之select模型
- LINUX 网络编程---多路复用和信号驱动I/O(王德仙)2012-04-07 客户端和服务器端编写完成,明天开始学习poll 和epoll
- linux网络编程常用函数详解与实例(socket-->bind-->listen-->accept)
- Linux 网络编程 select复用模式
- linux socket编程之多路复用select的例子
- linux网络编程常用函数详解与实例(socket-->bind-->listen-->accept)
- Linux【网络编程】——I/O多路转接之Select服务器
- linux网络编程常用函数详解与实例(socket-->bind-->listen-->accept)