unix下网络编程之I/O复用(五)

前言

本章节是用基本的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

原文地址:https://www.cnblogs.com/jiangzhaowei/p/8972188.html

时间: 2024-11-10 22:39:45

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

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

poll函数 在上文unix下网络编程之I/O复用(二)中已经介绍了select函数的相关使用,本文将介绍另一个常用的I/O复用函数poll.poll提供的功能与select类似,不过在处理流设备时,它能够提供额外的信息. poll函数原型: 1 2 3 #include<poll.h>    int poll (struct pollfd * fdarray , unsigned long nfds , int timeout);    //返回:就需描述字的个数,0--超时,-1--出错

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

什么是I/O复用? What we need is the capability to tell the kernel that we want to be notified if one or more I/O conditions are ready (i.e., input is ready to be read, or the descriptor is capable of taking more output). This capability is called I/O multi

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

select函数 该函数允许进程指示内核等待多个事件中的任何一个发生,并仅在有一个或是多个事件发生或经历一段指定的时间后才唤醒它.我们调用select告知内核对哪些描述字(就读.写或异常条件)感兴趣以及等待多长时间.我们感兴趣的描述字不局限于套接口,任何描述字都可以使用select来测试. select函数原型: #include<sys/select.h>#include<sys/time.h>int select (int maxfd , fd_set *readset ,fd

Unix网络编程之IO复用

上篇存在的问题 在上一篇TCP套接字中,还存在着一些问题. 当客户端连接上服务器后,阻塞于从标准输入读入信息的状态,若此时服务器进程被杀死,即使给客户TCP发来一个 FIN结束分节,但是由于客户处于阻塞状态,它将看不到这个EOF,直到读取之后,此时可能已经过去了很长时间. 因此进程需要一种能力,让内核同时检测多个IO口是否就绪,这个能力就称为IO复用.这是由select和poll两个函数 支持的. select函数 作用 允许进程指示内核等待多个事件中的任何一个发生,并只在有一个或多个事件发生或

网络编程之IO模型——selectors模块

网络编程之IO模型--selectors模块 一.了解select,poll,epoll IO复用:为了解释这个名词,首先来理解下复用这个概念,复用也就是共用的意思,这样理解还是有些抽象, 为此,咱们来理解下复用在通信领域的使用,在通信领域中为了充分利用网络连接的物理介质. 往往在同一条网络链路上采用时分复用或频分复用的技术使其在同一链路上传输多路信号,到这里我们就基本上理解了复用的含义, 即公用某个"介质"来尽可能多的做同一类(性质)的事,那IO复用的"介质"是什

扯谈网络编程之Tcp SYN flood洪水攻击

简介 TCP协议要经过三次握手才能建立连接: (from wiki) 于是出现了对于握手过程进行的攻击.攻击者发送大量的FIN包,服务器回应(SYN+ACK)包,但是攻击者不回应ACK包,这样的话,服务器不知道(SYN+ACK)是否发送成功,默认情况下会重试5次(tcp_syn_retries).这样的话,对于服务器的内存,带宽都有很大的消耗.攻击者如果处于公网,可以伪造IP的话,对于服务器就很难根据IP来判断攻击者,给防护带来很大的困难. 攻与防 攻击者角度 从攻击者的角度来看,有两个地方可以

【转】JAVA网络编程之Socket用法

JAVA网络编程之Socket用法 分类: JAVA2012-08-24 15:56 710人阅读 评论(0) 收藏 举报 在客户/服务器通信模式中,客户端需要主动建立与服务器连接的Socket,服务器端收到客户端的连接请求,也会创建与客户端连接的Socket.Socket可以看做是通信连接两端的收发器,客户端和服务店都通过Socket来收发数据. 1.构造Socket public Socket() 通过系统默认类型的 SocketImpl 创建未连接套接字 public Socket(Str

[深入浅出WIndows 10]网络编程之HttpClient类

14.2 网络编程之HttpClient类 除了可以使用HttpWebRequest类来实现HTTP网络请求之外,还可以使用HttpClient类来实现.对于基本的请求操作,HttpClient类提供了一个简单的接口来处理最常见的任务,并为身份验证提供了适用于大多数方案的合理的默认设置.对于较为复杂的 HTTP 操作,更多的功能包括:执行常见操作(DELETE.GET.PUT 和 POST)的方法:获取.设置和删除 Cookie 的功能:支持常见的身份验证设置和模式:异步方法上提供的 HTTP

网络编程之UDP编程

网络编程之UDP编程 UDP协议是一种不可靠的网络协议,它在通信的2端各建立一个Socket,但是这个Socket之间并没有虚拟链路,这2个Socket只是发送和接受数据的对象,Java提供了DatagramSocket对象作为基于UDP协议的Socket,使用DatagramPacket代表DatagramSocket发送和接受数据报.值得注意的是:UDP编程必须先由客户端发出信息.一个客户端就是一封信,Socket相当于美国式邮筒(信件的收发都在一个邮筒中).端口与协议相关,所以TCP的30