/*
Linux网络编程(五)——多路IO复用之select() 网络编程中,使用IO复用的典型场合:
1.当客户处理多个描述字时(交互式输入以及网络接口),必须使用IO复用。
2.一个客户同时处理多个套接口。
3.一个tcp服务程序既要处理监听套接口,又要处理连接套接口,一般需要用到IO复用。
4.如果一个服务器既要处理TCP,又要处理UDP,一般也需要用到IO复用。
5.如果一个服务器要处理多个服务或者多个协议,一般需要用到IO复用。
*/
/***********************************************************************
本程序功能:
使用单进程为多个客户端服务,接收到客户端发来的一条消息后,将该消息原样返回给客户端。
首先,建立一个监听套接字来接收来自客户端的连接。每当接收到一个连接后,将该连接套
接字加入客户端套接字数组,通过select实现多路复用。每当select返回时,检查套接字数
组的状态。并进行相应操作,如果是新的连接到来,则将新的连接套接字加入套接字数组,
如果客户端连接套接字变为可读,则对相应客户端进行响应。
***********************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <netdb.h>
#include <errno.h>
#define SERV_PORT 2046
#define LISTENQ 32
#define MAXLINE 1024
int
main(int argc, char **argv)
{
int i, maxi, maxfd, listenfd, connfd, sockfd;
int nready, client[FD_SETSIZE];
ssize_t n;
fd_set rset, allset;
char buf[MAXLINE];
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
if((listenfd = socket(AF_INET, SOCK_STREAM,))==-){
fprintf(stderr,"Socket error:%s\n\a",strerror(errno));
exit();
}
/* 服务器端填充 sockaddr结构*/
memset(&servaddr,,sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT); /* 捆绑listenfd描述符 */
if(bind(listenfd,(struct sockaddr*)(&servaddr),sizeof(struct sockaddr))==-){
fprintf(stderr,"Bind error:%s\n\a",strerror(errno));
exit();
}
/* 监听listenfd描述符*/
if(listen(listenfd,LISTENQ)==-){
fprintf(stderr,"Listen error:%s\n\a",strerror(errno));
exit();
}
maxfd = listenfd; /* 初始化最大文件描述符*/
maxi = -; /*client数组索引*/
for (i = ; i < FD_SETSIZE; i++)
client[i] = -; /* -1代表未使用*/
FD_ZERO(&allset);
FD_SET(listenfd, &allset); for ( ; ; ) {
rset = allset;
if((nready = select(maxfd+, &rset, NULL, NULL, NULL))<){
fprintf(stderr,"select Error\n");
exit();
}
if (FD_ISSET(listenfd, &rset)) { /*有新的客户端连接到来*/
clilen = sizeof(cliaddr);
if((connfd = accept(listenfd, (struct sockaddr *) &cliaddr, &clilen))<){
fprintf(stderr,"accept Error\n");
continue;
}
char des[sizeof(cliaddr)];
inet_ntop(AF_INET, &cliaddr.sin_addr, des, sizeof(cliaddr));
printf("new client: %s, port %d\n",des,ntohs(cliaddr.sin_port)); for (i = ; i < FD_SETSIZE; i++)
if (client[i] < ) {
client[i] = connfd; /*保存新的连接套接字*/
break;
}
if (i == FD_SETSIZE){
fprintf(stderr,"too many clients");
exit();
}
FD_SET(connfd, &allset); /*将新的描述符加入监听数组中*/
if (connfd > maxfd)
maxfd = connfd;
if (i > maxi)
maxi = i; /*当前client数组最大下标值*/
if (--nready <= )
continue; /*可读的套接字全部出来完了*/
} for (i = ; i <= maxi; i++) { /*检查所有已经连接的客户端是否由数据可读*/
if ( (sockfd = client[i]) < )
continue;
if (FD_ISSET(sockfd, &rset)) {
if ( (n = read(sockfd, buf, MAXLINE)) == ) {/*客户端主动断开了连接*/
close(sockfd);
FD_CLR(sockfd, &allset);
client[i] = -;
} else
write(sockfd, buf, n); if (--nready <= )
break; /*可读的套接字全部出来完了*/
}
}
}
}
/*
使用IO复用客户端
*/
/***********************************************************************
本程序(客户端)功能:
1.向服务器发起连接请求,并从标准输入stdin获取字符串,将字符串发往服务器。
2.从服务器中接收字符串,并将接收到的字符串输出到标准输出stdout.
=========================================================================
问题:由于既要从标准输入获取数据,又要从连接套接字中读取服务器发来的数据。
为避免当套接字上发生了某些事件时,程序阻塞于fgets()调用,使用select
实现多路IO复用,或等待标准输入,或等待套接口可读。这样一来,若服务器
进程终止,客户端能马上得到通知。
=========================================================================
对于客户端套接口,需要处理以下三种情况:
1.服务器端发送了数据过来,套接口变为可读,且read返回值大于0
2.服务器端发送了一个FIN(服务器进程终止),套接口变为可读,且read返回值等于0
3.服务器端发送了一个RST(服务器进程崩溃,且重新启动,此时服务器程序已经不认
识之前建立好了的连接,所以发送一个RST给客户端),套接口变为可读,且read返回-1
错误码存放在了errno
***********************************************************************/
//使用多路复用select的客户端程序
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <netdb.h>
#define SERV_PORT 2046
#define MAXLINE 1024
#define max(x,y) (x)>(y) ? (x):(y)
void str_cli(FILE *fp, int sockfd);
int
main(int argc, char **argv)
{
int sockfd;
struct sockaddr_in servaddr;
if (argc != ){
fprintf(stderr,"usage: tcpcli <IPaddress>\n\a");
exit();
}
if((sockfd=socket(AF_INET,SOCK_STREAM,))==-){
fprintf(stderr,"Socket error:%s\n\a",strerror(errno));
exit();
}
/*客户程序填充服务端的资料*/
memset(&servaddr,,sizeof(servaddr));
servaddr.sin_family=AF_INET;
servaddr.sin_port=htons(SERV_PORT);
if (inet_pton(AF_INET, argv[], &servaddr.sin_addr) <= ){
fprintf(stderr,"inet_pton Error:%s\a\n",strerror(errno));
exit();
}
/* 客户程序发起连接请求*/
if(connect(sockfd,(struct sockaddr *)(&servaddr),sizeof(struct sockaddr))==-){
fprintf(stderr,"connect Error:%s\a\n",strerror(errno));
exit();
}
str_cli(stdin, sockfd); /*重点工作都在此函数*/
exit();
} void
str_cli(FILE *fp, int sockfd)
{
int maxfdp1, stdineof;
fd_set rset;/*用于存放可读文件描述符集合*/
char buf[MAXLINE];
int n;
stdineof = ;/*用于标识是否结束了标准输入*/
FD_ZERO(&rset);
while(){
if (stdineof == )
FD_SET(fileno(fp), &rset);
FD_SET(sockfd, &rset);
maxfdp1 = max(fileno(fp), sockfd) + ;
if(select(maxfdp1, &rset, NULL, NULL, NULL)<){/*阻塞,直到有数据可读或出错*/
fprintf(stderr,"select Error\n");
exit();
}
if (FD_ISSET(sockfd, &rset)) { /*套接口有数据可读*/
if ( (n = read(sockfd, buf, MAXLINE)) == ) {
if (stdineof == )
return; /*标准输入正常结束*/
else
fprintf(stderr,"str_cli: server terminated prematurely");
}
write(fileno(stdout), buf, n);/*将收到的数据写到标准输出*/
}
if (FD_ISSET(fileno(fp), &rset)) { /*标准输入可读*/
if ( (n = read(fileno(fp), buf, MAXLINE)) == ) {
stdineof = ;
/*向服务器发送FIN,告诉它,后续已经没有数据发送了,但仍为读而开放套接口*/
if(-==shutdown(sockfd, SHUT_WR)){
fprintf(stderr,"shutdown Error\n");
}
FD_CLR(fileno(fp), &rset);
continue;
}
write(sockfd, buf, n);
}
}
}
/**********************************************************
注:关于close与shutdown的区别:
close()将描述字的访问计数减1,仅在访问计数为0时才关闭套接字。
而shutdown可以激发TCP的正常连接终止系列,而不管访问计数。
close()终止了数据传输的两个方向:读、写。
***********************************************************/

运行结果:

服务器端:

linux-code#./selectSrv &
[1] 5998
linux-code#new client: 127.0.0.1, port 53497
new client: 192.168.1.162, port 55807

客户端1:

linux-code# ./selectCli 127.0.0.1
hello,world!

hello,world!

客户端2:

linux-code# ./selectCli 192.168.1.162
hello,world!
hello,world!

利用netstat查看套接口连接情况如下:

linux-code# netstat -at

tcp 0 0 *:2046 *:* LISTEN
tcp 0 0 localhost.localdom:2046 localhost.localdo:53497 ESTABLISHED
tcp 0 0 ubuntu:2046 ubuntu:55807 ESTABLISHED
tcp 0 0 ubuntu:55807 ubuntu:2046 ESTABLISHED
tcp 0 0 localhost.localdo:53497 localhost.localdom:2046 ESTABLISHED

Linux网络编程(五)的更多相关文章

  1. Linux 网络编程五(UDP协议)

    UDP和TCP的对比 --UDP处理的细节比TCP少. --UDP不能保证消息被传送到目的地. --UDP不能保证数据包的传递顺序. --TCP处理UDP不处理的细节. --TCP是面向连接的协议 - ...

  2. Linux网络编程五、套接字超时

    1.accept超时 accept等待并接受连接请求的过程是阻塞的,可以通过IO转接来设置等待一定的时长,如果超时没有连接,就让这个函数返回,让当前进程/线程处理别的任务. 例: // 最大的文件描述 ...

  3. Linux网络编程(六)

    网络编程中,使用多路IO复用的典型场合: 1.当客户处理多个描述字时(交互式输入以及网络接口),必须使用IO复用. 2.一个客户同时处理多个套接口. 3.一个tcp服务程序既要处理监听套接口,又要处理 ...

  4. linux高性能服务器编程 (五) --Linux网络编程基础api

    第五章 Linux网络编程基础api 1.主机字节序和网络字节序 字节序是指整数在内存中保存的顺序.字节序分为大端字节序.小端字节序. 大端字节序:一个整数的高位字节数据存放在内存的低地址处.低位字节 ...

  5. Linux网络编程&内核学习

    c语言: 基础篇 1.<写给大家看的C语言书(第2版)> 原书名: Absolute Beginner's Guide to C (2nd Edition) 原出版社: Sams 作者: ...

  6. linux网络编程_1

    本文属于转载,稍有改动,以利于学习. (一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端         网络程序和普通的程序有一个最大的区别是网络程序是由两个 ...

  7. Linux网络编程入门 (转载)

    (一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端         网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户 ...

  8. Linux网络编程必看书籍推荐

    首先要说讲述计算机网络和TCP/IP的书很多. 先要学习网络知识才谈得上编程 讲述计算机网络的最经典的当属Andrew S.Tanenbaum的<计算机网络>第五版,这本书难易适中. &l ...

  9. [转] - Linux网络编程 -- 网络知识介绍

    (一)Linux网络编程--网络知识介绍 Linux网络编程--网络知识介绍客户端和服务端         网络程序和普通的程序有一个最大的区别是网络程序是由两个部分组成的--客户端和服务器端. 客户 ...

随机推荐

  1. 使用Heartbeat实现双机热备

    使用Heartbeat实现"双机热备"或者称为"双机互备"heartbeat的工作原理:heartbeat最核心的包含两个部分,心跳监測部分和资源接管部分,心跳 ...

  2. 浅谈JavaScript中继承的实现

    谈到js中的面向对象编程,都有一个共同点,选择原型属性还是构造函数,两者各有利弊,而就片面的从js的对象创建以及继承的实现两个方面来说,官方所推荐的是两个相结合,各尽其责,各取其长,在前面的例子中,我 ...

  3. 使用ArcGIS API for Silverlight 进行复合多条件空间查询

    原文:使用ArcGIS API for Silverlight 进行复合多条件空间查询 这两天帮网上认识的一个兄弟做了一个查询的示例,多多少少总结一下,在此和大家分享. 为什么说是复合多条件呢?因为进 ...

  4. 建立TextView位置的部分可以点击,不同的颜色

    String url="注册代表宝藏驱动器,你已经允许成员<服务条款>,请仔细阅读. "; SpannableStringBuilder style = new Spa ...

  5. 【云图】如何制作全国KTV查询系统?

    原文:[云图]如何制作全国KTV查询系统? 摘要:本文以[唱吧]531麦霸音乐节为案例,详细解读了如何导入自有数据到高德云图,并进行检索和展示.最后,调起高德mobile地图来进行路线规划和周边查询. ...

  6. 第一个JavaWeb程序

    转载 第一个JavaWeb程序 JavaWeb学习总结第二篇—第一个JavaWeb程序 最近我在学院工作室学习并加入到研究生的项目中,在学长学姐的带领下,进入项目实践中,为该项目实现一个框架(用已有框 ...

  7. 饼干怪兽和APT攻击

    APT攻击就像一个孩子,你通过各种方式窃取他们的大脑要拿出饼干,为了防止恶意攻击,过失作为母亲未能发现和防止饼干盗窃贼如.于她仅仅监视厨房椅子.衣柜门或烤箱门的开启.建立起有效防御目标攻击与APT攻击 ...

  8. JavaScript Date对象介绍

    原文:JavaScript Date对象介绍 Date 日期和时间对象 1. 介绍 Date对象,是操作日期和时间的对象.Date对象对日期和时间的操作只能通过方法. 2. 构造函数 2.1 new ...

  9. 专访雷水果国:离1.5K至18K 一个程序猿5每年的成长之路

    我只是一个小菜鸟,对于自主学习和交流PHP(jquery,linux,lamp,shell,javascript,server)等一系列的知识.小菜鸟创建了一个群.希望光临本博客的人能够进来交流. 寻 ...

  10. BEGINNING SHAREPOINT&#174; 2013 DEVELOPMENT 文件夹

    BEGINNING SHAREPOINT® 2013 DEVELOPMENT 文件夹 第一部分--開始使用SharePoint 2013 第1章节--SharePoint 2013 介绍 逐渐了解Sh ...