1. 在服务器端的do_service下增加这样一段代码
size_t len;
char buff[20];
if((len = read(fd, buff, 20)) < 0){
perror("read error");//先读取来源于客户端信息,IO都是阻塞读写,如果客户端没有写操作,这里会被阻塞。
}
重新编译运行会发现,客户端收不到服务器write返回的系统时间,服务端阻塞在read上。
一个客户端出问题,整个服务器都会停下来,这样的代码是低效的。
服务器端并发性处理分为三种:多进程模型、多线程模型、多路转换IO模型。
2.多进程模型
管道的读写特性 1.通过打开两个管道来创建一个双向的管道; 2.管道默认是阻塞性的,当进程从管道中读取数据,若没有数据进程会阻塞; 3.当一个进程往管道中不断地写入数据但是没有进程去读取数据,此时只要管道没有满是可以写的,但若管道放满数据的则会报错;
不完整管道:1.read端关闭,write写数据时,会产生信号SIGPIPE,write返回-1,erno=EPIPE;2.write端关闭,read读数据时,read返回0,表示已经读到文件末尾。
【服务端采用多进程模型】
echo_tcp_client.c:
void do_service(int fd)
{
//和客户端进行读写操作(双向通信)
char buff[512];
while(1){
memset(buff, 0, sizeof(buff));
printf("start read and write...\n");
size_t size;
if((size = read_msg(fd, buff, sizeof(buff))) < 0){//这里采用了自定义读取信息的协议,需要包括“msg.h”头文件
perror("protocal error");
break;
}
else if(size == 0){//如果客户端read已经关闭,跳出进程
break;
}
else{
printf("%s\n", buff);
if(write_msg(fd, buff, sizeof(buff)) < 0){//如果客户端write已经关闭,产生EPIPE,跳出进程
if(errno == EPIPE){
break;
}
perror("protocal error");
}
}
}
}
main(){
.......
while(1){
int fd = accept(sockfd, (struct sockaddr*)&clientaddr, &clientaddr_len);
if(fd < 0) {
perror("accept error");
continue;
}
/*step 5 IO function: read/write 启动子进程去调用IO函数*/
pid_t pid = fork();
if(pid < 0){
continue;
}
else if(pid == 0){ //child process
out_addr(&clientaddr);
do_service(fd);
close(fd);
break;
}
else{ //parent process
close(fd);
}
.....
}
通过fork进行系统调用,产生一个与原进程完全相同的进程,复制了包括fd在内的进程作为子进程。所以我们在父进程中,对原先的fd进行关闭操作即可。
pid_t类型的pid表示fork子进程的返回值。在父进程中,返回值是新创建的子进程的进程ID;子进程中,返回0;错误返回一个负值。进程创建错误的原因有以下两种:1)当前的进程数已经达到了系统规定的上限,这时errno的值被设置为EAGAIN。 2)系统内存不足,这时errno的值被设置为ENOMEM。
【客户端从标准输入流中获取要发送的信息】
/*step 3 IO function: read/write 双向通信*/
char buff[512];
size_t size;
char *prompt = ">";//提示符
while(1){
memset(buff, 0, sizeof(buff));
write(STDOUT_FILENO, prompt, 1);//先在客户端终端输出一个 > 提示符
size = read(STDIN_FILENO, buff, sizeof(buff));
if(size < 0) continue;
buff[size - 1] = '\0';
if(write_msg(sockfd, buff, sizeof(buff)) < 0){
perror("write msg error");
continue;
}
else{
if(read_msg(sockfd, buff, sizeof(buff)) < 0){
perror("read msg error");
continue;
}
else{
printf("%s\n", buff);
}
}
}
客户端向服务端发送一个message,然后服务端返回一个一样的信息输出在客户端界面
结果如下:
3. shell中的echo命令
echo:用于字符串输出,在显示器上显示echo后面跟的字符串or执行结果
$# 表示参数个数
$0 是脚本本身的名字
$1 是传递给该shell脚本的第一个参数
$2 是传递给该shell脚本的第二个参数
$@ 表示所有参数,并且所有参数都是独立的
$$ 是脚本运行的当前进程ID号
$? 是显示最后命令的退出状态,0表示没有错误,其他表示有错误
$SHELL 返回的是/bin/bash
4. 程序报错缺少头文件解决办法:man wait(需要头文件的函数名)就能知道需要包含什么头文件了