# 创建TCP服务器

建议先阅读[官方文档-创建TCP服务器](https://wiki.swoole.com/wiki/page/476.html)章节.

* 一、 Swoole-server介绍
* 二、 创建一个Tcp server
* 三、 swoole驱动模式及相应事件
* 四、server跟client交互
* 五、同步client跟异步client
* 六、tcp的特点及粘包处理

## 一、 Swoole-server介绍

server，顾名思义，就是服务端。平时接触比较多的无非就是nginx和apache。作为webServer，二者都是通过监听某端口对外提供服务,swoole的server也不例外同样需要绑定端口,同时能够提供给客户端相关的服务。

**创建server的步骤:**

* 1、实例化Server对象
* 2、设置运行时参数
* 3、注册事件回调函数
* 4、启动服务器

示例:

```php
<?php

//创建Server对象，监听 0.0.0.0:9501端口
$serv = new swoole_server("0.0.0.0", 9501,SWOOLE_PROCESS,SWOOLE_SOCK_TCP);

//配置
$serv->set([
    'worker_num' => 2, //设置进程
]);

//监听连接进入事件,有客户端连接进来的时候会触发
$serv->on('connect', function ($serv, $fd) {
     echo "有新的客户端连接，连接标识为$fd" . PHP_EOL;
});

//监听数据接收事件,server接收到客户端的数据后，worker进程内触发该回调
$serv->on('receive', function ($serv, $fd, $from_id, $data) {
       var_dump($data);
       $serv->send($fd, "服务器给你发送消息了: ".$data);
});

//监听连接关闭事件,客服端关闭，或者服务器主动关闭
$serv->on('close', function ($serv, $fd) {
    echo "编号为{$fd}的客户端已经关闭.".PHP_EOL;
});

//启动服务器
$serv->start();
```

代码解析:

**1、实例化Server对象**

server的创建，只需要绑定要监听的ip和端口，如果ip指定为127.0.0.1，则表示客户端只能**位于本机**才能连接，其他计算机无法连接，如果需要所有的客户端都能连接则可以设置0.0.0.0.

```php
//创建Server对象，监听 0.0.0.0:9501端口
$serv = new swoole_server("0.0.0.0", 9501,SWOOLE_PROCESS,SWOOLE_SOCK_TCP);
```

端口这里指定为9501，可以通过netstat查看下该端口是否被占用。如果该端口被占用，可更改为其他端口，如9502，9503等。

**2、设置运行时参数**

在启动service之前,同样也可以进行一些参数来配置一下server，比如调几个人来提供服务（几个进程），以及是否是后台执行（守护进程）等等一些其它的配置。

首要说明一下worker进程数的配置，因为swoole是多进程的异步服务器所以需要设置工作进程数，提升服务器性能。

```php
$serv->set([
    'worker_num' => 2, //设置进程
]);
```

配置项worker\_num等于某个正整数。这个正整数设置多少合适，即我要开多少个worker进程处理们的业务逻辑才好呢我？官方建议设置为**CPU核数的1-4倍**。因为开的进程越多，内存的占用也就更多，进程间切换也就需要耗费更多的资源。这里设置开启两个worker进程。**默认该参数的值等于你机器的CPU核数**。

**3、注册事件回调函数**

swoole\_server是事件驱动的。使用的过程中不需要关注底层是怎么实现的，底层是C写的,php只是做了个传递的作用，所以只需要对底层相应的动作注册相应的回调，在回调函数中处理业务逻辑即可。

什么意思？举个例子：

你启动了一个server，当客户端连接的时候（触发事件），你不需要关心它是怎么连接的，你就单纯的注册一个connect函数，做一些连接后的业务处理即可(执行业务)。类似于js的事件监听，比如触发了click事件，就会执行相应的闭包。

```php
//监听连接进入事件,有客户端连接进来的时候会触发
$serv->on('connect', function ($serv, $fd) {
     echo "有新的客户端连接，连接标识为$fd" . PHP_EOL;
});
```

参数$serv是一开始创建的swoole\_server对象，参数$fd是唯一标识，用于**区分不同的客户端**，同时该参数是1-1600万之间可以复用的整数。简单解释下复用：假设现在客户端1、2、3处于连接中，客户端4要连接的话$fd就是4，但是不巧的是客户端3连接不稳定，断掉了，客户端4连接到server的话，$fd就是3。

1600W个连接够用吗？单机业务百万连接，已经是很厉害了。

监听客户端数据发送，触发回调:

```php
//监听数据接收事件,server接收到客户端的数据后，worker进程内触发该回调
$serv->on('receive', function ($serv, $fd, $from_id, $data) {
       $serv->send($fd, "服务器给你发送消息了: ".$data);
});
```

worker进程内触发的。第三个参数$fromId指的是哪一个reactor线程。第四个参数，这个参数就是服务端接受到的数据，注意是**字符串**或者**二进制**内容,注意在Receive回调内，调用了$serv的send方法，可以使用send方法，向client发起通知。

监听客户端关闭，触发回调:

```php
//监听连接关闭事件,客服端关闭，或者服务器主动关闭
$serv->on('close', function ($serv, $fd) {
    echo "编号为{$fd}的客户端已经关闭.".PHP_EOL;
});
```

当客户端关闭，或者服务端主动关闭连接的时候会触发。

**4、启动服务器**

到此,基本上已经搭建到了一个高性能的server。当然，非常简单，下面只需要调用start方法启动server即可。

```php
//启动服务器
$serv->start();
```

由于swoole\_server只能运行在CLI模式下，所以不要试图通过浏览器进行访问:

```
$ php server.php
```

平时执行完一个指令，执行完就结束了，但是现在的情况正好相反，当前程序一直处于执行中的状态，并没有退出终端。同时因为swoole的server是常驻内存运行的，所以如果修改了代码，需要ctrl+c中断，重新运行程序使代码生效。

查看运行端口:

```
$ netstat -ntpl|grep 9501
```

![](https://3149448975-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LfnT31GMG1d9KiXwANW%2F-LfnT7UNILQRAFnaeTSW%2F-LfnTF1p0uFu9EvgdLsT%2F1.png?generation=1558862973178650\&alt=media)

服务器创建完成后需要创建一个client客户端和server服务端进行交互,请查看**创建TCP客户**端章节.
