前言

本章节是用基本的Linux/Unix基本函数加上select调用编写一个完整的服务器和客户端例子,可在Linux(ubuntu)和Unix(freebsd)上运行,客户端和服务端的功能如下:

客户端从标准输入读入一行,发送到服务端

服务端从网络读取一行,然后输出到客户端

客户端收到服务端的响应,输出这一行到标准输出

服务端

代码如下:

#include  <unistd.h>
#include <sys/types.h> /* basic system data types */
#include <sys/socket.h> /* basic socket definitions */
#include <netinet/in.h> /* sockaddr_in{} and other Internet defns */
#include <arpa/inet.h> /* inet(3) functions */
#include <sys/select.h> /* select function*/ #include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <string.h> #define MAXLINE 10240 void handle(int * clientSockFds, int maxFds, fd_set* pRset, fd_set* pAllset); int main(int argc, char **argv)
{
int servPort = 6888;
int listenq = 1024; int listenfd, connfd;
struct sockaddr_in cliaddr, servaddr;
socklen_t socklen = sizeof(struct sockaddr_in);
int nready, nread;
char buf[MAXLINE];
int clientSockFds[FD_SETSIZE];
fd_set allset, rset;
int maxfd; listenfd = socket(AF_INET, SOCK_STREAM, 0);
if (listenfd < 0) {
perror("socket error");
return -1;
} int opt = 1;
if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
perror("setsockopt error");
} bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(servPort); if(bind(listenfd, (struct sockaddr*)&servaddr, socklen) == -1) {
perror("bind error");
exit(-1);
} if (listen(listenfd, listenq) < 0) {
perror("listen error");
return -1;
} int i = 0;
for (i = 0; i< FD_SETSIZE; i++)
clientSockFds[i] = -1;
FD_ZERO(&allset);
FD_SET(listenfd, &allset);
maxfd = listenfd; printf("echo server use select startup, listen on port %d\n", servPort);
printf("max connection: %d\n", FD_SETSIZE); for ( ; ; ) {
rset = allset;
nready = select(maxfd + 1, &rset, NULL, NULL, NULL);
if (nready < 0) {
perror("select error");
continue;
}
if (FD_ISSET(listenfd, &rset)) {
connfd = accept(listenfd, (struct sockaddr*) &cliaddr, &socklen);
if (connfd < 0) {
perror("accept error");
continue;
} sprintf(buf, "accept form %s:%d\n", inet_ntoa(cliaddr.sin_addr), cliaddr.sin_port);
printf(buf, ""); for (i = 0; i< FD_SETSIZE; i++) {
if (clientSockFds[i] == -1) {
clientSockFds[i] = connfd;
break;
}
}
if (i == FD_SETSIZE) {
fprintf(stderr, "too many connection, more than %d\n", FD_SETSIZE);
close(connfd);
continue;
}
if (connfd > maxfd)
maxfd = connfd; FD_SET(connfd, &allset);
if (--nready <= 0)
continue;
} handle(clientSockFds, maxfd, &rset, &allset);
}
} void handle(int * clientSockFds, int maxFds, fd_set* pRset, fd_set* pAllset) {
int nread;
int i;
char buf[MAXLINE];
for (i = 0; i< maxFds; i++) {
if (clientSockFds[i] != -1) {
if (FD_ISSET(clientSockFds[i], pRset)) {
nread = read(clientSockFds[i], buf, MAXLINE);//读取客户端socket流
if (nread < 0) {
perror("read error");
close(clientSockFds[i]);
FD_CLR(clientSockFds[i], pAllset);
clientSockFds[i] = -1;
continue;
}
if (nread == 0) {
printf("client close the connection\n");
close(clientSockFds[i]);
FD_CLR(clientSockFds[i], pAllset);
clientSockFds[i] = -1;
continue;
} write(clientSockFds[i], buf, nread);//响应客户端 有可能失败,暂不处理
}
}
} }

客户端

代码如下:

#include  <unistd.h>
#include <sys/types.h> /* basic system data types */
#include <sys/socket.h> /* basic socket definitions */
#include <netinet/in.h> /* sockaddr_in{} and other Internet defns */
#include <arpa/inet.h> /* inet(3) functions */
#include <sys/select.h> /* select function*/ #include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <string.h> #define MAXLINE 10240
#define max(a,b) ((a) > (b) ? (a) : (b))
//typedef struct sockaddr SA; void handle(int sockfd); int main(int argc, char **argv)
{
char * servInetAddr = "127.0.0.1";
int servPort = 6888;
char buf[MAXLINE];
int connfd;
struct sockaddr_in servaddr; if (argc == 2) {
servInetAddr = argv[1];
}
if (argc == 3) {
servInetAddr = argv[1];
servPort = atoi(argv[2]);
}
if (argc > 3) {
printf("usage: selectechoclient <IPaddress> <Port>\n");
return -1;
} connfd = socket(AF_INET, SOCK_STREAM, 0); bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(servPort);
inet_pton(AF_INET, servInetAddr, &servaddr.sin_addr); if (connect(connfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) {
perror("connect error");
return -1;
}
printf("welcome to selectechoclient\n");
handle(connfd); /* do it all */
close(connfd);
printf("exit\n");
exit(0);
} void handle(int connfd)
{
FILE* fp = stdin;
char sendline[MAXLINE], recvline[MAXLINE];
fd_set rset;
FD_ZERO(&rset);
int maxfds = max(fileno(fp), connfd) + 1;
int nread;
for (;;) {
FD_SET(fileno(fp), &rset);
FD_SET(connfd, &rset); if (select(maxfds, &rset, NULL, NULL, NULL) == -1) {
perror("select error");
continue;
} if (FD_ISSET(connfd, &rset)) {
//接收到服务器响应
nread = read(connfd, recvline, MAXLINE);
if (nread == 0) {
printf("server close the connection\n");
break;
}
else if (nread == -1) {
perror("read error");
break;
}
else {
//server response
write(STDOUT_FILENO, recvline, nread);
}
} if (FD_ISSET(fileno(fp), &rset)) {
//标准输入可读
if (fgets(sendline, MAXLINE, fp) == NULL) {
//eof exit
break;
}
else {
write(connfd, sendline, strlen(sendline));
}
} }
}

下载和编译

下载地址

编译和启动服务端

gcc selectechoserver.c -o selectechoserver

编译和启动客户端

gcc selectechoclient.c -o selectechoclient

unix下网络编程之I/O复用(五)的更多相关文章

  1. unix下网络编程之I/O复用(三)

    poll函数 在上文unix下网络编程之I/O复用(二)中已经介绍了select函数的相关使用,本文将介绍另一个常用的I/O复用函数poll.poll提供的功能与select类似,不过在处理流设备时, ...

  2. unix下网络编程之I/O复用(一)

    什么是I/O复用? What we need is the capability to tell the kernel that we want to be notified if one or mo ...

  3. unix下网络编程之I/O复用(二)

    select函数 该函数允许进程指示内核等待多个事件中的任何一个发生,并仅在有一个或是多个事件发生或经历一段指定的时间后才唤醒它.我们调用select告知内核对哪些描述字(就读.写或异常条件)感兴趣以 ...

  4. unix下网络编程之I/O复用(四)

    首先需要了解的是select函数: select函数 #include<sys/select.h> #include<sys/time.h> int select (int m ...

  5. TCP/IP网络编程之I/O复用

    基于I/O复用的服务端 在前面章节的学习中,我们看到了当有新的客户端请求时,服务端进程会创建一个子进程,用于处理和客户端的连接和处理客户端的请求.这是一种并发处理客户端请求的方案,但并不是一个很好的方 ...

  6. linux网络编程之socket编程(十五)

    今天继续学习socket编程,这次主要是学习UNIX域协议相关的知识,下面开始: [有个大概的认识,它是来干嘛的] ①.UNIX域套接字与TCP套接字相比较,在同一台主机的传输速度前者是后者的两倍. ...

  7. python3网络编程之socketserver

    本节主要是讲解python3网络编程之socketserver,在上一节中我们讲到了socket.由于socket无法支持多用户和多并发,于是就有了socket server. socket serv ...

  8. GO语言的进阶之路-网络编程之socket

    GO语言的进阶之路-网络编程之socket 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.什么是socket; 在说socket之前,我们要对两个概念要有所了解,就是IP和端口 ...

  9. 网络编程之socket

    网络编程之socket socket:在网络编程中的一个基本组件,也称套接字. 一个套接字就是socket模块中的socket类的一个实例. 套接字包括两个: 服务器套接字和客户机套接字 套接字的实例 ...

随机推荐

  1. [NOI2008]设计路线

    题目 洛谷 BZOJ 做法 神仙题 显然这是棵树 个节点相东仅连接一个结点 不同于剖分,还能存在\("V"\)字型,一个节点最多与另外节点连两条边 \(dp[i][j][k]\)表 ...

  2. Mysql中in语句排序

    这只是Mysql语句的写法,不同数据库写法不太一样, ,,,) order by instr('1,11115,11140,11135',id); 如果不使用order by,in语句查询出来的顺序是 ...

  3. 【P2361】yyy棋(博弈论+贪心+模拟)

    这个题看上去本来不好处理,然而善意的题面已经基本告诉你做法了,小时候玩的那个游戏就是代码的核心.动动脑子想想,如果长和宽的积是奇数,那么一定要先手,如果是偶数,那么后手就会获胜. 好了,那么怎么处理对 ...

  4. IOS 被拒 关于 iPhone running iOS 10.3.1 on Wi-Fi connected to an IPv6 network.

    问题: Guideline 2.1 - Performance Thank you for your resubmission. However, we discovered one or more ...

  5. 通过yum安装mysql

    在linux中安装数据库首选MySQL,Mysql数据库的第一个版本就是发行在Linux系统上,其他选择还可以有postgreSQL,oracle等 在Linux上安装mysql数据库,我们可以去其官 ...

  6. UOJ180 【UR #12】实验室外的攻防战

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...

  7. servlet中service() doGet() doPost() 方法

    HttpServlet 里的三个方法:service(HttpServletRequest req, HttpServletResponse resp) ,doGet(HttpServletReque ...

  8. 使用 Spring Boot 快速构建 Spring 框架应用

    Spring 框架对于很多 Java 开发人员来说都不陌生.自从 2002 年发布以来,Spring 框架已经成为企业应用开发领域非常流行的基础框架.有大量的企业应用基于 Spring 框架来开发.S ...

  9. graphlab 安装好后的导入配置

    本以为下好了的结果... 然后等个十几二十分钟... 关掉这个页面重新打开,再重新导入graphlab 貌似好了...但是,,, 发现是自己的文件放错盘了...在F盘... 然后就好啦~

  10. Python中MySQL插入数据

    sql = 'INSERT INTO course(class_name, credit, properties, teacher_name, college_given, classroom) ' ...