一、判断题
1. close()函数用于关闭套接字描述符,这个函数的调用会引发TCP的中止连接操作。(0)
2. 在TCP的客户端程序中,如果connect()函数调用失败,则套接字符不能再使用,必须关闭。(1)
3. shutdown(int sockfd, int howto)函数,可以根据参数howto关闭指定方向的数据传输。
(1)
4. 5. 6. 7.
getsockname()函数返回套接字对应的远程地址。(0)
inet_addr()函数既可用于IPV4也可用于IPV6的地址转换。(0)
Linux支持多种套接字类型,其中SOCKET_STREAM类型对应的是UDP协议。(0) 绑定地址时,可以指定地址为通配地址:INADDR_ANY,其值一般为0,它通知内
核选择IP地址。(1)
8. 函数bind返回的一个常见错误是:所绑定的地址已被其他进程使用,我们可以通过设置套接字选项SO_REUSEADDR来避免产生这个错误。(1)
9. 在UDP套接字程序中,客户端与服务器通信时,必须使用sendto()和recvfrom()函数。
(0)
10. TCP的服务器端绑定地址时,可以同时指定绑定的地址和端口号,也可以指定其中之一,甚至一个也不指定。(1)
11. 在TCP套接字程序中,当read()函数收到FIN数据时,其返回-1。(0) 12. 在TCP套接字程序中,客户端程序要生成二个套接字描述符。(0)
13. 在多线程并发服务器中,为了防止父子线程对描述符的操作造成混乱,在主线程创建子线程后,应在父线程中关闭不用的已连接描述符。(0)
14. 在线程专用数据中,如果一个进程中有n个线程,就有n个关键字key。(0) 15. 在多进程执行程序中,父进程不能先于子进程退出。(0)
16. pthread_key_delete(pthread_key_t key)函数删除进程内的TSD表示的关键字,该函数执行时要先检查TSD是否有绑定值。(0) 17. tpthread_join()可以等待可分离线程的结束。(0) 18. 线程专用数据是解决线程安全性的的唯一方法。(0)
19. select()是一个阻塞函数,它可以作为一个比sleep()更为精确的定时器。(1) 20. 设置SO_LINGER选项后,可以周期性的探测连接是否存活。(0) 21. 在异步I/O模型下,接收数据时,等待数据到达和拷贝数据的操作都是由内核开完成。
(1)
22. flags|=O_NONBLOCK该语句的作用是将flags的值赋值为O_NONBLOCK。(0) 23. 改变某个描述符的SO_KEEPALIVE选项值,不会影响到主机上打开的其它描述符。
(0)
24. 由于异步I/O模型的效率最高,所以目前被广泛使用。(0) 25. 可以使用fcntl()函数设置和获取套接字的选项。(1)
26. 设置套接字的SO_RCVTIMEO选项后,会影响write、send和sendto函数的I/O工作
模式。(0)
27. 绑定地址时,可以指定地址为通配地址:INADDR_ANY,其值一般为0,它通知内核选择IP地址。(1)
28. 线程调用exit()函数退出时,不会影响同一进程中的其他线程。(0) 29.
二、问答题
1. 什么是中间件?
答:中间件是网络环境中运行于操作系统与应用软件之间可以简化应用软件的复杂性克服网络环境多种挑战的一类系统软件。
2. 中间件规范有哪些? 答:OSF 的 DCE
OMG 的 OMA 微软 的 DNA SUN 的 J2EE Gartner 的 SOA
3. 简述基于TCP的多线程并发服务器服务器程序的编程流程。 答:(1)使用socket()函数创建套接字;
(2)将创建的套接字绑定到指定的地址结构; (3)listen()函数设置套接字为监听模式,使服务器进入被动打开的状态; (4)接收客户端的连接请求,建立连接; (5)接收,应答客户端的数据请求 (6)终止连接
客户端实现的步骤:
(1) 使用socket()函数创建套接字
(2) 调用connect()函数建立一个与TCP服务器的连接 (3) 发送数据请求,接收服务器的数据应答 (4) 终止连接
4. 基于TCP协议的套接字服务器程序中,会产生两种套接字描述符,简述这两种套接
字描述符分别是什么?分别由什么函数创建?以及这两种描述符各自的作用。 答:socket()创建的监听描述符和accept()创建的已连接描述符。监
听描述符用来监听一个端口,当有一个客户与服务器连接时,它使用这个端口号,而此时这个端口号正与这个套接字关联。已连接描述符默认会阻塞进程,直到有一个客户连接建立后返回,它返回的是一个新可用的套接字,这个套接字是连接套接字。
5. 在基于UDP和TCP协议的套接字程序中调用connect()函数的作用分别是什么?
答:TCP中调用connect()函数的作用是激发TCP的三次握手过程,建
立与远程服务器的连接;
UDP中调用connect()函数的作用是指定与之通信的对方的IP地址和端口号。
6. 请说明TCP套接字listen(int s, int backlog)函数中backlog参数的含义。
答:backlog参数规定了请求队列中的最大连接数,它对队列中等待服务请求的数目进行限制。
7. 简述connect()激发TCP的三路握手过程,常见的三种错误情况。
答:(1)如果客户没有收到SYN分节的响应(总共75秒,这之间需要
可能需要重发若干次SYN),返回ETIMEDOUT,这可能需要重发若干次SYN;(2)如果对客户的SYN的响应是RST,则表明该服务器主机在指定的端口上没有进程在等待与之相连。函数返回错误ECONNREFUSED;(3)如果客户发出的SYN在中间路由器上引发一个目的地不可达ICMP错误,客户端内核保存此消息,并按第一种情况,连续发送SYN,直到规定的超时时间,对方
仍没有响应,则返回保存的消息(即ICMP错误)EHOSTUNREACH或ENETUNREACH错误返回给进程。
8. 请简述fork和vfork的区别和联系。
答:fork后,子进程和父进程继续执行fork()函数后的指令。子
进程是父进程的副本。子进程拥有父进程的数据空间、堆栈的副本。但父、子进程并不共享这些存储空间部分。使用vfork()创建新进程时,父进程将被暂时阻塞,而子进程则可以借用父进程的地址空间。这个奇特状态将持续直到子进程退出,至此父进程才继续执行。
9. 网络编程中设计并发服务器,使用多进程和多线程技术有什么区别?
答:根本区别:用多进程每个进程有自己的地址空间(address space),
线程则共享地址空间。所有其它区别都是由此而来的:
1)速度:线程产生的速度快,线程间的通讯快、切换快等,因为他们在同一个地址空间内。 2)资源利用率:线程的资源利用率比较好也是因为他们在同一个地址空间内。
3)同步问题:线程使用公共变量/内存时需要使用同步机制还是因为他们在同一个地址空间内。
10. 简述select()函数中timeout参数在不同的取值情况中,对select阻塞状态的影响。
答:(1)永远等待下去:仅在有一个描述字准备好I/O时才返回,因
此可以将参数timeout设置为空指针。
(2)等待固定时间:在有一个描述字准备好I/O时返回,但不超过由timeout()参数所指timeval结构中指定的秒数和微秒数。
(3)根本不用等待:检查描述字后立即返回,这称为轮询。
11. 请说明在Linux系统中阻塞I/O和I/O复用两种模型的异同点。
答:i/o复用模型调用select或poll,进程阻塞于这两个系统调用上,
而不是阻塞于真正的i/o系统调用上。与阻塞i/o模型相比,由于使用了系统调用select,似乎比阻塞i/o还差。但select的好处在于可以等待多个描述字准备好。IO复用模型是多了一个select函数,select函数有一个参数是文件描述符集合,意思就是对这些的文件描述符进行循环监听,当某个文件描述符就绪的时候,就对这个文件描述符进行处理。这种IO模型是属于阻塞的IO。但是由于它可以对多个文件描述符
12. 设置套接字选项时,请指出SO_RCVBUF选项所在的层(网络协议中的哪一层)?
并具体说明在服务器和客户端设置该选项时,必须在调用哪一网络编程函数之前进行设置及理由。
答:应用层 (1分)
在客户端必须,必须在调用connect之前进行设置,因为TCP的窗口规则是在建立连接时用SYN与对方互换得来的。 (2分)
在服务器端,在调用listen之前必须给监听套接字设置,因为服务器端的连接套接字的接收缓冲区是从监听套接字的接收缓冲区继承而来。 (2分)
三、填空题
1. select()函数可以测试( 读 )、( 写 )和异常描述集合中的任意个描述符是否准备好。 2. socket提供的网络编程接口位于TCP/IP模型的(应用层)和(传输层)两个层次之间。
3. 在多进程并发服务器中,为了防止父子进程对描述符的操作造成混乱,在父进程创建子进程后,应在父进程中关闭不用的(已连接)描述符,在子进程中关闭不用的(监听)描述符。
4. pthread_detach(pthread_t tid)的作用是将一个(汇合)线程变成(分离)线程。
5.基于TCP协议的套接字服务器程序中,会产生两个描述,它们分别是由socket()函数创建的(监听)描述符和(accept())函数创建的(已连接)描述符。 6. 谈话程序
双方都可以从终端输入一串字符(以回车结束),通过UDP的方式发送到对方,并显示在对方的终端上。从命令行输入目的地址、目的端口、源地址、源端口。使用I/O复用,进程阻塞到select,当标准输入和socket有数据时返回。根据以下程序示例完成程序填空。 fd_set infds; For(;;) { FD_ZERO(&infd); /*重置句柄集 */ FD_SET(fileno(stdin),&infds);FD_SET(sockfd,&infds); /*把标准输入置入句柄集*/
/*把socket置入句柄集*/
maxfd=max(fileno(stdin),sockfd)+1;
if(select(maxfd,&infds,NULL,NULL,NULL)==-1) { /*错误处理*/ }
if(FD_ISSET(sockfd,&infds)) /*测试socket是否可读*/ { /* 读socket */ }
if(FD_ISSET(fileno(stdin),&infds)) /*测试标准输入是否可读*/ { /*读标准输入 */ } }
7. 信号驱动I/O的实现
信号驱动I/O的实现,要通过信号驱动方式对多个句柄进行复用,要通过如下几个步骤:将这些句柄(描述符)的属主都设为一个进程。将这些句柄(描述符)的标志位O_ASYNC打开,让句柄(描述符)发生I/O事件时发送SIGIO信号。在属主进程中设置对SIGIO信号的处理。当有I/O事件在任何一个句柄(描述符)上发生时,系统都会向进程发送SIGIO信号。进程在对SIGIO的中断处理中再查询各个句柄(描述符)状态,进行相应的处理。根据以上描述完成下面的程序填空。
void ioaction(int signo) /*定义信号处理程序*/ {….} int main() {
stdinfd=fileno(stdin);
signal(SIGIO,ioaction);/*指定信号处理程序*/
/*句柄(描述符)所有者为当前进程*/
fcntl(sockfd,F_SETOWN,geypid()); int on=1;
ioctl(sockfd,FIOASYNC,&on); /*sockfd发生I/O事件时发送SIGIO信号*/ for(;;) {
n=read(stdinfd,msg,BUFLEN);/*可能阻塞*/ } }
8. 非阻塞I/O的实现
非阻塞I/O的实现,可以通过fcntl()来改变句柄(或描述符)的标志来实现。 将句柄(描述符)sockfd设置为非阻塞的代码是。 int flags:
flags = fcntl(sockfd,F_GETFL,0);/*读取句柄当前标志*/ /*加入非阻塞标志 */
/*设置带有非阻塞标志的新标志*/
flags |= O_NONBLOCK;fcntl(sockfd,F_SETFL,flags);9. (根据程序写运行结果)线程问题
#include pthread_mutex_t mymutex=PTHREAD_MUTEX_INITIALIZER; void *thread_function(void *arg) { int i,j; for ( i=0; i<6; i++) { pthread_mutex_lock(&mymutex); j=myglobal; j=j+1; printf(\".\"); fflush(stdout); sleep(1); myglobal=j; pthread_mutex_unlock(&mymutex); } return NULL; } int main(void) { pthread_t mythread; int i; if ( pthread_create( &mythread, NULL, thread_function, NULL) ) { printf(\"error creating thread.\"); abort(); } for ( i=0; i<6; i++) { pthread_mutex_lock(&mymutex); myglobal=myglobal+1; pthread_mutex_unlock(&mymutex); printf(\"F\"); fflush(stdout); sleep(1); } if ( pthread_join ( mythread, NULL ) ) { printf(\"error joining thread.\"); abort(); } printf(\"\\nmyglobal equals %d\\n\ exit(0); } 运行结果如下: .F.F.F.F.F.F Myglobal equals 12 10. (根据程序写运行结果)线程问题 #include pthread_once_t once=PTHREAD_ONCE_INIT; void once_run(void) { printf(\"once_run in thread %d\\n\} void * child1(void *arg) { int tid=pthread_self(); printf(\"thread %d enter\\n\ pthread_once(&once,once_run); printf(\"thread %d returns\\n\ } void * child2(void *arg) { int tid=pthread_self(); printf(\"thread %d enter\\n\ pthread_once(&once,once_run); printf(\"thread %d returns\\n\ } int main(void) { int tid1,tid2; printf(\"hello\\n\"); pthread_create(&tid1,NULL,child1,NULL); pthread_create(&tid2,NULL,child2,NULL); sleep(10); printf(\"main thread exit\\n\"); return 0; } 运行结果如下,线程的ID随便用一个整数来代替。 Hello Thread 123456 enter Once_run in thread 123456 Thread 123456 returns Thread 654321 enter Thread 654321 returns Main thread exit 11. (根据程序写运行结果)主机问题 main(int argc, const char **argv) { ulong_t addr; struct hostent *hp; char **p; if (argc != 2) { printf(\"usage: %s IP-address\\n\ exit (1); } if (((addr = inet_addr(argv[1])) == -1) { printf(\"IP-address must be of the form a.b.c.d\\n\"); exit (1); } hp = gethostbyaddr((char *)&addr, sizeof (addr), AF_INET); if (hp == NULL) { printf(\"host information for %s not found\\n\ exit (1); } for (p = hp->h_addr_list; *p != 0; p++) { struct in_addr in; char **q; memcpy(&in.s_addr, *p, sizeof (in.s_addr)); printf(\"%s\%s\ for (q = hp->h_aliases; *q != 0; q++) printf(\" %s\ } exit (0); } 执行 ./b 127.0.0.1 命令后 运行结果如下: 127.0.0.1 localhost.localdomain localhost 三、综合题 1. 设计一段程序,作为UDP套接字的客户端,与之通信的服务器的IP地址为192.168.0.12, 端口为2345。其中头文件可以省略,客户端的功能是(1)将用户从键盘输入的内容发送给指定服务器(2)接收该指定服务器回应的数据(3)打印出数据内容并退出。 (20 分) 26.ZH_diao_2202 基于TCP的服务端程序 该实例包括服务器程序和客户程序,具体功能如下:服务器等待客户连接请求,连接成功后显示客户地址,并接收该客户的名字并显示,然后接收来自客户的信息(字符串)并显示,然后将该字符串反转,并将结果送回客户,之后,继续等待接收该客户的信息直至该客户关闭连接。要求服务器具有同时处理多个客户的能力。客户首先与服务器连接,接着接收用户输入客户的名字,并将该名字发送给服务器,接收用户输入的字符串,发送给服务器,接收服务器返回的经处理后的字符串,并显示之。之后,继续等待用户输入直至用户输入Ctrl+C,终止连接并退出。 本题只需要编写服务端部分关键代码。 int main(void) { int listenfd, connfd; pid_t pid; intBACKLOG = 5; if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { perror(“Create socket failed.”); exit(1); } bind(listenfd, …); listen(listenfd, BACKLOG); while(1) { } void process_cli(int connectfd, struct sockaddr_in client) { } 因篇幅问题不能全部显示,请点此查看更多更全内容