PHP socket编程+防火墙解决Connection refused+tcpdump分析网络包

代码实现

服务端:socket_server.php

<?php
    //创建服务端的socket套接流,net协议为IPv4,protocol协议为TCP
    $socket = socket_create(AF_INET,SOCK_STREAM,SOL_TCP);    
    
    //绑定接收的套接流主机和端口,与客户端相对应
    if(socket_bind($socket,'127.0.0.1',8888) == false){        
        echo 'server bind fail:'.socket_strerror(socket_last_error());
    }   

     //监听套接流
    if(socket_listen($socket,4)==false){       
        echo 'server listen fail:'.socket_strerror(socket_last_error());
    }
    
    //让服务器无限监听获取客户端传过来的信息
    do{    

        //socket_accept的作用就是接受socket_bind()所绑定的主机发过来的套接流
        $accept_resource = socket_accept($socket);    
 

        if($accept_resource !== false){        
            //读取客户端传过来的资源,并转化为字符串
            //socket_read的作用就是读出socket_accept()的资源并把它转化为字符串
            $string = socket_read($accept_resource,1024);        
            
            echo 'server receive is :'.$string.PHP_EOL;

            if($string != false){            
                $return_client = $string. ' too' . PHP_EOL;           
                //向socket_accept的套接流写入信息,也就是回馈信息给socket_bind()所绑定的主机客户端
                //socket_write的作用是向socket_create的套接流写入信息,或者向socket_accept的套接流写入信息
                socket_write($accept_resource,$return_client,strlen($return_client));            
            }else{            
                echo 'socket_read is fail';
            }    
            
            //socket_close的作用是关闭socket_create()或者socket_accept()所建立的套接流
            socket_close($accept_resource);

        }

}while(true);

socket_close($socket);

客户端:socket_client.php

<?php    
    //创建一个socket套接流
    $socket = socket_create(AF_INET,SOCK_STREAM,SOL_TCP);  

    //接收套接流的最大超时时间1秒,后面是微秒单位超时时间,设置为零,表示不管它
    socket_set_option($socket, SOL_SOCKET, SO_RCVTIMEO, array("sec" => 1, "usec" => 0));     
    //发送套接流的最大超时时间为6秒
    socket_set_option($socket, SOL_SOCKET, SO_SNDTIMEO, array("sec" => 6, "usec" => 0));  
 
    //连接服务端的套接流,这一步就是使客户端与服务器端的套接流建立联系
    if(socket_connect($socket,'127.0.0.1',8888) == false){        
        echo 'connect fail massege:'.socket_strerror(socket_last_error());
    }else{        
        $message = 'l love you socket';        
        //转为GBK编码,处理乱码问题,这要看你的编码情况而定,每个人的编码都不同
        $message = mb_convert_encoding($message,'GBK','UTF-8');       
        
        //向服务端写入字符串信息
        if(socket_write($socket,$message,strlen($message)) == false){            
            echo 'fail to write'.socket_strerror(socket_last_error());
        }else{            
            echo 'client write success'.PHP_EOL;            
            echo 'send message is:' . $message . PHP_EOL;

            //读取服务端返回来的套接流信息
            while($return_message = socket_read($socket,1024)){                
                echo 'server return message is:' . $return_message . PHP_EOL;
            }

        }

    }

    socket_close($socket);//工作完毕,关闭套接流

服务端:

[root@www html]# php socket_server.php

server receive is :l love you socket

客户端:

localhost:php why$ php socket_client.php
client write success
send message is:l love you socket
server return message is:l love you socket too

localhost:php why$

 

问题

上面的代码都运行在本地,任何问题都没有,但是如果服务端和客户端是在不同的主机上,会出现如下问题:

PHP Warning:  socket_connect(): unable to connect [61]: Connection refused in /Users/why/Desktop/php/socket_client.php on line 11
connect fail massege:Connection refused

很明显,是server端拒绝连接,猜测是服务端防火墙问题,应该是没有开放8888端口

[root@www ~]# firewall-cmd --zone=public --list-ports
80/tcp

通过firewall-cmd命令可以看到只开放了80端口,所以第一步就是开放8888端口

firewall-cmd --zone=public --add-port=8888/tcp --permanent
success

此时如果重新开启防火墙,那么所有的主机都可以访问本机的8888端口了,而假如我们当前端口提供的服务仅允许某台机器使用,所以需要设置仅允许指定的机器连接

[root@www ~]# firewall-cmd --zone=public --remove-port=8888/tcp --permanent
success
[root@www ~]# firewall-cmd --permanent --add-rich-rule="rule family="ipv4" source address="10.211.55.2" port protocol="tcp" port="8888" accept"
success
[root@www ~]# firewall-cmd --reload
success
[root@www ~]#                       

此时再运行客户端机器上的脚本发现运行成功:

localhost:php why$ php socket_client.php
client write success
send message is:l love you socket
server return message is:l love you socket too

localhost:php why$

同时通过tcpdump抓包查看数据:

[root@www ~]# tcpdump tcp port 8888  

21:40:52.910291 IP localhost.61008 > www.weihaoyu.com.ddi-tcp-1: Flags [SEW], seq 1721959505, win 65535, options [mss 1460,nop,wscale 6,nop,nop,TS val 1131017848 ecr 0,sackOK,eol], length 0
21:40:52.910364 IP www.weihaoyu.com.ddi-tcp-1 > localhost.61008: Flags [S.E], seq 893885278, ack 1721959506, win 28960, options [mss 1460,sackOK,TS val 348747313 ecr 1131017848,nop,wscale 7], length 0
21:40:52.910489 IP localhost.61008 > www.weihaoyu.com.ddi-tcp-1: Flags [.], ack 1, win 2058, options [nop,nop,TS val 1131017848 ecr 348747313], length 0
21:40:52.910542 IP localhost.61008 > www.weihaoyu.com.ddi-tcp-1: Flags [P.], seq 1:18, ack 1, win 2058, options [nop,nop,TS val 1131017848 ecr 348747313], length 17
21:40:52.910567 IP www.weihaoyu.com.ddi-tcp-1 > localhost.61008: Flags [.], ack 18, win 227, options [nop,nop,TS val 348747313ecr 1131017848], length 0
21:40:52.910654 IP www.weihaoyu.com.ddi-tcp-1 > localhost.61008: Flags [P.], seq 1:23, ack 18, win 227, options [nop,nop,TS val 348747314 ecr 1131017848], length 22
21:40:52.910678 IP www.weihaoyu.com.ddi-tcp-1 > localhost.61008: Flags [F.], seq 23, ack 18, win 227, options [nop,nop,TS val 348747314 ecr 1131017848], length 0
21:40:52.910957 IP localhost.61008 > www.weihaoyu.com.ddi-tcp-1: Flags [.], ack 23, win 2058, options [nop,nop,TS val 1131017848 ecr 348747314], length 0
21:40:52.910985 IP localhost.61008 > www.weihaoyu.com.ddi-tcp-1: Flags [.], ack 24, win 2058, options [nop,nop,TS val 1131017848 ecr 348747314], length 0
21:40:52.911013 IP localhost.61008 > www.weihaoyu.com.ddi-tcp-1: Flags [F.], seq 18, ack 24, win 2058, options [nop,nop,TS val1131017848 ecr 348747314], length 0
21:40:52.911034 IP www.weihaoyu.com.ddi-tcp-1 > localhost.61008: Flags [.], ack 19, win 227, options [nop,nop,TS val 348747314ecr 1131017848], length 0

可以看到完整的三次握手和四次挥手的过程,其中[SEW]为连接请求报文、[S.E]为确认连接报文、[.]为确认报文、[P.]为数据报文、[F.]为释放报文,从上到下解释如下:

  1. 客户端seq为1721959505,标志SEW
  2. 服务端收到请求后同意连接,向客户端发送确认,ack=seq+1=1721959506
  3. 客户端收到服务端的确认后,向服务端给出去人,确认报文段ack为1
  4. 客户端向服务端发送数据,长度为17,刚好等于 'I love you socket'的长度
  5. 服务端回复确认收到数据ack=seq=18
  6. 服务端向客户端传输数据,长度为22(这里我认为应该刚好等于'I love you socket too'的长度,但是长度却多了1,这里不太明白,评论区请教大佬)
  7. 服务器发送断开连接请求,停止发送数据,初始化seq为23
  8. 客户端回复确认收到数据(dump打印顺序问题),发送确认ack=seq=23
  9. 客户端收到断开连接请求,向服务端发送确认,ack=seq+1=24(这个步骤客户端仍然处于timewait状态,等待2个时间周期,服务端在这两个时间周期内如果没收到客户端的确认,会再次发送断开连接的请求报文,直到完整收到确认后才会断开连接,这样做的目的是为了确保服务端关闭连接,避免持续浪费服务端资源)
  10. 客户端发送断开连接请求,停止发送数据,初始化seq=18
  11. 服务端收到断开连接请求,向客户端发送确认ack=seq+1=19

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AirGo.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值