《TCP/IP网络编程》课后练习答案第二部分15~18章 尹圣雨

第十五章 套接字和标准IO

  1. 请说明标准I/O函数的2个优点。它为何拥有这2个优点?

    • 基于ANSIX标准具有良好的一致性

    • 可以利用缓冲提高性能

  2. 标准IO中,“调用fputs函数传输数据时,调用后应立即开始发送!”,为何这种想法是错误的?为了达到这种效果应添加哪些处理过程?

    通过标准输出函数的传输的数据不直接通过套接字的输出缓冲区发送,而是保存在标准输出函数的缓冲中,然后再用fflush函数进行输出。因此,即使调用“fputs"函数,也不能立即发送数据。如果想保障数据传输的时效性,必须经过fflush函数的调用过程

第十六章 关于I/O流分离的其他内容

  1. abcd
  2. 全部都是正确的,没有错误选项

第十七章 优于select的epoll

  1. 利用select函数实现服务器端时,代码层面存在的2个缺点是?

    • 调用select函数后常见的针对所有文件描述符的循环语句
    • 每次调用select函数时都需要向该函数传递监视对象信息
  2. 无论是select方式还是epoll方式,都需要将监视对象文件描述符信息通过函数调用传递给操作系统。请解释传递该信息的原因

    select和epoll是系统函数,准确地说,是要求观察套接字变化的方式的。套接字是受操作系统进行管理的。既,select和epoll是一个有操作系统执行的函数。因此,应该将监视对象的文件描述符传递给操作系统

  3. select方式和epoll方式的最大差异在于监视对象文件描述符传递给操作系统的方式。请说明具体的差异,并解释为何存在这种差异。

    epoll不同于select的地方是只要将监视对象文件描述符的信息传递一个给操作系统就可以了。因此epoll方式克服了select方式的缺点,体现在linux内核上保存监视对象信息的方式。

  4. 虽然epoll是select的改进方式,但select也有自己的缺点。在何种情况下使用select方式更合理

    如果连接服务器的人数不多(不需要高性能),而且需要在多种操作系统(windows和linux)下进行操作,在兼容性方面,使用select会比epoll更合理

  5. epoll以条件触发或边缘触发方式工作。二者有何区别?从输入缓冲的角度说明这2中方式通知事件的时间点的差异

    在条件触发方式中,只要输入缓冲有数据,就会持续进行事件通知;而在边缘触发中,只有当输入缓冲数据为空时才进行通知

  6. 采用边缘触发时可以分离数据的接收和处理时间点。说明其原因及优点。

    如果使用边缘触发方式,在输入缓冲中接收数据时,只会发生一次事件通知,而且输入缓冲中仍有数据时,不会进行通知,因此可以在数据被接收后,在想要的时间内处理数据。而且,如果分离数据的接收和处理时间点,在服务器中会有更大的灵活性

  7. 实现聊天服务器,条件触发和边缘触发两种epoll方式实现

    /**********************************char_EPLTserv.c*************************/
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <arpa/inet.h>
    #include <sys/socket.h>
    #include <sys/epoll.h>
    
    #define BUF_SIZE 100
    #define MAX_CLNT 256
    #define EPOLL_SIZE 50
    void error_handling(char *buf);
    void send_msg(char * msg, int len);
    
    int clnt_cnt=0;
    int clnt_socks[MAX_CLNT];
    
    int main(int argc, char *argv[])
    {
    	int serv_sock, clnt_sock;
    	struct sockaddr_in serv_adr, clnt_adr;
    	socklen_t adr_sz;
    	int str_len, i;
    	char buf[BUF_SIZE];
    
    	struct epoll_event *ep_events;
    	struct epoll_event event;
    	int epfd, event_cnt;
    
    	if(argc!=2) {
    		printf("Usage : %s <port>\n", argv[0]);
    		exit(1);
    	}
    
    	serv_sock=socket(PF_INET, SOCK_STREAM, 0);
    	memset(&serv_adr, 0, sizeof(serv_adr));
    	serv_adr.sin_family=AF_INET;
    	serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);
    	serv_adr.sin_port=htons(atoi(argv[1]));
    	
    	if(bind(serv_sock, (struct sockaddr*) &serv_adr, sizeof(serv_adr))==-1)
    		error_handling("bind() error");
    	if(listen(serv_sock, 5)==-1)
    		error_handling("listen() error");
    
    	epfd=epoll_create(EPOLL_SIZE);
    	ep_events=malloc(sizeof(struct epoll_event)*EPOLL_SIZE);
    
    	event.events=EPOLLIN;
    	event.data.fd=serv_sock;	
    	epoll_ctl(epfd, EPOLL_CTL_ADD, serv_sock, &event);
    
    	while(1)
    	{
    		event_cnt=epoll_wait(epfd, ep_events, EPOLL_SIZE, -1);
    		if(event_cnt==-1)
    		{
    			break;
    		}
    
    		for(i=0; i<event_cnt; i++)
    		{
    			if(ep_events[i].data.fd==serv_sock)
    			{
    				adr_sz=sizeof(clnt_adr);
    				clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_adr, &adr_sz);
    				event.events=EPOLLIN;
    				event.data.fd=clnt_sock;
    				epoll_ctl(epfd, EPOLL_CTL_ADD, clnt_sock, &event);
    				clnt_socks[clnt_cnt++]=clnt_sock;
    				printf("connected client: %d \n", clnt_sock);
    			}
    			else
    			{
    				str_len=read(ep_events[i].data.fd, buf, BUF_SIZE);
    				if(str_len==0)  
    				{
    					epoll_ctl(epfd, EPOLL_CTL_DEL, ep_events[i].data.fd, NULL);
    					close(ep_events[i].data.fd);
    					printf("closed client: %d \n", ep_events[i].data.fd);
    
    					for(i=0; i<clnt_cnt; i++) 
    					{
    						if(clnt_sock==clnt_socks[i])
    						{
    							while(i++<clnt_cnt-1)
    								clnt_socks[i]=clnt_socks[i+1];
    							break;
    						}
    					}
    					clnt_cnt--;
    				}
    				else
    				{
    					send_msg(buf, str_len);
    				}
    			}
    		}
    	}
    	close(serv_sock);
    	close(epfd);
    	return 0;
    }
    
    void send_msg(char * msg, int len)   // send to all
    {
    	int i;
    	for(i=0; i<clnt_cnt; i++)
    		write(clnt_socks[i], msg, len);
    }
    
    void error_handling(char *buf)
    {
    	fputs(buf, stderr);
    	fputc('\n', stderr);
    	exit(1);
    }
    /**********************************char_EPETserv.c*************************/
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <errno.h>
    #include <arpa/inet.h>
    #include <sys/socket.h>
    #include <sys/epoll.h>
    
    #define BUF_SIZE 100
    #define MAX_CLNT 256
    #define EPOLL_SIZE 50
    
    void setnonblockingmode(int fd);
    void error_handling(char *buf);
    void send_msg(char * msg, int len);
    
    int clnt_cnt=0;
    int clnt_socks[MAX_CLNT];
    
    int main(int argc, char *argv[])
    {
    	int serv_sock, clnt_sock;
    	struct sockaddr_in serv_adr, clnt_adr;
    	socklen_t adr_sz;
    	int str_len, i;
    	char buf[BUF_SIZE];
    
    	struct epoll_event *ep_events;
    	struct epoll_event event;
    	int epfd, event_cnt;
    
    	if(argc!=2) {
    		printf("Usage : %s <port>\n", argv[0]);
    		exit(1);
    	}
    
    	serv_sock=socket(PF_INET, SOCK_STREAM, 0);
    	memset(&serv_adr, 0, sizeof(serv_adr));
    	serv_adr.sin_family=AF_INET;
    	serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);
    	serv_adr.sin_port=htons(atoi(argv[1]));
    	
    	if(bind(serv_sock, (struct sockaddr*) &serv_adr, sizeof(serv_adr))==-1)
    		error_handling("bind() error");
    	if(listen(serv_sock, 5)==-1)
    		error_handling("listen() error");
    
    	epfd=epoll_create(EPOLL_SIZE);
    	ep_events=malloc(sizeof(struct epoll_event)*EPOLL_SIZE);
    
    	setnonblockingmode(serv_sock);
    	event.events=EPOLLIN;
    	event.data.fd=serv_sock;	
    	epoll_ctl(epfd, EPOLL_CTL_ADD, serv_sock, &event);
    
    	while(1)
    	{
    		event_cnt=epoll_wait(epfd, ep_events, EPOLL_SIZE, -1);
    		if(event_cnt==-1)
    		{
    			puts("epoll_wait() error");
    			break;
    		}
    
    		puts("return epoll_wait");
    		for(i=0; i<event_cnt; i++)
    		{
    			if(ep_events[i].data.fd==serv_sock)
    			{
    				adr_sz=sizeof(clnt_adr);
    				clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_adr, &adr_sz);
    				setnonblockingmode(clnt_sock);
    				event.events=EPOLLIN|EPOLLET;
    				event.data.fd=clnt_sock;
    				epoll_ctl(epfd, EPOLL_CTL_ADD, clnt_sock, &event);
    				clnt_socks[clnt_cnt++]=clnt_sock;
    				printf("connected client: %d \n", clnt_sock);
    			}
    			else
    			{
    					while(1)
    					{
    						str_len=read(ep_events[i].data.fd, buf, BUF_SIZE);
    						if(str_len==0)    // close request!
    						{
    							epoll_ctl(epfd, EPOLL_CTL_DEL, ep_events[i].data.fd, NULL);
    							close(ep_events[i].data.fd);
    
    							for(i=0; i<clnt_cnt; i++) 
    							{
    								if(ep_events[i].data.fd==clnt_socks[i])
    								{
    									while(i++<clnt_cnt-1)
    										clnt_socks[i]=clnt_socks[i+1];
    									break;
    								}
    							}
    							clnt_cnt--;
    							printf("closed client: %d \n", ep_events[i].data.fd);
    							break;
    						}
    						else if(str_len<0)
    						{
    							if(errno==EAGAIN)
    								break;
    						}
    						else
    						{
    							send_msg(buf, str_len);
    						}
    				}
    			}
    		}
    	}
    	close(serv_sock);
    	close(epfd);
    	return 0;
    }
    
    
    void send_msg(char * msg, int len)   // send to all
    {
    	int i;
    	for(i=0; i<clnt_cnt; i++)
    		write(clnt_socks[i], msg, len);
    }
    void setnonblockingmode(int fd)
    {
    	int flag=fcntl(fd, F_GETFL, 0);
    	fcntl(fd, F_SETFL, flag|O_NONBLOCK);
    }
    void error_handling(char *buf)
    {
    	fputs(buf, stderr);
    	fputc('\n', stderr);
    	exit(1);
    }
    

第十八章 多线程服务器的实现

  1. 单CPU系统中如何同时执行多个进程?请解释该过程中发生的上下文切换

    因为系统将CPU切分成多个微小的块后分配给多个进程,为了分时使用CPU,需要”上下文切换“过程。”上下文切换“是指,在CPU改变运行对象的过程中农,执行准备的过程将之前执行的进程数据从换出内存,并将待执行额进程数据传到内存的工作区域

  2. 为何线程的上下文切换速度相对更快?线程间数据交换为何不需要类似IPC的特别技术?

    因为线程进行上下文切换时不需要切换数据区和堆区。同时,可以利用数据区和堆区进行数据交换

  3. 请从执行流角度说明进程和线程的区别

    • 进程:在操作系统中构成单独执行流的单位
    • 线程:在进程内构成单独执行流的单位
  4. bcd

  5. d

  6. 请说明完全销毁Linux线程的两种方法

    pthread_join函数和pthread_detach函数

  7. 利用多线程实现回声客户端,要求所有线程共享保存客户端消息的内存空间

    //echo_thrserv.c
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <arpa/inet.h>
    #include <sys/socket.h>
    #include <sys/select.h>
    #include <pthread.h>
    
    #define BUF_SIZE 100
    void * handle_clnt(void * arg);
    void error_handling(char *buf);
    
    char buf[BUF_SIZE];
    pthread_mutex_t mutx;
    
    int main(int argc, char *argv[])
    {
    	int serv_sock, clnt_sock;
    	struct sockaddr_in serv_adr, clnt_adr;
    	int clnt_adr_sz;
    	pthread_t t_id;
    
    	if(argc!=2) {
    		printf("Usage : %s <port>\n", argv[0]);
    		exit(1);
    	}
    
    	pthread_mutex_init(&mutx, NULL);
    	serv_sock=socket(PF_INET, SOCK_STREAM, 0);
    	memset(&serv_adr, 0, sizeof(serv_adr));
    	serv_adr.sin_family=AF_INET;
    	serv_adr.sin_addr.s_addr=htonl(INADDR_ANY);
    	serv_adr.sin_port=htons(atoi(argv[1]));
    	
    	if(bind(serv_sock, (struct sockaddr*) &serv_adr, sizeof(serv_adr))==-1)
    		error_handling("bind() error");
    	if(listen(serv_sock, 5)==-1)
    		error_handling("listen() error");
    
    	while(1)
    	{
    		clnt_adr_sz=sizeof(clnt_adr);
    		clnt_sock=accept(serv_sock, (struct sockaddr*)&clnt_adr,&clnt_adr_sz);
    		pthread_create(&t_id, NULL, handle_clnt, (void*)&clnt_sock);
    		pthread_detach(t_id);
    		printf("Connected client IP: %s \n", inet_ntoa(clnt_adr.sin_addr));
    	}
    
    	close(serv_sock);
    	return 0;
    }
    
    void * handle_clnt(void * arg)
    {
    	int clnt_sock=*((int*)arg);
    	int str_len=0;
    	
    	while(1)
    	{
    		pthread_mutex_lock(&mutx);
    		str_len=read(clnt_sock, buf, sizeof(buf));
    		if(str_len<=0)
    			break;
    		else
    			write(clnt_sock, buf, str_len);
    		pthread_mutex_unlock(&mutx);
    	}
    	
    	close(clnt_sock);
    	return NULL;
    }
    void error_handling(char *buf)
    {
    	fputs(buf, stderr);
    	fputc('\n', stderr);
    	exit(1);
    }
    
  8. 上一题有什么问题?

    如果不同步,两个以上线程会同时访问内存空间,从而引发问题。相反,如果同步,由于read函数中调用了临界区的参数,可能会发生无法读取其他客户端发送过来的字符串而必须等待的情况

  • 12
    点赞
  • 71
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值