IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 【UNIX网络编程】第5章TCP客户/服务器程序例子 -> 正文阅读

[网络协议]【UNIX网络编程】第5章TCP客户/服务器程序例子

5.1简单的回射客户/服务器

在这里插入图片描述

5.2 TCP回射服务器程序

#include "unp.h"
#include <time.h>
void str_echo(int sockfd)
{
	ssize_t n;
	char buf[MAXLINE];
again:
	// read函数从套接口读入数据 
	while ( (n = read(sockfd, buf, MAXLINE)) > 0)
		// Writen把数据回射给客户
		Writen(sockfd, buf, n);
	if (n < 0 && errno == EINTR)
		goto again;
	else if (n < 0)
		err_sys("str_echo: read error");
}

int main(int argc, char const *argv[])
{
	int listenfd, connfd; // 监听描述字、已连接描述字
	pid_t childpid;
	socklen_t clilen;  //值-结果变量
	// cliaddr存放客户的协议地址
	struct sockaddr_in servaddr, cliaddr;

	// socket、bind、listen是TCP服务器的监听描述子

	// 创建套接字
	listenfd = Socket(AF_INET, SOCK_STREAM, 0);
	bzero(&servaddr, sizeof(servaddr));
	// 置地址族为AF_INET
	servaddr.sin_family = AF_INET;
	// IP地址为INADDR_ANY,即允许服务器在任意网络接口上接受客户连接
	servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
	// 置端口号为13
	servaddr.sin_port = htons(SERV_PORT);
	// 通过bind函数,把服务器的端口捆绑到所创建的套接口
	Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
	// 将套接口转换成监听套接口
	// 来自客户的外来连接在这个监听套接口上由内核接受
	Listen(listenfd, LISTENQ);

	while {
		clilen = sizeof(cliaddr);   //初始化为套接口地址结构的大小
		// 服务器进程调用accept函数后处于睡眠状态,等待单个客户连接的到达和内核对它的接受(三次握手)
		connfd = Accept(listenfd, (SA *) &cliaddr, &clilen);

		if ( (childpid = Fork()) ==0)
		{
			// 子进程关闭监听套接口,父进程关闭已连接套接口
			Close(listenfd);
			str_echo(connfd);
			exit(0);
		}
		Close(connfd);
	}
	return 0;
}

5.3 TCP回射客户程序


#include "unp.h"
void str_cli(FILE *fp, int sockfd)
{
	char sendline[MAXLINE], recvline[MAXLINE];
	// 读入一行文本
	while (Fgets(sendline, MAXLINE, fp) != NULL) {
		// 将该行发给服务器
		Writen(sockfd, sendline. strlen(sendline));
		// Readline从服务器读入回射行,并把它写到标准输出
		if (Readline(sockfd, recvline, MAXLINE) == 0)
			err_quit("str_cli: server terminated prematurely");
		Fputs(recvline, stdout);
	}
}
int main(int argc, char const *argv[])
{
	int sockfd;
	struct sockaddr_in servaddr;
	if (argc != 2)
	{
		err_quit("usage: tcpcli<IPaddress>");
	}
	// 创建套接口
	sockfd = Socket(AF_INET, SOCK_STREAM, 0);
	bzero(&servaddr, sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	// 从unp.h获得服务器的端口号SERV_PORT
	servaddr.sin_port = htons(SERV_PORT);
	Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
	// 连接服务器
	Connect(sockfd, (SA *)&servaddr, sizeof(servaddr));
	str_cli(stdin, sockfd);
	exit(0);

	return 0;
}

客户调用socket和connect,后者引起TCP的三路握手过程。当三路握手完成后,客户的connect和服务器中的accept均返回,连接于是建立。接着发生的步骤如下:

  • 1.客户调用str_len函数,该函数阻塞于fgets调用,等待键盘输入一行文本;
  • 2.当服务器在的accept返回时,服务器调用fork,再由子进程调用str_echo。该函数调用readline,readline调用read,而read在等待客户送入一行文本期间阻塞。
  • 3.另一方面,服务器父进程再次调用accept并阻塞,等待下一个客户连接。

总结下正常终止客户和服务器的步骤:

  • 输入EOF字符时,fgets返回一个空指针,str_len函数返回;
  • 当str_len返回到客户的main函数时,main通过调用exit终止;
  • 进程终止处理的一部分是关闭所有打开的描述字,因此客户打开的套接口由内核关闭。这导致客户TCP发送一个FIN给服务器,服务器TCP则以ACK响应,这就是TCP连接终止序列的前半部分。至此,服务器套接口处于CLOSE_WAIT状态,客户套接口则处于FIN_WAIT_2状态;
  • 当服务器TCP接收FIN时,服务器进程阻塞于readline调用,于是readline返回0。这导致str_echo函数返回服务器子进程的main函数;
  • 服务器子进程通过调用exit来终止;
  • 服务器子进程中打开的所有描述字随之关闭。由子进程关闭已连接套接口引发TCP连接终止序列最后两个分节:一个从服务器到客户的FIN和一个从客户到服务器的ACK。至此,连接完全终止,客户套接口进入TIME_WAIT状态;
  • 进程终止处理的另一部分内容是:在服务器子进程终止时,给父进程发送一个SIGCHILD信号。此处父进程未对该信号进行处理,因此子进程进入僵死状态(状态Z)。

5.8 POSIX信号处理

Sigfunc *signal(int signo, Sigfunc *func) 
{
	struct sigaction act, oact;
	// sigaction结构的sa_handler元素被置为func参数
	act.sa_handler = func;
	sigemptyset(&act.sa_mask);
	act.sa_flags = 0;
	if (signo == SIGALRM)
	{
#ifdef SA_INTERRUPT
		act.sa_flags |= SA_INTERRUPT;
#endif
	} else {
#ifdef SA_RESTART
		act.sa_flags |= SA_RESTART;
#endif
	}
	if (sigaction(signo, &act, &oact) < 0)
	{
		return(SIG_ERR);
	}
	return(oact.sa_handler);
}

5.9 处理SIGCHILD信号

  • 设置僵死状态的目的是维护子进程的信息,以便父进程在以后某个时候获取。这些信息包括子进程的进程ID、终止状态以及资源利用信息(CPU时间、内存使用量等等)。
  • 如果一个进程终止,而该进程有子进程处于僵死状态,那么它的所有僵死子进程的父进程ID将被重置为1(init进程)。
  • 继承这些子进程的init进程将清理它们(即init进程将wait它们,从而去除它们的僵死状态)。
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2021-08-09 10:33:55  更:2021-08-09 10:35:40 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 19:50:20-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码