linux 网络编程---->多路复用:select实例!
2015-12-07 13:35
731 查看
好吧,我承认找了好久,网上都没有像样的完整的实例,然后自己参照书自己写一个吧!
//!> 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;
struct
sockaddr_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("Bind
Error : %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(
"To many ... " );
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("no
data\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 ); //!>
读出来的写进去
}
}
}
}
return
0;
}
//!> 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;
char
send[MAXLINE];
char
recv[MAXLINE];
fd_set
rset;
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("Recv
ok...\n");
break;
}
else
if( n == -1 )
{
printf("Recv
error...\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;
struct
sockaddr_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");
return
0;
}
编译+运行:
gcc -o server server.c
gcc -o client client.c
./server
./client 10.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
FROM: http://blog.sina.com.cn/s/blog_6dc9e4cf0100ycuw.html
//!> 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;
struct
sockaddr_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("Bind
Error : %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(
"To many ... " );
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("no
data\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 ); //!>
读出来的写进去
}
}
}
}
return
0;
}
//!> 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;
char
send[MAXLINE];
char
recv[MAXLINE];
fd_set
rset;
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("Recv
ok...\n");
break;
}
else
if( n == -1 )
{
printf("Recv
error...\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;
struct
sockaddr_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");
return
0;
}
编译+运行:
gcc -o server server.c
gcc -o client client.c
./server
./client 10.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
FROM: http://blog.sina.com.cn/s/blog_6dc9e4cf0100ycuw.html
相关文章推荐
- Linux网络编程之socket文件传输示例
- linux 网络编程-基础篇
- Linux环境下网络编程杂谈
- Linux网络编程--多线程实现echo服务器与客户端“一对多”功能,网络编程的“Hello World!” - 壮壮熊
- Linux 网络编程 简单实例
- linux网络编程实现投票功能
- HttpURLConnection连接网页和获取数据的使用实例
- App Transport Security has blocked a cleartext HTTP (http://) resource load since it is insecure.
- 【2015ZUFE新生赛网络同步赛M】【DP 打表 二分】GW I (3) 暴力预处理+分类打表二分查找
- 【2015ZUFE新生赛网络同步赛L】【线段树找最小值最早出现位置 破环成链】God Wang 环上找小于等于tim的第一个数
- 【2015ZUFE新生赛网络同步赛K】【神奇技巧-数位拆分 哈希-桶哈希】贪吃的松鼠 只有一个数出现了k次
- AngularJS $http 异步后台无法获取请求参数
- 【2015ZUFE新生赛网络同步赛J】【水题 dfs】周末出游 是否所有点可达
- 【2015ZUFE新生赛网络同步赛I】【模拟】中国象棋 是否可以一步吃敌将
- 【2015ZUFE新生赛网络同步赛H】【水题 DP】会长晨刷记 步长1~4 不踩水坑从1到m
- OpenStack for icehouse的自动化安装脚本——创建GRE网络实例
- 【2015ZUFE新生赛网络同步赛G】【贪心】贪心的小猫咪 不含0的字符串删除一位使得数值尽可能大
- 【2015ZUFE新生赛网络同步赛F】【水题】石锅全拌 简单前缀和
- 【2015ZUFE新生赛网络同步赛E】【水题 c++函数 strstr】伟大先知的教导 子串查询
- 【2015ZUFE新生赛网络同步赛D】【暴力 时间戳】会长爱数学 数字十进制临位乘法循环节长度