|
簡答題 1.采用阻塞I/O模型時(shí),套接字函數(shù)read()的返回值有哪幾種?分別對應(yīng)什么情況?
返回值n | 對應(yīng)情況 | 無數(shù)據(jù)
| 阻塞
| n >= len
| 讀取len個(gè)字節(jié)
| 0 < n < len
| 讀取n個(gè)字節(jié)
| n = 0
| 讀通道關(guān)閉
| n = -1 且 ERRON = EINTR
| 讀中斷引起錯(cuò)誤
| n = -1 且 ERRON = ECONNREST
| 網(wǎng)絡(luò)連接有問題
|
2.為什么客戶機(jī)通常不需要綁定自己的端口號?
當(dāng)客戶機(jī)請求連接時(shí),系統(tǒng)會自動為它選擇一個(gè)未用的端口號,并且用本地的IP地址設(shè)置嵌套字地址的相應(yīng)項(xiàng)。
3.close()函數(shù)和shutdown()函數(shù)有何差別?
①shutdown操作連接通道,其他進(jìn)程不能再使用已被關(guān)閉的通道;close操作描述符,其他進(jìn)程仍然可以使用該socket描述符
②close關(guān)閉應(yīng)用程序與socket的接口,調(diào)用close之后進(jìn)程不能再讀寫這個(gè)socket;shudown可以只關(guān)閉一個(gè)通道,另一個(gè)通道仍然可以操作
4.僵尸進(jìn)程有什么危害?清除僵尸進(jìn)程的方法有哪些?(至少4種)
僵尸進(jìn)程雖然對其他進(jìn)程幾乎沒有什么影響,不占用CPU時(shí)間,消耗的內(nèi)存也幾乎可以忽略不計(jì),但Linux系統(tǒng)中進(jìn)程數(shù)目是有限的,在一些特殊的情況下,如果存在太多的僵尸進(jìn)程,會影響到新進(jìn)程的產(chǎn)生。
清除方法:
①忽略SIGCHLD信號,系統(tǒng)將清除子進(jìn)程的進(jìn)程表項(xiàng),這種方法依賴于Linux版本的實(shí)現(xiàn)
②調(diào)用函數(shù)wait()或waitpid()等待子進(jìn)程。這種方法沒有兼容性問題,但主程序進(jìn)入等待循環(huán)后不能做任何事情
③捕獲SIGCHLD信號,在相應(yīng)的處理函數(shù)中調(diào)用wait()或waitpid()對子進(jìn)程進(jìn)行處理
④調(diào)用fork()兩次,使子進(jìn)程成為孤兒進(jìn)程,由init進(jìn)程管理
5.Linux系統(tǒng)默認(rèn)的I/O模型是什么?請給出兩種將阻塞式模型轉(zhuǎn)換為非阻塞式模型的方法
默認(rèn)的I/O模型是阻塞式I/O模型
方法一:fcntl
int flags = fcntl(sockfd, F_GETF, 0);
flags |= O_NONBLOCK;
fcntl(sockfd, F_SETF, flags);
方法二:ioctl
int on = 1;
Ioctl(sockfd, FIONBIO, &on);
6.函數(shù)fork()與exec()的差別
①fork()是執(zhí)行一個(gè)新的任務(wù),exec()執(zhí)行另一個(gè)程序
②fork()的子進(jìn)程共享正文,exec()將改變子進(jìn)程的正文
③fork()在創(chuàng)建完進(jìn)程后立即返回,exec執(zhí)行時(shí)函數(shù)不返回,只有錯(cuò)誤時(shí)才返回
7.創(chuàng)建一個(gè)守護(hù)進(jìn)程有哪些步驟
①調(diào)用fork(),父進(jìn)程退出,子進(jìn)程繼續(xù)運(yùn)行
②調(diào)用setsid()創(chuàng)建新的session
③忽略信號SIGHUP,再次調(diào)用fork(),父進(jìn)程(session的頭進(jìn)程)退出
④調(diào)用chdir("/"),使進(jìn)程不使用任何目錄
⑤調(diào)用unmask(0),使進(jìn)程對任何內(nèi)容都有寫權(quán)限
⑥關(guān)閉所有打開的文件描述符,為標(biāo)準(zhǔn)輸入、標(biāo)準(zhǔn)輸出、標(biāo)準(zhǔn)錯(cuò)誤輸出打開新的文件描述符0、1、2
8.列舉出不少于5項(xiàng)的進(jìn)程間通信方式
①管道②信號量③消息隊(duì)列④消息郵箱⑤共享內(nèi)存⑥內(nèi)存映像文件⑦Unix域Socket
9.請給出利用Socket接口實(shí)現(xiàn)面向連接的網(wǎng)絡(luò)編程模型
在51黑附件中
10.網(wǎng)絡(luò)編程時(shí),為什么要考慮字節(jié)順序問題
不同機(jī)器表示數(shù)據(jù)的字節(jié)順序是不同的:
Intel處理器:低字節(jié)在前,高字節(jié)在后,稱為小端存儲
Motorola處理器:高字節(jié)在前,低字節(jié)在后,稱為大端存儲
11.在使用UDP套接字時(shí)也可以使用connnect()函數(shù),連接UDP套接字有哪些特點(diǎn)?
使得在無連接的UDP通信中,發(fā)送時(shí)不必帶目的地址,接收時(shí)不必帶源地址
12.服務(wù)器經(jīng)常使用說明綁定方式,為什么
常使用綁定任意地址方式(INADDR_ANY)
這樣服務(wù)器可以接收發(fā)送到服務(wù)器的任意IP地址的數(shù)據(jù)
13.試比較pipe和Unix域套接字的差異
Pipe是單工通信,Unix域套接字是雙工通信
編程題 1.請給出實(shí)現(xiàn)一個(gè)守護(hù)進(jìn)程的程序代碼
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/stat.h>
void init_daemon(void)
{
int pid, i;
/*
步驟一:調(diào)用fork(),父進(jìn)程退出,子進(jìn)程繼續(xù)
*/
if(pid = fork())
exit(0); //父進(jìn)程直接退出
else if(pid < 0)
exit(1); //fork()失敗退出
/*
步驟二:調(diào)用setsid()創(chuàng)建新的會話
*/
//第一子進(jìn)程
setsid(); //第一子進(jìn)程成為新的會話組組長,并與控制臺終端分離
/*
步驟三:再次調(diào)用fork(),會話組組長退出
*/
if(pid = fork())
exit(0); //是第一子進(jìn)程,退出
else if(pid < 0)
exit(1); //fork()失敗退出
//第二子進(jìn)程(孫進(jìn)程),不再是會話組組長
for(i = 0; i < NOFILE; i++)
close(i); //關(guān)閉打開的文件描述符
/*
步驟四:釋放掛載的文件系統(tǒng)
*/
chdir("\n"); //改變當(dāng)前目錄到系統(tǒng)目錄下,釋放掛載的文件系統(tǒng)
/*
步驟五:提升進(jìn)程權(quán)限
*/
umask(0); //使守護(hù)進(jìn)程擁有高權(quán)限
}
int main()
{
FILE *fp;
time_t t;
init_daemon();
while(1)
{
sleep(3);
if((fp = fopen("test.log", "a")) >= 0)
{
t = time(0);
fprintf(fp, "daemon process logged at: %s \n",asctime(localtime(&t)));
fclose(fp);
}
}
return 0;
}
2.編程產(chǎn)生2個(gè)進(jìn)程:子進(jìn)程1和子進(jìn)程2,然后利用管道,將字符串“abcde”從子進(jìn)程1傳到子進(jìn)程2
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main()
{
int fd[2], pid;
if(pipe(fd) == -1)
perror("pipe()");
if((pid = fork()) == -1)
perror("fork()");
else if(pid > 0)
{
close(fd[0]);
if(write(fd[1], "abcde", 5) == -1)
perror("write()");
printf("this is parent process!\n");
}
else if(pid == 0)
{
close(fd[1]);
char buf[6];
if(read(fd[0], buf, 5) == -1)
perror("read()");
buf[6] = 0;
printf("this is son process, parent says: %s\n", buf);
}
sleep(3);
}
3.使用UDP套接字完成如下服務(wù)器和客戶機(jī)的編程工作,客戶機(jī)發(fā)送一個(gè)整數(shù),UDP循環(huán)服務(wù)器將這個(gè)整數(shù)加上100,結(jié)果返回給客戶機(jī)
服務(wù)器:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SERVERPORT 3000
#define BUFFERSIZE 10
int main(int argc, char** argv)
{
int sockfd;
if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
perror("socket()");
struct sockaddr_in srvaddr, clientaddr;
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(SERVERPORT);
srvaddr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(sockfd, (struct sockaddr*)&srvaddr, sizeof(srvaddr)) == -1)
perror("bind()");
char buf[BUFFERSIZE];
socklen_t clientaddr_len = sizeof(clientaddr);
while(1)
{
recvfrom(sockfd, buf, BUFFERSIZE, 0, (struct sockaddr*)&clientaddr,&clientaddr_len);
sprintf(buf, "%d", atoi(buf) + 100);
sendto(sockfd, buf, BUFFERSIZE, 0, (struct sockaddr*)&clientaddr,clientaddr_len);
}
return0;
}
客戶端:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define SERVERPORT 3000
#define SERVERADDR "127.0.0.1"
#define BUFFERSIZE 10
int main(int argc, char** argv)
{
int sockfd;
if((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
perror("socket()");
struct sockaddr_in srvaddr;
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons(SERVERPORT);
if(inet_aton(SERVERADDR, &srvaddr.sin_addr) == -1)
perror("inet_aton()");
char buf[BUFFERSIZE];
sprintf(buf, "%s", argv[1]);
socklen_t srvaddr_len = sizeof(srvaddr);
sendto(sockfd, buf, sizeof(buf), BUFFERSIZE, (structsockaddr*)&srvaddr, sizeof(srvaddr));
recvfrom(sockfd, buf, sizeof(buf), 0, NULL, NULL);
printf("%d\n",atoi(buf));
}
4.采用預(yù)創(chuàng)建10個(gè)子進(jìn)程的方式,編程實(shí)現(xiàn)一個(gè)TCP并發(fā)服務(wù)器
服務(wù)器:
- #include <string.h>
- #include <sys/socket.h>
- #include <sys/types.h>
- #include <netinet/in.h>
- #include <stdio.h>
- #include <errno.h>
- #include <signal.h>
- #define SERVER_PORT 8080
- #define BACKLOG 5
- #define CLDNUM 10
- void theEnd(int n) //根據(jù)實(shí)際進(jìn)程數(shù)清除子進(jìn)程
- {
- int i;
- for(i=0; i< n; i ++)
- if(pid[i]>0)
- kill(pid[i],SIGTERM);
- while(wait(NULL)>0);
- }
- int pid[CLDNUM];
- int i;
- int main()
- {
- int listenfd,connfd;
- struct sockaddr_in servaddr;
- char cmd[10];
- int nErr=0;
- int buf_recv[1024];
- int buf_send[1024];
-
- listenfd=socket(AF_INET,SOCK_STREAM,0);
- if(listenfd<0)
- fprintf(stderr,"Socket error");
-
- bzero(&servaddr,sizeof(servaddr));
- servaddr.sin_family=AF_INET;
- servaddr.sin_addr.s_addr=htonl(INADDR_ANY);
- servaddr.sin_port=htons(SERVER_PORT);
-
- int n=1;
- setsockopt( listenfd,SOL_SOCKET,SO_REUSEADDR,&n,sizeof(n));
- if(bind(listenfd,(struct sockaddr*)&servaddr,sizeof(servaddr))<0)
- fprintf(stderr,"Bind error");
- if(listen(listenfd,BACKLOG)<0)
- fprintf(stderr,"Listen error");
- for(i=0; i < CLDNUM; i ++)
- {
- if((pid[i]=fork())==0)
- {
- for(;;) //預(yù)創(chuàng)建的子進(jìn)程
- {
- connfd=accept(listenfd,NULL,NULL);
- ......
- nbytes=read_all(connfd,buf_recv,256);
- process(......); //數(shù)據(jù)處理
- nbytes=write_all(connfd,buf_send,256);
- ......
- }
- }
- else if(pid[i]<0)
- {
- nErr=i+1; //從第i+1個(gè)進(jìn)程開始,創(chuàng)建失敗
- break;
- }
- }
- if(nErr!=CLDNUM) //創(chuàng)建子進(jìn)程組失敗
- {
- theEnd(nErr);
- exit(1);
- }
- int m=1;
- while(m) //鍵盤輸入,"theEnd",要求進(jìn)程結(jié)束
- {
- gets(cmd);
- m=strcmp(cmd,"theEnd");
- }
- theEnd(nErr);
- exit(0);
- }
復(fù)制代碼
5.編程產(chǎn)生子進(jìn)程,孫進(jìn)程,并使進(jìn)程按“孫-子-父”的順序結(jié)束
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- int main()
- {
- int son_pid, frandson_pid;
- if((son_pid = fork()) == 0)
- { //子進(jìn)程
- if((frandson_pid = fork()) == 0)
- { //孫進(jìn)程
- printf("this is frandson process!\n");
- exit(0);
- }
- else
- { //子進(jìn)程
- printf("this is son process!\n");
- wait();
- printf("frandson process exit!\n");
- exit(0);
- }
- }
- else
- { //父進(jìn)程
- printf("this is father process!\n");
- wait();
- printf("son process exit!\n");
- sleep(3);
- }
- return 0;
- }
復(fù)制代碼
|
|