写在前面:最级要开始写我们的项目了,是一个网络编程的项目,语言用C++,基于Socket通信,采用JSON数据交换格式,存数据用Mysql数据库。我们开始的困难是不知道如何处理客户端,组内都没有学写APP,如果是在终端下跑那就有太多限制了,感觉反而是加大了难度。
纠结了一会,突然想到能不能用PHP来写客户端,我学过一段时间PHP,目前还不太了解高阶的用法,前不久写了一个简单的订餐系统,用到一些特性,记得看到过PHP也可以进行Socket编程。在网上搜了一下,然后我写了一个简单的测试样例,客户端是用PHP,服务端用C++,互发送一条消息之后成功接收。
PHP的语言特性决定了它不适合做socket服务器端,socket主要面向底层和网络服务开发,一般用C或Java实现,能更好的处理并发、阻塞等。和C++或C写TCP客户端是一样的,工作过程可用下面的图表示:
PHP中和Socket相关函数
socket_accept() 接受一个Socket连接
socket_bind() 把socket绑定在一个IP地址和端口上
socket_clear_error() 清除socket的错误或者最后的错误代码
socket_close() 关闭一个socket资源
socket_connect() 开始一个socket连接
socket_create_listen() 在指定端口打开一个socket监听
socket_create_pair() 产生一对没有区别的socket到一个数组里
socket_create() 产生一个socket,相当于产生一个socket的数据结构
socket_get_option() 获取socket选项
socket_getpeername() 获取远程类似主机的ip地址
socket_getsockname() 获取本地socket的ip地址
socket_iovec_add() 添加一个新的向量到一个分散/聚合的数组
socket_iovec_alloc() 这个函数创建一个能够发送接收读写的iovec数据结构
socket_iovec_delete() 删除一个已经分配的iovec
socket_iovec_fetch() 返回指定的iovec资源的数据
socket_iovec_free() 释放一个iovec资源
socket_iovec_set() 设置iovec的数据新值
socket_last_error() 获取当前socket的最后错误代码
socket_listen() 监听由指定socket的所有连接
socket_read() 读取指定长度的数据
socket_readv() 读取从分散/聚合数组过来的数据
socket_recv() 从socket里接收数据到缓存
socket_recvfrom() 接受数据从指定的socket,如果没有指定则默认当前socket
socket_recvmsg() 从iovec里接受消息
socket_select() 多路选择
socket_send() 这个函数发送数据到已连接的socket
socket_sendmsg() 发送消息到socket
socket_sendto() 发送消息到指定地址的socket
socket_set_block() 在socket里设置为块模式
socket_set_nonblock() socket里设置为非块模式
socket_set_option() 设置socket选项
socket_shutdown() 这个函数允许你关闭读、写、或者指定的socket
socket_strerror() 返回指定错误号的详细错误
socket_write() 写数据到socket缓存
socket_writev() 写数据到分散/聚合数组
PHP客户端
设置变量,”主机”和”端口”
$host = "127.0.0.1"; $port = 9999; // 设置超时时间,保证连接不会超时 set_time_limit(0);
创建socket
$socket = socket_create(AF_INET, SOCK_STREAM, 0 ) or die("Could not create socket!\n"); /* 三个参数: 1.创建socket用的通信协议族,AF_INET是基于IPv4的Internet协议 2.指定服务类型对TCP/IP来说,SOCK_STREAM表示传输层使用TCP协议 3.指定socket使用哪种具体的协议 */
连接到服务端
$result = socket_connect($socket, $host, $port) or die("Could not connect to server\n");
向服务端写数据
socket_write($socket, $message, strlen($message)) or die("Could not send data to server\n"); /* 向socket中写入指定大小的缓冲数据,三个参数,返回写入的字节数 1.有效的socket句柄 2.要写入的数据 3.可选,写入socket数据的字节数 */
从服务端接收数据
$result = socket_read($socket,1024) or die("Could not read server response\n"); /* 从socket中读取指定字节的数据,三个参数,返回读取到的字符串数据 1.有效的socket句柄 2.指定读取的字节长度 3.可选,默认为PHP_BINARY_READ,安全读取二进制数据 */
关闭socket
socket_close($socket);
简单的测试样例
一个小样例,客户端用PHP,服务器端用C++,数据传递采用json格式,用jsoncpp。
server
/*server.cpp*/
/*************************************************************************
> File Name: server.cpp
> Author: Tanswer_
> Mail: duxm@xiyoulinux.org
> Created Time:
************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <assert.h>
#include <errno.h>
#include <signal.h>
#include <json/json.h>
using namespace std;
#define BUF_SIZE 1024
int main(int argc, char*argv[])
{
if(argc <= 2)
{
printf("usage: %s ip_address port \n",basename(argv[0]));
return -1;
}
const char* ip = argv[1];
int port = atoi(argv[2]);
struct sockaddr_in address;
bzero(&address,sizeof(address));
address.sin_family = AF_INET;
inet_pton( AF_INET, ip, &address.sin_addr );
address.sin_port = htons( port );
int sockfd = socket(PF_INET, SOCK_STREAM, 0);
assert(sockfd >= 0);
int ret = bind(sockfd, (struct sockaddr*)&address, sizeof(address));
assert(ret != -1);
ret = listen(sockfd,5);
assert(ret != -1);
struct sockaddr_in client;
socklen_t len = sizeof(client);
int connfd = accept(sockfd,(struct sockaddr*)&client,&len);
if(connfd < 0)
{
printf("errno is : %d\n",errno);
}
else
{
/*接收数据*/
char buffer[BUF_SIZE];
memset(buffer,'\0',BUF_SIZE);
ret = recv(connfd, buffer, BUF_SIZE-1, 0);
printf("got %d bytes of normal data %s\n",ret,buffer);
/*将json串解析*/
Json::Value root;
Json::Reader reader;
if(reader.parse(buffer,root))
{
cout << "name: " << root["name"].asString() << endl;
cout << "hobbies: " << root["hobbies"].asString() << endl;
cout << "birthdate: " << root["birthdate"].asString() << endl;
}
/*发送给客户端*/
const char* data = "注册成功!";
send(connfd,data,strlen(data),0);
/*关闭*/
close(connfd);
}
close(sockfd);
return 0;
}
client
<html>
<head>
<meta charset="utf-8">
<title>test</title>
</head>
<body>
<form action="" method="post">
姓名:<input type="text" name="name"><br >
爱好:<input type="text" name="hobbies"><br >
生日:<input type="text" name="birthdate"><br >
<input type="submit" value="提交" name="submit">
</form>
</body>
</html>
<?php
if(isset($_POST['submit']))
{
$host = "127.0.0.1";
$port = 8888;
// create socket
$socket = socket_create(AF_INET, SOCK_STREAM, 0) or die("Could not create socket\n");
// connect to server
$result = socket_connect($socket, $host, $port) or die("Could not connect to server\n");
class Emp{
public $name = "";
public $hobbies = "";
public $birthdate = "";
}
$e = new Emp();
$e->name = $_POST["name"];
$e->hobbies = $_POST["hobbies"];
$e->birthdate = $_POST["birthdate"];
$message = json_encode($e);
#打印一些信息
echo $message; #json串
echo '<br >';
echo strlen($message);#json长度
echo '<br >';
// send string to server
socket_write($socket, $message, strlen($message)) or die("Could not send data to server\n");
// get server response
$result = socket_read ($socket, 1024) or die("Could not read server response\n");
echo "\tReply From Server : ".$result;
// close socket
socket_close($socket);
}
?>
结果
编译运行server.cpp
,开启服务端,等待连接
将client.php放到/home/wwwroot/default下,本地已配好 lnmp环境,在浏览器输入 localhost/client.php
,可以看到自己写的页面,
然后填好表单内容点击提交,结果如下:
感觉是可行的,下面构思项目啦。