|
简答题 1.采用阻塞I/O模型时,套接字函数read()的返回值有哪几种?分别对应什么情况?
返回值n | 对应情况 | 无数据
| 阻塞
| n >= len
| 读取len个字节
| 0 < n < len
| 读取n个字节
| n = 0
| 读通道关闭
| n = -1 且 ERRON = EINTR
| 读中断引起错误
| n = -1 且 ERRON = ECONNREST
| 网络连接有问题
|
2.为什么客户机通常不需要绑定自己的端口号?
当客户机请求连接时,系统会自动为它选择一个未用的端口号,并且用本地的IP地址设置嵌套字地址的相应项。
3.close()函数和shutdown()函数有何差别?
①shutdown操作连接通道,其他进程不能再使用已被关闭的通道;close操作描述符,其他进程仍然可以使用该socket描述符
②close关闭应用程序与socket的接口,调用close之后进程不能再读写这个socket;shudown可以只关闭一个通道,另一个通道仍然可以操作
4.僵尸进程有什么危害?清除僵尸进程的方法有哪些?(至少4种)
僵尸进程虽然对其他进程几乎没有什么影响,不占用CPU时间,消耗的内存也几乎可以忽略不计,但Linux系统中进程数目是有限的,在一些特殊的情况下,如果存在太多的僵尸进程,会影响到新进程的产生。
清除方法:
①忽略SIGCHLD信号,系统将清除子进程的进程表项,这种方法依赖于Linux版本的实现
②调用函数wait()或waitpid()等待子进程。这种方法没有兼容性问题,但主程序进入等待循环后不能做任何事情
③捕获SIGCHLD信号,在相应的处理函数中调用wait()或waitpid()对子进程进行处理
④调用fork()两次,使子进程成为孤儿进程,由init进程管理
5.Linux系统默认的I/O模型是什么?请给出两种将阻塞式模型转换为非阻塞式模型的方法
默认的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.函数fork()与exec()的差别
①fork()是执行一个新的任务,exec()执行另一个程序
②fork()的子进程共享正文,exec()将改变子进程的正文
③fork()在创建完进程后立即返回,exec执行时函数不返回,只有错误时才返回
7.创建一个守护进程有哪些步骤
①调用fork(),父进程退出,子进程继续运行
②调用setsid()创建新的session
③忽略信号SIGHUP,再次调用fork(),父进程(session的头进程)退出
④调用chdir("/"),使进程不使用任何目录
⑤调用unmask(0),使进程对任何内容都有写权限
⑥关闭所有打开的文件描述符,为标准输入、标准输出、标准错误输出打开新的文件描述符0、1、2
8.列举出不少于5项的进程间通信方式
①管道②信号量③消息队列④消息邮箱⑤共享内存⑥内存映像文件⑦Unix域Socket
9.请给出利用Socket接口实现面向连接的网络编程模型
在51黑附件中
10.网络编程时,为什么要考虑字节顺序问题
不同机器表示数据的字节顺序是不同的:
Intel处理器:低字节在前,高字节在后,称为小端存储
Motorola处理器:高字节在前,低字节在后,称为大端存储
11.在使用UDP套接字时也可以使用connnect()函数,连接UDP套接字有哪些特点?
使得在无连接的UDP通信中,发送时不必带目的地址,接收时不必带源地址
12.服务器经常使用说明绑定方式,为什么
常使用绑定任意地址方式(INADDR_ANY)
这样服务器可以接收发送到服务器的任意IP地址的数据
13.试比较pipe和Unix域套接字的差异
Pipe是单工通信,Unix域套接字是双工通信
编程题 1.请给出实现一个守护进程的程序代码
#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;
/*
步骤一:调用fork(),父进程退出,子进程继续
*/
if(pid = fork())
exit(0); //父进程直接退出
else if(pid < 0)
exit(1); //fork()失败退出
/*
步骤二:调用setsid()创建新的会话
*/
//第一子进程
setsid(); //第一子进程成为新的会话组组长,并与控制台终端分离
/*
步骤三:再次调用fork(),会话组组长退出
*/
if(pid = fork())
exit(0); //是第一子进程,退出
else if(pid < 0)
exit(1); //fork()失败退出
//第二子进程(孙进程),不再是会话组组长
for(i = 0; i < NOFILE; i++)
close(i); //关闭打开的文件描述符
/*
步骤四:释放挂载的文件系统
*/
chdir("\n"); //改变当前目录到系统目录下,释放挂载的文件系统
/*
步骤五:提升进程权限
*/
umask(0); //使守护进程拥有高权限
}
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.编程产生2个进程:子进程1和子进程2,然后利用管道,将字符串“abcde”从子进程1传到子进程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套接字完成如下服务器和客户机的编程工作,客户机发送一个整数,UDP循环服务器将这个整数加上100,结果返回给客户机
服务器:
#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.采用预创建10个子进程的方式,编程实现一个TCP并发服务器
服务器:
- #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) //根据实际进程数清除子进程
- {
- 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(;;) //预创建的子进程
- {
- connfd=accept(listenfd,NULL,NULL);
- ......
- nbytes=read_all(connfd,buf_recv,256);
- process(......); //数据处理
- nbytes=write_all(connfd,buf_send,256);
- ......
- }
- }
- else if(pid[i]<0)
- {
- nErr=i+1; //从第i+1个进程开始,创建失败
- break;
- }
- }
- if(nErr!=CLDNUM) //创建子进程组失败
- {
- theEnd(nErr);
- exit(1);
- }
- int m=1;
- while(m) //键盘输入,"theEnd",要求进程结束
- {
- gets(cmd);
- m=strcmp(cmd,"theEnd");
- }
- theEnd(nErr);
- exit(0);
- }
复制代码
5.编程产生子进程,孙进程,并使进程按“孙-子-父”的顺序结束
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- int main()
- {
- int son_pid, frandson_pid;
- if((son_pid = fork()) == 0)
- { //子进程
- if((frandson_pid = fork()) == 0)
- { //孙进程
- printf("this is frandson process!\n");
- exit(0);
- }
- else
- { //子进程
- printf("this is son process!\n");
- wait();
- printf("frandson process exit!\n");
- exit(0);
- }
- }
- else
- { //父进程
- printf("this is father process!\n");
- wait();
- printf("son process exit!\n");
- sleep(3);
- }
- return 0;
- }
复制代码
|
|