最近了解了下下socket方面的东西,想做一个socket上传文件的例子。
在网上搜了搜代码执行后,图片数据传输了一半,图片的下半部分是灰色的。然后就自己仿着搜来的代码和php.net 中socket方法的介绍写了一个可以完整上传文件的代码。这里以图片为例。
client.php
/** * client.php */ define("token", "1k8o3u4h5t6uv8a53uz4210mz"); # 通过appkey 与appsecret动态生成上传凭证, $filePath = "C:\Users\Administrator\Desktop\socketIMg\"; $fileName = "chorme.png"; $fp = fsockopen("127.0.0.1", 1688, $errno, $errstr, 30); if (is_resource($fp)) { $fileSize = filesize($filePath . $fileName); fwrite($fp, json_encode(['filename' => $fileName, 'filesize' => $fileSize, 'token' => token])); # 发送文件名、上传凭证、文件大小 $out = file_get_contents($filePath . $fileName); $receiveFileSize = fwrite($fp, $out); # 发送文件if ($receiveFileSize == $fileSize) { echo 'send success'; } else { echo 'data uncomplete!'; } // while (!feof($fp)) { // echo fgets($fp, 128); // } fclose($fp); }
server.php
/** * server.php */ define("token", "1k8o3u4h5t6uv8a53uz4210mz"); # 约定好的token凭证,通过appkey动态生成,这里演示固定值 $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); socket_bind($socket, '127.0.0.1', 1688); $listening = socket_listen($socket); if ($listening) { echo "status OK "; } else { echo "fail"; exit(); } while (true) { $connection = socket_accept($socket); if ($connection) { $buffer = ''; $begin = TRUE; $fileSize = 0; # 接收的数据大小 $sendFilesize = 0; # 发送文件自带文件大小 while (socket_recv($connection, $buffer, 1024, 0)) { # 一直保持连接且接受数据,1024字节大小为一个接收包 if ($begin) { # 客户端发送文件名, $fileInfo = json_decode($buffer, TRUE); if ($fileInfo['token'] != token) { echo "token valid false ! "; break; } $file_name = $fileInfo["filename"]; $sendFilesize = $fileInfo["filesize"]; $fp = fopen($file_name, "wb"); //写入方式+二进制模式写入 if (!$fp) { echo "Create file error! "; break; } $begin = FALSE; continue; } #客户端发送 文件数据,fwrite 写入数据 $fileSize+=strlen($buffer); fwrite($fp, $buffer); } # 发送大小与接收大小相同=保存成功 if ($sendFilesize != 0 && $fileSize == $sendFilesize) { echo "receive success! "; } else { echo "receive fail! "; } #socket_write($connection, "aaa", strlen("aaa")); isset($fp) && fclose($fp); socket_close($connection); } } socket_close($socket);
cmder 切换到server.php目录路径
在看s1根目录下文件:
问题:
上传文件成功后,服务端应该给客户端返回一个响应,通过socket_write发送。客户端通过fgets接收,但是打开代码中注释的:
socket_write($connection, "aaa", strlen("aaa"));
while (!feof($fp)) { echo fgets($fp, 128); }
会直接导致程序堵塞,暂未解决fgets堵塞发方法。
昨天又看了个workerman PHP socket 服务器框架 的确很“socket” ,不用手写了,用到socket功能的鞋童建议直接使用这个框架。
在workerman给的手册中也有上传文件的例子,在这里我把文件搬过来。
链接: http://doc3.workerman.net/protocols/example.html
新建协议查看文档说明,目录结果如下:
跟目录放一个漂亮的美女图片,新增一个tmp目录
我们先用二进制方法上传文件
打开cmder运行 :php upimgserBinary.php启动服务
cmder 运行uploadclientBinary.php +文件001.jpg
提示上传成功,查看tmp目录是否有图片
相同的方法执行text协议上传。
代码如下:
upimgserBinary.php
use WorkermanWorker; require_once dirname(__DIR__) . '/Workerman/Autoloader.php'; $worker = new Worker('BinaryTransfer://0.0.0.0:8333'); // 保存文件到tmp下 $worker->onMessage = function($connection, $data) { $save_path = 'tmp/'.$data['file_name']; file_put_contents($save_path, $data['file_data']); $connection->send("upload success. save path $save_path"); }; Worker::runAll();
uploadclientBinary.php
/** 上传文件客户端 **/ // 上传地址 $address = "127.0.0.1:8333"; // 检查上传文件路径参数 if(!isset($argv[1])) { exit("use php client.php $file_path "); } // 上传文件路径 $file_to_transfer = trim($argv[1]); // 上传的文件本地不存在 if(!is_file($file_to_transfer)) { exit("$file_to_transfer not exist "); } // 建立socket连接 $client = stream_socket_client($address, $errno, $errmsg); if(!$client) { exit("$errmsg "); } // 设置成阻塞 stream_set_blocking($client, 1); // 文件名 $file_name = basename($file_to_transfer); // 文件名长度 $name_len = strlen($file_name); // 文件二进制数据 $file_data = file_get_contents($file_to_transfer); // 协议头长度 4字节包长+1字节文件名长度 $PACKAGE_HEAD_LEN = 5; // 协议包 $package = pack('NC', $PACKAGE_HEAD_LEN + strlen($file_name) + strlen($file_data), $name_len) . $file_name . $file_data; // 执行上传 fwrite($client, $package); // 打印结果 echo fread($client, 8192)," ";
------------------------
upimgserText.php
use WorkermanWorker; require_once dirname(__DIR__) . '/Workerman/Autoloader.php'; $worker = new Worker('TextTransfer://0.0.0.0:8333'); // 保存文件到tmp下 $worker->onMessage = function($connection, $data) { $save_path = 'tmp/'.$data['file_name']; file_put_contents($save_path, $data['file_data']); $connection->send("upload success. save path $save_path"); }; Worker::runAll();
uploadclientText.php
/** 上传文件客户端 **/ // 上传地址 $address = "127.0.0.1:8333"; // 检查上传文件路径参数 if(!isset($argv[1])) { exit("use php client.php $file_path "); } // 上传文件路径 $file_to_transfer = trim($argv[1]); // 上传的文件本地不存在 if(!is_file($file_to_transfer)) { exit("$file_to_transfer not exist "); } // 建立socket连接 $client = stream_socket_client($address, $errno, $errmsg); if(!$client) { exit("$errmsg "); } stream_set_blocking($client, 1); // 文件名 $file_name = basename($file_to_transfer); // 文件二进制数据 $file_data = file_get_contents($file_to_transfer); // base64编码 $file_data = base64_encode($file_data); // 数据包 $package_data = array( 'file_name' => $file_name, 'file_data' => $file_data, ); // 协议包 json+回车 $package = json_encode($package_data)." "; // 执行上传 fwrite($client, $package); // 打印结果 echo fread($client, 8192)," ";
打包的7zip附件文件:
http://files.cnblogs.com/files/dcb3688/workerman-upload-file.7z