基于TCP协议 I/O多路转接(select) 的高性能回显服务器客户端模型
服务端代码:
myselect.c
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h> #define ARRAY_SIZE 1024 /* 使用说明 */
void usage(const char* proc)
{
printf("%s usage : [server_ip] [ server_port]\n", proc);
} /* 初始化一个socket连接 */
int init_sock(const char* _ip, int _port)
{
int sock = socket(AF_INET, SOCK_STREAM, );
if(sock < )
{
perror("socket");
exit();
} int flg = ;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &flg, sizeof(flg));
struct sockaddr_in local;
local.sin_family = AF_INET;
local.sin_port = htons(_port);
local.sin_addr.s_addr = inet_addr(_ip); if(bind(sock, (struct sockaddr*)&local, sizeof(local)) < )
{
perror("bind");
exit();
} if(listen(sock , ) < )
{
perror("listen");
exit();
} return sock;
} /* 等待事件发生并处理 */
void run_select(int listen_sock)
{ int array_fds[ARRAY_SIZE]; /* 保存所有关心的描述符 */
array_fds[] = listen_sock; /* 将listen_sock 保存*/ /* 初始化为 array_fds */
int idx_init = ;
for(idx_init = ; idx_init < ARRAY_SIZE; ++idx_init)
array_fds[idx_init] = -; fd_set rfds; /* 关心的读事件描述符集 */
fd_set wfds; /* 关心的写事件描述符集 */
int max_fd = -; struct timeval _timeout = {, }; while()
{
max_fd = -;
_timeout.tv_sec = ;
FD_ZERO(&rfds);
FD_ZERO(&wfds); /* 一. 将数组中存储的描述符,添加到关心的集合中
* 并找出其中最大的描述符,
*/
int idx_add = ;
for(idx_add = ; idx_add < ARRAY_SIZE; idx_add++)
{
if(array_fds[idx_add] > )
{
FD_SET(array_fds[idx_add], &rfds);
FD_SET(array_fds[idx_add], &wfds);
if(array_fds[idx_add] > max_fd)
max_fd = array_fds[idx_add]; }
}
/* 二. 检测select的返回情况*/
int select_ret = select(max_fd+, &rfds, &wfds, NULL, &_timeout);
switch( select_ret )
{
case :
printf("timeout\n");
break;
case -:
perror("select\n");
break;
default:
{
/* 遍历数组,检测事件发生的描述符,并响应 */
int idx_check = ;
for(idx_check = ; idx_check < ARRAY_SIZE; ++idx_check)
{
if(array_fds[idx_check] < )
continue; if(idx_check == && FD_ISSET(array_fds[idx_check], &rfds) )
{ /* listensock 有数据来,表示有新的连接请求 */
struct sockaddr_in client;
socklen_t len = sizeof(client); int new_sock = accept(listen_sock, (struct sockaddr*)&client, &len);
if(new_sock > )
{
printf("新的连接请求来自: ip = %s, port = %d \n",inet_ntoa(client.sin_addr), ntohs(client.sin_port)); /* 在数组中找到未被占用的描述符位置,并保存新连接的描述符 */
int idx_find = ;
for(idx_find = ; idx_find < ARRAY_SIZE; ++idx_find)
{
if(array_fds[idx_find] < )
{
array_fds[idx_find] = new_sock;
break;
}
}
if(idx_find == ARRAY_SIZE)
close(new_sock);
}
} // << end if idx_check == 0 && FD_ISSET -- rfds>>
else if( idx_check != && FD_ISSET(array_fds[idx_check], &rfds) )
{ /* 其余描述符有数据来*/ char buf[ ];
memset(buf, '\0', sizeof(buf));
ssize_t s = read(array_fds[idx_check], buf, sizeof(buf) - );
if(s > )
{
printf("client say : %s\n", buf);
if(FD_ISSET(array_fds[idx_check], &wfds))
write(array_fds[idx_check], buf, sizeof(buf)-);
}
else if(s == )
{
printf("client quit\n");
close(array_fds[idx_check]);
array_fds[idx_check] = -;
}
else
{
perror("read fail");
close(array_fds[idx_check]);
array_fds[idx_check] = -;
}
} // << end else if idx_check != 0 && FD_ISSET -- rfds >>
} // << end for idx_check = 0; idx_check<ARRAY_SIZE; ++idx_check >>
} // << end default >>
break;
} // << end switch select >>
} // << end while 1 >>
} int main(int argc, char **argv)
{
if( != argc)
{
usage(argv[]);
exit();
} int sock = init_sock(argv[], atoi(argv[])); printf("start run_select\n");
run_select(sock); return ;
}
客户端代码:
为了练习dup 和 dup2 函数的使用,在客户端中,使用了这两个函数进行标准输出的重定向以及恢复,使用printf 函数向sockfd 中写数据,并提示用户输入。
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h> int main(int argc, char **argv)
{
if(argc != )
{
printf("Usage [server_ip] [ server_port]\n");
exit();
} int sock = socket(AF_INET, SOCK_STREAM, );
if(sock < )
{
perror("socket");
exit();
} struct sockaddr_in server;
server.sin_family = AF_INET;
server.sin_addr.s_addr = inet_addr(argv[]);
server.sin_port = htons(atoi(argv[])); if(connect(sock,(struct sockaddr*)&server, sizeof(server))<)
{
perror("connect");
exit();
} // 保存标准输出的描述符
int oldfd = dup(STDOUT_FILENO);
char buf[]; while()
{
printf("please input #");
fflush(stdout);
memset(buf, '\0', sizeof(buf)); /* 重定向输出到sock */
dup2(sock, STDOUT_FILENO);
ssize_t s = read(, buf, sizeof(buf)-);
if(s > )
{
/* 处理客户端只输入一个回车而程序挂起的bug */
if( buf[] == '\n' )
{
dup2(oldfd, STDOUT_FILENO);
continue;
} if(strncmp("quit", buf, ) == )
break; buf[s - ] = ; /* 用printf 向sock写 */
printf("%s", buf);
fflush(stdout); /* 恢复 标准输出 */
dup2(oldfd, STDOUT_FILENO); /* 从sock读服务端回显的数据, */
ssize_t _s = read(sock, buf, sizeof(buf) - );
if(_s >)
{
buf[_s] = ;
printf("server echo # %s\n", buf);
}
else if (s <= )
{
continue;
}
}
} close(sock);
close(oldfd); return ;
}
基于TCP协议 I/O多路转接(select) 的高性能回显服务器客户端模型的更多相关文章
- 【Linux网络编程】基于TCP流 I/O多路转接(poll) 的高性能http服务器
服务器比较简陋,为了学习poll的使用,只向客户端回写一条html语句.启动服务器后,浏览器发起请求,服务端向浏览器写回html,响应字符串,然后可以看到,浏览器解析并显示 Hello Poll!. ...
- 网络编程——基于TCP协议的Socket编程,基于UDP协议的Socket编程
Socket编程 目前较为流行的网络编程模型是客户机/服务器通信模式 客户进程向服务器进程发出要求某种服务的请求,服务器进程响应该请求.如图所示,通常,一个服务器进程会同时为多个客户端进程服务,图中服 ...
- 基于TCP协议的网络通信
TCP/IP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket,从而在通信的两端之间形成网络虚拟链路,一旦建立了虚拟的网络链路,两端的程序就可以通过虚拟链路进行通信.Java对基于TC ...
- 浅析C#基于TCP协议的SCOKET通信
TCP协议是一个基本的网络协议,基本上所有的网络服务都是基于TCP协议的,如HTTP,FTP等等,所以要了解网络编程就必须了解基于TCP协议的编程.然而TCP协议是一个庞杂的体系,要彻底的弄清楚它的实 ...
- 基于TCP协议的客户端
基于TCP协议的客户端 此客户端能用于下一篇博客的单线程服务器和多线程服务器 import java.io.BufferedReader; import java.io.IOException; im ...
- 用c++开发基于tcp协议的文件上传功能
用c++开发基于tcp协议的文件上传功能 2005我正在一家游戏公司做程序员,当时一直在看<Windows网络编程> 这本书,把里面提到的每种IO模型都试了一次,强烈推荐学习网络编程的同学 ...
- 网络编程应用:基于TCP协议【实现对象传输】--练习
要求: 基于TCP协议实现,客服端向服务器发送一个对象 服务器接受并显示用户信息 ,同时返回给客户端 "数据已收到" 建一个Student类,属性:name age Student ...
- 网络编程应用:基于TCP协议【实现文件上传】--练习
要求: 基于TCP协议实现一个向服务器端上传文件的功能 客户端代码: package Homework2; import java.io.File; import java.io.FileInputS ...
- 网络编程应用:基于TCP协议【实现一个聊天程序】
要求: 基于TCP协议实现一个聊天程序,客户端发送一条数据,服务器端发送一条数据 客户端代码: package Homework1; import java.io.IOException; impor ...
随机推荐
- PHP扩展模块Pecl、Pear以及Perl的区别
一.简短总结:pear:一个书写的比较规范,国外较流行的工具箱代码集pecl:php扩展包,但不属于php基本扩展范围perl:一种早于php出现的脚本级语言,php借鉴了他的正则表达式部分 二.Pe ...
- MySQL-LRU_List Free_List Flush_List
关于 LRU_List ,Free_List,Flush_List的介绍: LRU算法:(Latest Recent Used)最近最少使用 数据库的缓冲池通过LRU算法来进行管理. ...
- IIS部署PHP项目并与mysql完美结合
在上一篇文章中,提到iis与apache共用80端口,但是发现很多问题,例如 IIS网站有支付功能,而微信支付是不支持带有端口的网址的,虽然通过apche代理,在外面看来没有端口,可是内部还是避免不了 ...
- INSPIRED启示录 读书笔记 - 第32章 提防有特殊要求的产品
产品需求不能用户说了算 1.在看到具体的产品之前,用户很难知道自己需要什么 2.用户不知道什么样的产品是可行的(在目前的技术条件下可以实现) 3.用户之间缺少沟通,需求很难统一 怎样回避特例产品可能带 ...
- INSPIRED启示录 读书笔记 - 第7章 管理产品经理
产品总监的关键职责 1.组建优秀的产品经理团队 如果产品经理不称职,只能退而求其次,请其他团队成员(比如主程序员)越俎代庖 新产品经理必须经过约三个月刻苦学习才能开始管理产品,管理者应该为新人创造学习 ...
- Android系统--输入系统(三)必备Linux知识_双向通信(scoketpair)
Android系统--输入系统(三)必备Linux知识_双向通信(scoketpair) 引入 1. 进程和APP通信 创建进程 读取.分发 - 进程发送输入事件给APP 进程读取APP回应的事件 输 ...
- 【转载】关于C++中cin的几点说明性总结
转载地址:http://www.07net01.com/program/289153.html 学C++的时候,这几个输入函数弄的有点迷糊:这里做个小结: 1.cin 2.cin.get() 3.ci ...
- wareshark网络协议分析之DHCP
声明:本文关于DHCP协议介绍部分摘自百度百科 一.DHCP协议介绍: DHCP(Dynamic Host Configuration Protocol,动态主机配置协议)是一个局域网的网络协议,使用 ...
- R语言笔记002——sample()函数
sample()函数 sample(x,size,replace=FALSE) x表示一个或多个向量,size表示从x中随机取的样本个数,replace=FALSE表示不放回抽样,即不会选取到相同的值 ...
- linux命令:mv命令
mv命令是move的缩写,可以用来移动文件或者将文件改名(move (rename) files),是Linux系统下常用的命令,经常用来备份文件或者目录. 1.命令格式: mv [选项] 源文件或目 ...