1. 其他函数准备

1. TCP 回射服务器程序: str_echo 函数

#include “unp.h”void str_echo(int sockfd)
{ssize_t n;char    buf[MAXLINE];again:/*write()
函数定义:ssize_t write (int fd, const void * buf, size_t count);
函数说明:write()会把参数buf所指的内存写入count个字节到参数fd所指的文件内。
返回值:如果顺利write()会返回实际写入的字节数(len)。当有错误发生时则返回-1,错误代码存入errno中。
*//*
read()函数定义:ssize_t read(int fd, void * buf, size_t count);函数说明:read()会把参数fd所指的文件传送count 个字节到buf 指针所指的内存中。返回值:返回值为实际读取到的字节数, 如果返回0, 表示已到达文件尾或是无可读取的数据。若参数count 为0, 则read()不会有作用并返回0。另外,以下情况返回值小于count:
*/while((n = read(sockfd, buf, MAXLINE)) > 0)Writen(sockfd, buf, n);if(n < 0 && errno == EINTR) // 被中断后继续执行goto again;else if(n < 0)err_sys("str_echo: read error");
}

2. TCP 回射客户端程序: str_cli 函数

#include "unp.h"void str_cli(FILE *fp, int sockfd)
{char    sendline[MAXLINE], recvline[MAXLINE];while(Fgets(sendline, NAXLINE, fp) != NULL){Writen(sockfd, sendline, strlen(sendline));if(Readline(sockfd, recvline, MALINE) == 0)err_quit("str_cli: server terminated prematurely");Fputs(recvline, stdout);}}

显示客户IP地址和端口号的时间获取服务器程序

#include "unp.h"
#include    <time.h>#pragma clang diagnostic push
#pragma ide diagnostic ignored "EndlessLoop" //这是防止IDE输出警告的,不用管。
int
main(int argc, char **argv)
{int listenfd, connfd;// //len是一个 值——结果 变量socklen_t len;struct sockaddr_in servaddr,cliaddr;//cliaddr存放客户的协议地址//缓冲区存放转换结果char buff[MAXLINE];//存放时间time_t ticks;//初始化套接字,指定协议类型(第一个参数),套接字类型(字节流,数据报,或者原始套接字(极少使用))listenfd  = Socket(AF_INET,SOCK_STREAM,0);bzero(&servaddr,sizeof (servaddr));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port = htons(13);//本地协议地址赋予套接字//参数一:套接字描述符,用于标识套接字//参数二:指定特定于协议的地址结构的指针//参数三:地址结构的长度/*详细解释下为什么要传如地址结构的长度:由于该结构体的传递方向为从进程到内核,所以内核需要知道有多大的内容复制进来,所以需要一个参数告知内核传入地址的大小,这种变量叫做“值——结果”变量*/Bind(listenfd,(SA*) &servaddr,sizeof (servaddr));//使用socket创建的套接字最初为主动套接字,listen将其转换为一个被动套接字,指示内核应该接受指向该套接字的连接请求Listen(listenfd,LISTENQ);while (1){len =sizeof (cliaddr);//accept()函数功能是,从处于 established 状态的连接队列头部取出一个已经完成的连接,如果这个队列没有已经完成的连接,accept()函数就会阻塞,直到取出队列中已完成的用户连接为止。//如果accept成功,那么它的返回值是由内核生成的全新的描述符,代表与客户之间的 已成功建立 的TCP链接(已连接套接字描述符)//参数一:监听套接字描述符//参数二:客户进程的协议地址//参数三:该地址的大小//监听套接字与已连接套接字的区别:监听套接字在该服务的生命周期内一直存在,而每个已完成三次握手的TCP链接都会分配一个已连接套接字connfd = Accept(listenfd,(SA*) &cliaddr,&len);printf("connection from %s , port %d\n",inet_ntop(AF_INET,&cliaddr.sin_addr,buff,sizeof (buff)),//打印IP地址ntohs(cliaddr.sin_port));//打印端口号//获取时间ticks = time(NULL);//为什么不使用printf:因为printf的缓冲区不可控制,对我们来说难以控制snprintf(buff,sizeof (buff),"%.24s\r\n",ctime(&ticks));Write(connfd,buff,strlen(buff));Close(connfd);}}#pragma clang diagnostic pop

4.2作业

#include "unp.h"int
main(int argc, char **argv)
{int                    sockfd, n;char              recvline[MAXLINE + 1];struct sockaddr_in   servaddr;if (argc != 2)err_quit("usage: a.out <IPaddress>");if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)err_sys("socket error");bzero(&servaddr, sizeof(servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port   = htons(13);  /* daytime server */if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)err_quit("inet_pton error for %s", argv[1]);if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0)err_sys("connect error");struct sockaddr_in ss;char buff[MAXLINE];socklen_t  len;len =sizeof (ss);if (getsockname(sockfd,(SA*) &ss,&len) < 0) printf("错误喽!");else{printf("connection from %s,port %d\n",inet_ntop(AF_INET,&ss.sin_addr,buff,sizeof (buff)),ntohs(ss.sin_port));}while ( (n = read(sockfd, recvline, MAXLINE)) > 0) {recvline[n] = 0;    /* null terminate */if (fputs(recvline, stdout) == EOF)err_sys("fputs error");}if (n < 0)err_sys("read error");exit(0);
}

5.2

#include "unp.h"#pragma clang diagnostic push
#pragma ide diagnostic ignored "EndlessLoop"
int
main(int argc, char **argv)
{int listenfd,confd;pid_t childpid;socklen_t clilen;struct sockaddr_in cliaddr,servaddr;listenfd =Socket(AF_INET,SOCK_STREAM,0);bzero(&servaddr,sizeof (servaddr));servaddr.sin_family = AF_INET;servaddr.sin_addr.s_addr =htonl(INADDR_ANY);servaddr.sin_port =htons(SERV_PORT);Bind(listenfd,(SA*) &servaddr,sizeof (servaddr));Listen(listenfd,LISTENQ);while (1){clilen =sizeof (cliaddr);confd = Accept(listenfd,(SA*) &cliaddr,&clilen);if((childpid = Fork()) == 0)//fork为每个客户派生一个处理它们的子进程//紫禁城关闭监听套接字,父进程关闭已连接套接字,紫禁城接着调用str_echo处理客户{Close(listenfd);str_echo(listenfd); exit(0);}Close(confd);}}
#pragma clang diagnostic pop

TCP回射客户程序

#include "unp.h"#pragma clang diagnostic push
#pragma ide diagnostic ignored "EndlessLoop"
int main(int argc, char **argv)
{int sockfd;struct sockaddr_in servaddr;if (argc !=2 )err_quit("请输入IP地址。");sockfd = Socket(AF_INET,SOCK_STREAM,0);bzero(&servaddr,sizeof (servaddr));servaddr.sin_family = AF_INET;servaddr.sin_port = htons(SERV_PORT);Inet_pton(AF_INET,argv[1],&servaddr.sin_addr);//将IP地址转换为二进制机器可读Connect(sockfd,(SA*) &servaddr,sizeof (servaddr));str_cli(stdin,sockfd);//回射函数exit(0);
}
#pragma clang diagnostic pop

僵死进程:

   (1)概念:   进程实体已经释放,但进程对应的PCB进程控制块(进程描述符)还在.(2)产生的条件:  子进程比父进程结束的早,且父进程没有调用 wait() 获取子进程的退出码,这时子进程就变为僵死进程.(3)若子进程比父进程结束的晚,则在父进程结束后,子进程的父进程会变成pid为1 的进程

最终版本:

#include "unp.h"int
main(int argc, char **argv)
{int                    listenfd, connfd;pid_t              childpid;socklen_t          clilen;struct sockaddr_in   cliaddr, servaddr;void              sig_chld(int);listenfd = Socket(AF_INET, SOCK_STREAM, 0);bzero(&servaddr, sizeof(servaddr));servaddr.sin_family      = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port        = htons(SERV_PORT);Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));Listen(listenfd, LISTENQ);/*按我现阶段的理解:(1)Signal捕获信号SIGCHLD,并交给sig_chld处理(2)Signal第二个参数实际上是一个函数指针,将sig_chld传入*//** sig_chld()函数解析:* 使用waitpid函数,并指定WNOHANG参数(有尚未终止的子进程是不要阻塞)* 为什么不适用wait函数,防止产生僵死进程,因为没有办法防止wait在正在运行的子进程尚有未终止阻塞(具体参见《unix网络编程》110页)* 阻塞:此处的阻塞不同于此前使用的同名词语,这里的阻塞是指阻塞信号或某个信号集,防止他们在阻塞期间提交。* 本节的目的是示范网络编程中可能遇到的情况:* (1)当fork某个进程时,必须捕获SIGCHLD信号* (2)当捕获信号时,必须处理被中断的系统调用* (3)SIGCHLD的信号处理函数必须正确编写,应使用waitpid函数以免留下僵死进程*/Signal(SIGCHLD, sig_chld);   /* must call waitpid() */for ( ; ; ){clilen = sizeof(cliaddr);if ( (connfd = accept(listenfd, (SA *) &cliaddr, &clilen)) < 0) {if (errno == EINTR)continue;      /* back to for() */elseerr_sys("accept error");}if ( (childpid = Fork()) == 0) {   /* child process */Close(listenfd); /* close listening socket */str_echo(connfd);   /* process the request */exit(0);}Close(connfd);            /* parent closes connected socket */}
}

select()函数

#include "unp.h"#pragma clang diagnostic push
#pragma ide diagnostic ignored "EndlessLoop"void str_cli(FILE *fp, int sockfd) {//需要检查的文件描述符个数int maxfdp1;/* fd_set其实这是一个数组的宏定义,实际上是一long类型的数组,* 每一个数组元素都能与一打开的文件句柄(socket、文件、管道、设备等)建立联系,建立联系的工作由程序员完成,* 当调用select()时,由内核根据IO状态修改fd_set的内容,* 由此来通知执行了select()的进程哪个句柄可读。*/fd_set rset;char sendline[MAXLINE], recvline[MAXLINE];FD_ZERO(&rset);while (1) {//将fd加入set集合/** FD_CLR(int fd, fd_set *fdset);       //将fd从set集合中清除*FD_ISSET(int fd, fd_set *fdset);     //检测fd是否在set集合中,不在则返回0*FD_ZERO(fd_set *fdset);              //将set清零使集合中不含任何fd*///fileno()用来取得参数stream 指定的文件流所使用的文件描述词FD_SET(fileno(fp), &rset);//将套接字描述符加入集合FD_SET(sockfd, &rset);//为什么要加1呢:我们需要描述符的个数,而不是最大值,而描述符是从0开始的(妙吧!)maxfdp1 = max(fileno(fp), sockfd )+ 1;/*int select(int nfds,  fd_set* readset,  fd_set* writeset,  fe_set* exceptset,  struct timeval* timeout);* 参数:nfds           需要检查的文件描述字个数readset     用来检查可读性的一组文件描述字。writeset     用来检查可写性的一组文件描述字。exceptset  用来检查是否有异常条件出现的文件描述字。(注:错误不包括在异常条件之内)timeout      超时,填NULL为阻塞,填0为非阻塞,其他为一段超时时间返回值:返回fd的总数,错误时返回SOCKET_ERROR*/Select(maxfdp1, &rset, NULL, NULL, NULL);//FD_ISSET(int fd, fd_set *fdset);     //检测fd是否在set集合中,不在则返回0if (FD_ISSET(sockfd, &rset)) {if (Readline(sockfd, recvline, MAXLINE) == 0)err_quit("服务器超时");Fputs(recvline, stdout);}if (FD_ISSET(fileno(fp), &rset)) {if (Fgets(sendline, MAXLINE, fp) == NULL)return;Write(sockfd, sendline, strlen(sendline));}}}#pragma clang diagnostic pop
/* include fig01 */
#include    "unp.h"#pragma clang diagnostic push
#pragma ide diagnostic ignored "EndlessLoop"
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;listenfd = Socket(AF_INET, SOCK_STREAM, 0);bzero(&servaddr, sizeof(servaddr));servaddr.sin_family      = AF_INET;servaddr.sin_addr.s_addr = htonl(INADDR_ANY);servaddr.sin_port        = htons(SERV_PORT);Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));Listen(listenfd, LISTENQ);maxfd = listenfd;         /* 初始化 */maxi = -1;                    /* index into client[] array *///将所有描述符初始化为-1for (i = 0; i < FD_SETSIZE; i++)client[i] = -1;         /* -1 indicates available entry */FD_ZERO(&allset);FD_SET(listenfd, &allset);
/* end fig01 *//* include fig02 */for ( ; ; ) {rset = allset;      /* structure assignment *///等待某个事件的发生nready = Select(maxfd+1, &rset, NULL, NULL, NULL);if (FD_ISSET(listenfd, &rset)) {   /* new client connection */clilen = sizeof(cliaddr);connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);
#ifdef  NOTDEFprintf("new client: %s, port %d\n",Inet_ntop(AF_INET, &cliaddr.sin_addr, 4, NULL),ntohs(cliaddr.sin_port));
#endiffor (i = 0; i < FD_SETSIZE; i++)if (client[i] < 0) {client[i] = connfd; /* 保存描述符 */break;/*保存了就跳出*/}if (i == FD_SETSIZE)err_quit("too many clients");FD_SET(connfd, &allset);   /*添加新的描述符来设置*/if (connfd > maxfd)maxfd = connfd;            /* for select */if (i > maxi)maxi = i;              /* max index in client[] array */if (--nready <= 0)continue;                /* no more readable descriptors */}for (i = 0; i <= maxi; i++) { /* check all clients for data */if ( (sockfd = client[i]) < 0)continue;if (FD_ISSET(sockfd, &rset)) {if ( (n = Read(sockfd, buf, MAXLINE)) == 0) {/*4connection closed by client */Close(sockfd);FD_CLR(sockfd, &allset);client[i] = -1;} elseWriten(sockfd, buf, n);if (--nready <= 0)break;               /* no more readable descriptors */}}}}
/* end fig02 */
#pragma clang diagnostic pop

pselect()函数

函数select()是用一种超时轮循的方式来查看文件的读写错误可操作性。在Linux下,还有一个相似的函数pselect()。

poll()函数

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

功能:
监视并等待多个文件描述符的属性变化
参数:

fds:指向一个结构体数组的第0个元素的指针,每个数组元素都是一个struct pollfd结构,用于指定测试某个给定的fd的条件

struct pollfd{int fd;            //文件描述符short events;    //等待的事件short revents;   //实际发生的事件
};

UNIX网络编程学习笔记(代码超详细解析)(持续更新)相关推荐

  1. Unix网络编程学习笔记之第11章 名字与地址转换

    一. 域名系统(DNS) 1. 简介 DNS主要用于主机名和IP地址之间的映射. 主机名可以是简单的名字ljm,也可以是全限定域名ljm.localdomainbaidu.com等. 2.资源记录 D ...

  2. 2021版!万字UNIX网络编程学习笔记(套接字篇)

    目录 1.一个简单的时间获取服务器的程序 2.套接字篇 2.1 套接字简介 2.2 套接字中常用的函数 2.3 基本TCP套接字编程 2.3.1.socket函数 2.3.2 connect函数 2. ...

  3. Spring框架学习笔记,超详细!!(4)

    Java小白开始学习Spring框架,一方面,跟着视频学习,并记录下学习笔记,方便以后复习回顾.另一方面,发布学习笔记来约束自己,学习路程还很遥远,继续加油坚持!!!希望能帮助到大家! 另外还有我的牛 ...

  4. 15Java网络编程学习笔记

    Java网络编程学习笔记 文章目录 1 网络基础 1.1 网络通信 1.2 网络 1.3 IP地址 1.5 域名 1.6 端口号 1.7 网络通信协议 1.8 TCP协议 1.9 UDP协议 2 In ...

  5. Linux 学习笔记之超详细基础linux命令 Part 3

    Linux学习笔记之超详细基础linux命令 by:授客 QQ:1033553122 ---------------------------------接Part 2----------------- ...

  6. java 网络编程学习笔记

    java 网络编程学习笔记 C/S模式:客户端和服务器 客户端创建流程 1 1.建立Socket端点 2 3 Socket s = new Socket(绑定地址, 绑定端口); 2.确认源数据方式和 ...

  7. [Linux网络编程学习笔记]索引

    一.Linux基本知识 [学习笔记]Linux平台的文件I/O操作 [学习笔记]Linux平台的文件,目录及操作 [Linux学习笔记]标准输入输出 [Linux学习笔记]进程概念及控制 [Linux ...

  8. 编程开发:Linux网络编程学习笔记

    非常全面.通俗易懂.值得借鉴的Linux网络编程学习笔记.关键字:linux linux编程 网络编程 linux网络编程 下载地址:点我下载 特别说明:本资源收集于网络,版权归原作者及版权商所有,仅 ...

  9. python网络编程学习笔记(二)

    python网络编程学习(四) 多用途客户端协议 一.ftp 功能:上传下载文件,删除命名文件,建立删除目录,自动压缩,保存目录 1.代码: #coding=utf-8 '''连接远程ftp服务器,显 ...

最新文章

  1. 【JavaScript总结】JavaScript语法基础:DOM
  2. 深度丨全球14家顶尖 AI 产业巨头深度学习实力及战略分析
  3. EasyX识别不到VC++6.0
  4. 使用DOM解析常用方法
  5. Socket常用语法与socketserver实例
  6. C++:求五位学生的总成绩以及平均成绩
  7. 求最大公约数---字符串并集---交集代码小结
  8. itop 导入AD用户
  9. 在Proteus上进行LED流水灯的仿真
  10. ApiCloud组件
  11. 两手空空也创业 没钱照样做老板
  12. mysql图形查询操作 点找面及面找点 Polygon获取中心点坐标 空间地理位置计算
  13. 网站中PV、UV、IP的区别
  14. 【数字图像处理】双三次插值及其卷积算法(Bicubic Interpolation)
  15. java基于springboot在线小说阅读网站
  16. php需要做临时表嘛,php中使用临时表查询数据的一个例子
  17. git 录制简单实用好工具 LICEcap
  18. Jenkins下载与安装
  19. java springboot房地产信息管理系统+实训报告+PPT+功能需求报告
  20. pythonelectron桌面开发案例_Web桌面应用框架1:Electron与WEB桌面应用程序开发及其它...

热门文章

  1. 安装inde.html使用babel,reactjs – 使用Babel Standalone进行单个React组件渲染,仅使用index.html和Component...
  2. python调用ctypes中windll中的方法超时处理_python中使用ctypes调用MinGW生成的动态链接库(dll)...
  3. php通过实现excel导入,php实现excel导入数据
  4. WordPress中输出当前页面SQL语句的方法
  5. html页面通过特殊链接:打电话,发短信,发邮件详细教程
  6. shell 删除文本中的重复行(sort+uniq/awk/sed) (方法=效率啊)
  7. Linux和windows中的换行符差异问题 LINUX的换行符在Windows记事本打开不换行或出现黑点
  8. javascript学习系列(4):数组中的some方法
  9. [css] css的height:100%和height:inherit之间有什么区别呢?
  10. 工作336:uni-ele-el-table修改宽度问题