服务端测试
01-server.c 02-client.c
编译
gcc -o server 01-server.c
运行
./server
堵塞在accept
新的终端测试工具
nc 127.1 8888
输入
客户端测试
编译
gcc -o client 02-server.c
运行
./client
测试过程中可以使用netstat命令查看监听状态和连接状态
netstat命令:
a 表示显示所有
n 表示显示的时候以数字的方式来显示
p 表示显示进程信息(进程名和进程PID)
lgw@lgw-Lenovo-XiaoXin-Air-13-Pro:~$ netstat -anp | grep 8888
(并非所有进程都能被检测到,所有非本用户的进程信息将不会显示,如果想看到所有信息,则必须切换到 root 用户)
tcp 0 0 0.0.0.0:8888 0.0.0.0:* LISTEN 2757/server
tcp 0 0 127.0.0.1:8888 127.0.0.1:43432 ESTABLISHED 2757/server
tcp 0 0 127.0.0.1:43432 127.0.0.1:8888 ESTABLISHED 2797/client
一些细节
1、调用accept函数据不是说新建一个连接,而是从已连接队列中取出一个可用连接。
函数封装思想
像accept和read这样能够引起阻塞的函数,若被信号打断,由于信号的优先级较高,会优先处理信号,信号处理完成后,会accept或者read解除阻塞,然后返回,此时返回值为-1,设置errno=EINTR
errno=ECONNABORTED 表示连接被打断,异常了。
errno宏
在/usr/include/asm-generic/errno.h 文件中包含了errno所有的宏和对应的错误描述信息
命令 man errno 可以查看帮助文件
多进程并发服务器
解决办法1
将cfd设置为非阻塞:fcntl
假如有多个客户端连接请求,cfd只会保留最后一个文件描述符的值
解决方法2
使用多进程:让父进程监听接收新的连接,子进程处理新的连接(接收和发送数据),父进程还负责回收子进程
处理流程
1、创建socket,得到一个监听的文件描述符lfd–socket()
2、将lfd和IP和端口port进行绑定—bind()
3、设置监听—listen()
4、进入while(1)
{
//等待有新的客户端连接到来
cfd = accept();
//fork一个子进程,让子进程去处理数据
pid = fork();
if(pid<0)
{
exit(-1)
}
else if (pid>0)
{
//关闭通信文件描述符cfd
close(cfd)
}
else if (pid==0)
{
//关闭监听文件描述符
close(lfd)
//收发数据
while(1)
{
//读数据
n = read(cfd,buf,sizeof(buf));
if(n<=0)
{
break;
}
//发送数据给对方
write(cfd,buf,n)
}
close(cfd);
//下面的exit必须有,防止子进程再去创建子进程
exit(0);
}
}
注意点:accept或者read函数是阻塞函数,会被信号打断,此时不应该视为一个错误
创建子进程
知识改变命运