《TCP/IP网络编程》课后练习答案第三+四部分19~24章 尹圣雨
第十九章 Windows平台下线程的使用bcdbd请比较从内存中完全销毁Windows线程和Linux线程的方法Windows上的线程销毁是随其线程main函数的返回,在内存中自动销毁的。但是linux的线程销毁必须经过pthread_join函数或者pthread_detach函数的响应才能在内存空间中完全销毁通过线程创建过程解释内核对象、线程、句柄之间的关系线程也属...
第十九章 Windows平台下线程的使用
-
bcd
-
bd
-
请比较从内存中完全销毁Windows线程和Linux线程的方法
Windows上的线程销毁是随其线程main函数的返回,在内存中自动销毁的。但是linux的线程销毁必须经过pthread_join函数或者pthread_detach函数的响应才能在内存空间中完全销毁
-
通过线程创建过程解释内核对象、线程、句柄之间的关系
线程也属于操作系统的资源,因此会伴随着内核对象的创建,并为了引用内核对象而返回句柄。整理的话,可以通过句柄区分内核对象,通过内核对象区分线程
-
√ × ×
-
请解释"auto-reset模式"和"manual-reset模式"的内核对象。区分二者的内核对象特征是什么?
WaitForSingleObject
函数针对单个内核对象验证signaled状态时,由于发生事件(变为signaled状态)返回时,有时会把相应内核对象再次改为non-signaled状态。这种可以再次进入non-signaled状态的内核对象称为"auto-reset模式"的内核对象,而不会自动跳转到non-signaled状态的内核对象称为"manual-reset模式"的内核对象。
第二十章 Windows中的线程同步
-
c
-
× √ × √
-
本章示例SyncSema_win.c的Read函数中,退出临界区需要较长时间,请给出解决方案并实现
对可能超过一定时间处于阻塞状态的函数,如
scanf
函数的响应,应尽量不纳入临界区unsigned WINAPI Read(void * arg) { int i, rdData; for(i=0; i<5; i++) { fputs("Input num: ", stdout); scanf("%d", &rdData); WaitForSingleObject(semTwo, INFINITE); num=rdData; ReleaseSemaphore(semOne, 1, NULL); } return 0; }
-
更改程序,并运行结果
#include <stdio.h> #include <windows.h> #include <process.h> #define STR_LEN 100 unsigned WINAPI NumberOfA(void *arg); unsigned WINAPI NumberOfOthers(void *arg); static char str[STR_LEN]; static HANDLE hSema; int main(int argc, char *argv[]) { HANDLE hThread1, hThread2; hSema=CreateSemaphore(NULL, 0, 2, NULL); hThread1=(HANDLE)_beginthreadex(NULL, 0, NumberOfA, NULL, 0, NULL); hThread2=(HANDLE)_beginthreadex(NULL, 0, NumberOfOthers, NULL, 0, NULL); fputs("Input string: ", stdout); fgets(str, STR_LEN, stdin); ReleaseSemaphore(hSema, 2, NULL); WaitForSingleObject(hThread1, INFINITE); WaitForSingleObject(hThread2, INFINITE); ResetEvent(hSema); CloseHandle(hSema); return 0; } unsigned WINAPI NumberOfA(void *arg) { int i, cnt=0; WaitForSingleObject(hSema, INFINITE); for(i=0; str[i]!=0; i++) { if(str[i]=='A') cnt++; } printf("Num of A: %d \n", cnt); return 0; } unsigned WINAPI NumberOfOthers(void *arg) { int i, cnt=0; WaitForSingleObject(hSema, INFINITE); for(i=0; str[i]!=0; i++) { if(str[i]!='A') cnt++; } printf("Num of others: %d \n", cnt-1); return 0; }
第二十一章 异步通知I/O模型
-
结合send & recv 函数解释同步和异步方式的I/O。并请说明同步I/O的缺点,以及怎样通过异步I/O进行解决
同步I/O是指数据发送的时间点和函数返回的时间一致的I/O方式。即,send函数返还的时间是数据传输完毕的时间,recv函数返还的时间是数据接收完毕的时间。但是异步I/O是指I/O函数的返回时刻与数据收发的完成时刻不一致。同步I/O的缺点是,直到输入输出完成为止,都处于阻塞状态。在这段时间里,CPU实际上是没有被利用的,但由于阻塞状态,无法进行其他工作。从这一点看,异步I/O对CPU的使用效率优于同步I/O
-
异步I/O并不是所有状况下的最佳选择。它具有哪些缺点?何种情况下同步I/O更优?可以参考异步I/O相关源代码,亦可结合线程进行说明
异步I/O有优点也有缺点,缺点在于要在完成输入输出以后去确认。如果服务器的服务非常简单,回复所需的数据量很小,就会很不方便。特别是在每一个用户都生成一个线程的服务器中,没有必要一定使用异步I/O
-
全√
-
请从源代码的角度说明select函数和WSAEventSelect函数再使用上的差异
使用select函数时,要循环将观察的文件描述符传到select函数中。而如果使用
WSAEventSelect
函数,观察的对象会被操作系统记录下俩,无需每次都注册。而且,如果是用select函数,则到事件发生为止,应保持阻塞状态;而如果使用WSAEventSelect
函数,到事件发生为止,都不应该进入阻塞。 -
第17章的epoll可以在条件触发和边缘触发这2中方式下工作。那种方式更适合异步I/O模型?为什么?请概括说明
边缘触发很好地配合异步I/O模型。边缘触发模式并不会因为输入缓冲中有数据而发起消息。即在边缘触发中,对异步的存取发生过程,不会发起消息,而只注册新的输入事件。但是在条件触发中,当输入缓冲中有有数据时,仍然会发起消息
-
Linux中的epoll同样属于异步I/O模型。请说明原因
epoll方式将观察的对象的连接过程和事件发生过程分离成两种处理模式。因此,可以在事件发生后,在希望的时间确认活动是否发生。因此epoll可以说是一种异步I/O模式
-
如何获取WSAWaitForMultipleEvents可以监视的最大句柄数?请编写代码读取该值
获取WSA_MAXIMUM_WAIT_EVENTS的值即可
-
为何异步通知I/O模型中的事件对象必须是manual-reset模式
事件的发生,需要由
WSAWaitForMultipleEvents
函数调用寻找相应的句柄调用执行的;首先通过调用WSAWaitForMultipleEvents
函数寻找事件发生的句柄数目,然后遍历找到具体发生的事件进行处理。如果不是manual-reset模式,就会自动转换为signaled状态,无法用第二次的WSAWaitForMultipleEvents
函数进行操作处理 -
代码
/*****************************AysnNotiChatServ.c*****************************/ #include <stdio.h> #include <string.h> #include <winsock2.h> #define BUF_SIZE 100 void SendMsg(SOCKET clntSocks[], int clntCnt, char * msg, int len); void CompressSockets(SOCKET hSockArr[], int idx, int total); void CompressEvents(WSAEVENT hEventArr[], int idx, int total); void ErrorHandling(char *msg); int main(int argc, char *argv[]) { WSADATA wsaData; SOCKET hServSock, hClntSock; SOCKADDR_IN servAdr, clntAdr; SOCKET hSockArr[WSA_MAXIMUM_WAIT_EVENTS]; WSAEVENT hEventArr[WSA_MAXIMUM_WAIT_EVENTS]; WSAEVENT newEvent; WSANETWORKEVENTS netEvents; int numOfClntSock=0; int strLen, i; int posInfo, startIdx; int clntAdrLen; char msg[BUF_SIZE]; if(argc!=2) { printf("Usage: %s <port>\n", argv[0]); exit(1); } if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) ErrorHandling("WSAStartup() error!"); hServSock=socket(PF_INET, SOCK_STREAM, 0); servAdr.sin_family=AF_INET; servAdr.sin_addr.s_addr=htonl(INADDR_ANY); servAdr.sin_port=htons(atoi(argv[1])); if(bind(hServSock, (SOCKADDR*) &servAdr, sizeof(servAdr))==SOCKET_ERROR) ErrorHandling("bind() error"); if(listen(hServSock, 5)==SOCKET_ERROR) ErrorHandling("listen() error"); newEvent=WSACreateEvent(); if(WSAEventSelect(hServSock, newEvent, FD_ACCEPT)==SOCKET_ERROR) ErrorHandling("WSAEventSelect() error"); hSockArr[numOfClntSock]=hServSock; hEventArr[numOfClntSock]=newEvent; numOfClntSock++; while(1) { posInfo=WSAWaitForMultipleEvents( numOfClntSock, hEventArr, FALSE, WSA_INFINITE, FALSE); startIdx=posInfo-WSA_WAIT_EVENT_0; for(i=startIdx; i<numOfClntSock; i++) { int sigEventIdx= WSAWaitForMultipleEvents(1, &hEventArr[i], TRUE, 0, FALSE); if((sigEventIdx==WSA_WAIT_FAILED || sigEventIdx==WSA_WAIT_TIMEOUT)) { continue; } else { sigEventIdx=i; WSAEnumNetworkEvents( hSockArr[sigEventIdx], hEventArr[sigEventIdx], &netEvents); if(netEvents.lNetworkEvents & FD_ACCEPT) { clntAdrLen=sizeof(clntAdr); hClntSock=accept( hSockArr[sigEventIdx], (SOCKADDR*)&clntAdr, &clntAdrLen); newEvent=WSACreateEvent(); WSAEventSelect(hClntSock, newEvent, FD_READ|FD_CLOSE); hEventArr[numOfClntSock]=newEvent; hSockArr[numOfClntSock]=hClntSock; numOfClntSock++; puts("connected new client..."); } if(netEvents.lNetworkEvents & FD_READ) { strLen=recv(hSockArr[sigEventIdx], msg, sizeof(msg), 0); SendMsg(hSockArr, numOfClntSock, msg, strLen); } if(netEvents.lNetworkEvents & FD_CLOSE) { WSACloseEvent(hEventArr[sigEventIdx]); closesocket(hSockArr[sigEventIdx]); numOfClntSock--; CompressSockets(hSockArr, sigEventIdx, numOfClntSock); CompressEvents(hEventArr, sigEventIdx, numOfClntSock); } } } } WSACleanup(); return 0; } void SendMsg(SOCKET clntSocks[], int clntCnt, char * msg, int len) // send to all { int i; for(i=0; i<clntCnt; i++) send(clntSocks[i], msg, len, 0); } void CompressSockets(SOCKET hSockArr[], int idx, int total) { int i; for(i=idx; i<total; i++) hSockArr[i]=hSockArr[i+1]; } void CompressEvents(WSAEVENT hEventArr[], int idx, int total) { int i; for(i=idx; i<total; i++) hEventArr[i]=hEventArr[i+1]; } void ErrorHandling(char *msg) { fputs(msg, stderr); fputc('\n', stderr); exit(1); }
第二十二章 重叠I/O模型
-
异步通知I/O模型和重叠I/O模型在异步处理方面有哪些区别?
在异步I/O模型下,以异步的方式处理IO事件发生的过程
在重叠I/O模型下,还需要异步确认I/O完成的过程
-
请分析非阻塞I/O、异步I/O、重叠I/O之间的关系
异步I/O意味着异步处理,确认I/O完成的过程。为了实现这一过程,I/O必须在非阻塞模式下运行。当I/O以非阻塞模式运行并且I/O以异步方式进行时,I/O会重叠
-
阅读下面代码,指出问题并给出解决方案
若accpet函数返回失败,WSARecv传入的overlapped会报错,因为overlapped事先没有任何传入。若accept返回成功,因为所有的OVERLAPPED结构都相同,可以使用同一个OVERLAPPED结构体变量给多个WSARecv函数调用
-
请从源代码角度说明调用WSASend函数后如何验证进入Pending状态
WSASend函数再返回SOCKET_ERROR的状态下,利用WSAGetLastError函数是否返回WSA_IO_PENDING来验证
-
线程的"alertable wait状态"的含义是什么?说出能使线程进入这种状态的两个函数
所谓"alertable wait状态"是指等待接收操作系统信息的状态,在接下来的函数调用中,线程会进入alertable wait状态
- WaitForSingleObjectEx
- WaitForMultipleObjectEx
- WSAWaitForMultipleEvents
- SleepEx
第二十三章 IOCP
-
完成端口对象将分配多个线程用于处理I/O。如何创建这些线程?如何分配?请从源码级别进行说明
Completion Port
对象所分摊的线程由程序员创建。这些线程为了确认I/O的完成会调用GetQueuedCompletionStatus
函数。虽然任何线程都能调用GetQueuedCompletionStatus
函数,但实际得到I/O完成信息的线程数不会超过调用CreateIoCompletionPort
函数时指定的最大线程数。 -
CreateIoCompletionPort
函数与其他函数不同,提供2种功能。请问是哪2种?Completion Port对象的产生,CP对象与套接字进行连接
-
完成端口对象和套接字之间的连接意味着什么?如何连接?
首先通过
CreateIoCompletionPort
生成CP对象。之后再次使用CreateIoCompletionPort
函数将CP对象与套接字进行连接。 -
cd
-
√ √ ×
-
代码
#include <stdio.h> #include <stdlib.h> #include <process.h> #include <winsock2.h> #include <windows.h> #define BUF_SIZE 100 #define MAX_CLNT 256 #define READ 3 #define WRITE 5 typedef struct // socket info { SOCKET hClntSock; SOCKADDR_IN clntAdr; } PER_HANDLE_DATA, *LPPER_HANDLE_DATA; typedef struct // buffer info { OVERLAPPED overlapped; WSABUF wsaBuf; char buffer[BUF_SIZE]; } PER_IO_DATA, *LPPER_IO_DATA; DWORD WINAPI EchoThreadMain(LPVOID CompletionPortIO); void SendMsg(char * msg, DWORD len); void ErrorHandling(char *message); int clntCnt=0; SOCKET clntSocks[MAX_CLNT]; int main(int argc, char* argv[]) { WSADATA wsaData; HANDLE hComPort; SYSTEM_INFO sysInfo; LPPER_IO_DATA ioInfo; LPPER_HANDLE_DATA handleInfo; SOCKET hServSock; SOCKADDR_IN servAdr; int recvBytes, i, flags=0; if(WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) ErrorHandling("WSAStartup() error!"); hComPort=CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0); GetSystemInfo(&sysInfo); for(i=0; i<sysInfo.dwNumberOfProcessors; i++) _beginthreadex(NULL, 0, EchoThreadMain, (LPVOID)hComPort, 0, NULL); hServSock=WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, WSA_FLAG_OVERLAPPED); servAdr.sin_family=AF_INET; servAdr.sin_addr.s_addr=htonl(INADDR_ANY); servAdr.sin_port=htons(atoi(argv[1])); bind(hServSock, (SOCKADDR*)&servAdr, sizeof(servAdr)); listen(hServSock, 5); while(1) { SOCKET hClntSock; SOCKADDR_IN clntAdr; int addrLen=sizeof(clntAdr); hClntSock=accept(hServSock, (SOCKADDR*)&clntAdr, &addrLen); clntSocks[clntCnt++]=hClntSock; handleInfo=(LPPER_HANDLE_DATA)malloc(sizeof(PER_HANDLE_DATA)); handleInfo->hClntSock=hClntSock; memcpy(&(handleInfo->clntAdr), &clntAdr, addrLen); CreateIoCompletionPort((HANDLE)hClntSock, hComPort, (DWORD)handleInfo, 0); ioInfo=(LPPER_IO_DATA)malloc(sizeof(PER_IO_DATA)); memset(&(ioInfo->overlapped), 0, sizeof(OVERLAPPED)); ioInfo->wsaBuf.len=BUF_SIZE; ioInfo->wsaBuf.buf=ioInfo->buffer; WSARecv(handleInfo->hClntSock, &(ioInfo->wsaBuf), 1, &recvBytes, &flags, &(ioInfo->overlapped), NULL); } return 0; } DWORD WINAPI EchoThreadMain(LPVOID pComPort) { HANDLE hComPort=(HANDLE)pComPort; SOCKET sock; DWORD bytesTrans; LPPER_HANDLE_DATA handleInfo; LPPER_IO_DATA ioInfo; DWORD flags=0; int i; while(1) { GetQueuedCompletionStatus(hComPort, &bytesTrans, (LPDWORD)&handleInfo, (LPOVERLAPPED*)&ioInfo, INFINITE); sock=handleInfo->hClntSock; puts("message received!"); if(bytesTrans==0) // EOF { for(i=0; i<clntCnt; i++) // remove disconnected client { if(sock==clntSocks[i]) { while(i++<clntCnt-1) clntSocks[i]=clntSocks[i+1]; break; } } clntCnt--; closesocket(sock); free(handleInfo); free(ioInfo); continue; } SendMsg(ioInfo->buffer, bytesTrans); memset(&(ioInfo->overlapped), 0, sizeof(OVERLAPPED)); WSARecv(sock, &(ioInfo->wsaBuf), 1, NULL, &flags, &(ioInfo->overlapped), NULL); } return 0; } void SendMsg(char * msg, DWORD len) // send to all { int i; for(i=0; i<clntCnt; i++) send(clntSocks[i], msg, len, 0); } void ErrorHandling(char *message) { fputs(message, stderr); fputc('\n', stderr); exit(1); }
第二十四章 制作HTTP服务器端
-
abe
-
a
-
IOCP和epoll是可以保证高性能的典型服务器端模型,但如果在基于HTTP协议的Web服务器端使用这些模型,则无法保证一定能得到高性能。请说明原因
IOCP和epoll都是可以管理两个以上socket的服务器模型。即在称为观察对象的端口中,感知与IO相关的事件发生的端口,并处理相关的I/O服务器模式。如实对网络服务器来说,并不要管理两个以上的socket。因为只要完成一次请求和回答的过程,连接就会结束。因此,用IOCP和epoll提高性能,有一定的局限性
更多推荐
所有评论(0)