分类 Web开发 下的文章

PHP 命名空间(namespace)详解

PHP 的命名空间(namespace)概念是从 5.3.0 开始引入的。在其它程序语言中,命名空间相当于文件夹,是用来对类文件进行分组的。许多 PHP 框架已经全面使用命名空间特性。

命名空间的引入,主要是为了解决类、函数、常量与 PHP 内部或者第三方库之间的名称冲突。

基础

语法主要是声明与使用。

声明:

// file: foo.php
namespace foo;
class Bar {}

使用:

// file: test.php
include 'foo.php';
$bar = new \foo\Bar();
// 使用别名
use foo as first;
$bar2 = new first\Bar();

声明必须放在其它代码之前,declare 关键字除外。

注意,在使用别名时,前面不能加反斜杠(因为反斜杠表示全局命名空间,而别名不是实际存在的命名空间)。但是引入进来的命名空间 foo,在使用时,必须加上正确的限定,确保命名空间的正确解析。在设置了别名后,原名称依然可用。

如果当前代码没有声明命名空间,则为全局命名空间(或者称为根空间)。

如果当前代码声明了命名空间,那么使用不带命名空间的类,会默认以当前命名空间作为限定。如果当前命名空间不存在该类,则会报错。

要使用引用文件中的类,且被引用的文件使用了命名空间,必须带上命名空间前缀。否则,使用的是当前命名空间下的类。

命名空间的名称,可以是多层级的(可称之为子命名空间),如 namespace foo\bar,使用时可以把它当作一个层级一样处理。

关键字 namespace 也可以出现在命名空间的“路径”中,放在“路径”的开头,指当前的命名空间,不管当前文件有没有声明命名空间。也就是说,在不是反斜杠开头的名称前加 namespace\ 总是正确的。

由于命名空间可能层次很多,书写麻烦,可以使用 use 语句设置别名,不加 as 则表示使用命名空间最后一节作为别名。

use My\Full\Classname;
use My\Full\Classname as C;

上一条语句与 use MyFullClassname as Classname 效果一样。下一条语句,是为了进一步简化别名。

注意,use 语句中的命名空间不需要以反斜杠开头,但在开头加上反斜杠也没有问题。如果 as 的别名与当前命名空间下类名相同(当然该同名类一定不会在当前文件中),将优先使用 as 别名对应的类。

导入函数(使用 function 关键字):

// PHP 5.6+
use function My\Full\functionName as func;

导入常量(使用 const 关键字):

// PHP 5.6+
use const My\Full\CONSTANT;

以上导入函数和常量的名称,如果与当前命名空间的名称冲突,可以用 as 取一个别名。

为了简化操作,可以将多个 use 语句写到一行:

use My\Full\Classname as Another, My\Full\NSname;

与其它语言不一样的地方是,同一个命名空间的代码可以定义在不同的文件中。

使用常量 __NAMESPACE__ 可以获得当前代码所在的命名空间。

进阶

一个文件多个命名空间

PHP 也允许在一个文件声明多个命名空间。

<?php
namespace MyProject;

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }

namespace AnotherProject;

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }

它表示,文件中的代码按块划分到 2 个命名空间。一般像这样使用,主要用于将多个文件合并到一个文件:

<?php
namespace MyProject {

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }
}

namespace AnotherProject {

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }
}

namespace { // global code
session_start();
$a = MyProject\connect();
echo MyProject\Connection::start();
}

上面代码中,第三个 namespace 后没有命名空间名称,这表示是全局代码。混合在一个文件中的代码只能这样组织,命名空间的大括号外不能有任何 PHP 代码(declare 语句除外)。

命名空间的层次关系

名词定义:

  • 非限定名称(Unqualified name):名称中不包含命名空间分隔符(反斜杠)的标识符,例如 Bar
  • 限定名称(Qualified name):名称中含有命名空间分隔符(反斜杠)的标识符,例如 fooBar
  • 完全限定名称(Fully qualified name):名称中包含命名空间分隔符,并以命名空间分隔符开始的标识符,例如 fooBar。 namespaceBar 也是一个完全限定名称。

前面两种名称,没有以反斜杠开头,是相当于当前命名空间的名称。可以将 PHP 命名空间与文件系统作一个简单的类比。在文件系统中访问一个文件有三种方式:

  1. 相对文件名形式如foo.txt。它会被解析为 currentdirectory/foo.txt,其中 currentdirectory 表示当前目录。因此如果当前目录是 /home/foo,则该文件名被解析为/home/foo/foo.txt。
  2. 相对路径名形式如subdirectory/foo.txt。它会被解析为 currentdirectory/subdirectory/foo.txt。
  3. 绝对路径名形式如/main/foo.txt。它会被解析为/main/foo.txt。

PHP 命名空间使用同样的原理。例如,类名可以通过三种方式引用:

  1. 非限定名称,即不包含前缀的类名称,例如 $a=new foo(); 或 foo::staticmethod();。如果当前命名空间是 currentnamespace,foo 将被解析为 currentnamespacefoo。如果使用 foo 的代码是全局的,不包含在任何命名空间中的代码,则 foo 会被解析为foo。 注意:如果命名空间中的函数或常量未定义,则该非限定的函数名称或常量名称会被解析为全局函数名称或常量名称。(注意只是函数和常量,不包括类。类的访问行为与此不同,见下文。)
  2. 限定名称,或包含前缀的名称,例如 $a = new subnamespacefoo(); 或 subnamespacefoo::staticmethod();。如果当前的命名空间是 currentnamespace,则 foo 会被解析为 currentnamespacesubnamespacefoo。如果使用 foo 的代码是全局的,不包含在任何命名空间中的代码,foo 会被解析为subnamespacefoo。
  3. 完全限定名称,或包含了全局前缀操作符的名称,例如, $a = new currentnamespacefoo(); 或 currentnamespacefoo::staticmethod();。在这种情况下,foo 总是被解析为代码中的字面名(literal name)currentnamespacefoo。

全局命名空间

反斜杠()表示全局命名空间,使用 PHP 内部函数,始终可以使用反斜杠作为前缀。

访问任意全局类、函数或常量,都可以使用完全限定名称,例如 strlen() 或 Exception 或 INI_ALL。尤其是当命名空间内,存在与全局类、函数、常量相同的名称时,若想使用全局类、函数、常量,则必须加上反斜杠,否则使用的是当前命名空间下的类、函数、常量。

namespace A\B\C;

/* 这个函数是 A\B\C\fopen */
function fopen() { 
     /* ... */
     $f = \fopen(...); // 调用全局的fopen函数
     return $f;
}

在声明命名空间的代码中访问全局类(必须使用反斜杠)

namespace A\B\C;
class Exception extends \Exception {}

$a = new Exception('hi'); // $a 是类 A\B\C\Exception 的一个对象
$b = new \Exception('hi'); // $b 是全局类 Exception 的一个对象

$c = new ArrayObject; // 致命错误, 找不到 A\B\C\ArrayObject 类

但是,对于函数和常量来说,如果当前命名空间中不存在该函数或常量,PHP 会使用全局空间中的函数或常量。

注:本文部分内容转载自官方文档。

参考文档:
PHP 命名空间

PHP 编码规范

PHP 编码规范

当多人协作编码时,一份共同遵守的编码规范必不可少。它有利于阅读和编写代码,既可以提高新成员熟悉代码的效率,也可以提高老成员编码的效率。

如果以下规范与框架规范冲突,则以框架规范为准。

本文档中使用了大量的“能愿动词”,说明如下:

  • 必须:绝对,严格遵循,请照做,无条件遵守;
  • 一定不能:禁令,严令禁止;
  • 应该:强烈建议这样做,但不强求;
  • 不该:强烈不建议这样做,但不强求;
  • 可以:更多的选择。

1. 命名

变量名、类的属性名 必须 使用小驼峰形式(如 $varName)。

常量名中所有字母 必须 大写,单词间 应该 以下划线分隔。

文件夹名所有字母 应该 使用小写字母,单词间 应该 以下划线分隔。

类名 必须 使用大驼峰形式(如 ClassName)。

定义类的文件名 应该 与类名相同,或者包含类名,同样 应该 使用大驼峰形式。

方法名 必须 使用小驼峰形式。

PHP 的关键字 必须 全部小写。

系统常量(true, false, null必须 小写。

2. 基础规范

代码 必须 使用 4 个空格符而不是 Tab 键进行缩进。

每行代码的字符数 应该 控制在 120 个以内。

类和方法的起始花括号({必须 在声明后自成一行,结束花括号(}必须 在代码主体后自成一行。

类的属性和方法 必须 添加访问修饰符(private, protected, public),abstractfinal 必须 写在访问修饰符之前,而 static 必须 写在访问修饰符之后。

<?php

abstract class ClassName
{
    protected static $fooVar;

    abstract protected function baz();

    final public static function barShow($a, $b = null)
    {
        // 方法主体
    }
}

赋值符号(=)的左右 必须 有且只有一个空格。

取反符号(!)后 必须 有一个空格。

纯 PHP 代码文件 应该 省略末尾的结束标签(?>)。

非空行后 不该 有多余的空格。

每行 一定不能 存在多条语句。

文件 应该 以一个空白行作为结束。

字符串 应该 使用单引号。

3. 编码风格

3.1 类、属性和方法

关键词 extendsimplements 必须 写在类名称的同一行。

<?php

class ClassName extends ParentClass implements ImplementName
{
    // 这里面是常量、属性、类方法
}

implements 的继承列表也 可以 分成多行,但每个继承接口名称都 必须 分开独立成行,包括第一个。

<?php

class ClassName extends ParentClass implements
    ImplementName1,
    ImplementName2,
    ImplementName3
{
    // 这里面是常量、属性、类方法
}

每个属性都 必须 添加访问修饰符。
一定不能 使用关键字 var 声明一个属性。
每条语句 一定不能 定义超过一个属性。
不该 使用下划线作为前缀,来区分属性是 protectedprivate

所有方法都 必须 添加访问修饰符。
不该 使用下划线作为前缀,来区分方法是 protectedprivate
方法名称后 一定不能 有空格符,参数左括号后和右括号前 一定不能 有空格。
参数列表中,每个逗号后面 必须 要有一个空格,而逗号前面 一定不能 有空格。
有默认值的参数,必须 放到参数列表的末尾。

<?php

class ClassName
{
    public function foo($arg1, &$arg2, $arg3 = [])
    {
        // 方法主体
    }
}

訪問修飾符 必須 按最小訪問原則。例如只在本類中使用的方法,必須使用 private;當該方法需要被外部訪問時,再改為 public

3.2 控制结构

控制结构的关键字(如 if)后 必须 有一个空格,而调用方法、函数的左括号前 一定不能 有空格。
控制结构的起始花括号 应该 写在声明的同一行,而最后一个结束花括号 必须 在主体后自成一行。
控制结构的左括号后,右括号前 一定不能 有空格。
结构体主体 必须 要有一次缩进。
注意,控制结构的花括号并不总是单独成行。

if 条件语句

<?php

if ($expr1) {
    // if 代码块
} elseif ($expr2) {
    // elseif 代码块
} else {
    // else 代码块
}

应该 使用关键词 elseif 代替所有 else if
主体部分,必须 使用花括号括起来,即使只有一句。

switch 语句

<?php

switch ($expr) {
    case 0:
        echo '第一种情况, 带有 break';
        break;
    case 1:
        echo '第二种情况';
        // no break
    case 2:
    case 3:
    case 4:
        echo '第三种情况, 直接 return';
        return;
    default:
        echo '默认情况';
        break;
}

case 语句 必须 相对 switch 进行一次缩进,而 break 语句以及 case 内的其它语句都 必须 相对 case 进行一次缩进。
如果存在非空的 case 直穿语句,主体中 必须 有类似 // no break 的注释。

while 和 do...while 语句

<?php

while ($expr) {
    // 循环主体
}

do {
    // 循环主体
} while ($expr);

for 语句

<?php

for ($i = 0; $i < 10; $i++) {
    // 循环主体
}

foreach 语句

<?php

foreach ($iterable as $key => $value) {
    // 循环主体
}

try...catch 语句

<?php

try {
    // 主体代码
} catch (FirstExceptionType $e) {
    // 异常处理代码
} catch (OtherExceptionType $e) {
    // 异常处理代码
}

3.3 闭包

关键词 function 后以及关键词 use 的前后都 必须 要有一个空格。
开始花括号 必须 写在声明的同一行,结束花括号 必须 紧跟主体结束的下一行。

<?php

$closureWithArgs = function ($arg1, $arg2) {
    // 主体
};

$closureWithArgsAndVars = function ($arg1, $arg2) use ($var1, $var2) {
    // 主体
};

参数列表以及变量列表 可以 分成多行,这样,包括第一个在内的每个参数或变量都 必须 单独成行,而列表的右括号与闭包的开始花括号 必须 放在同一行。

<?php

$longArgs_longVars = function (
    $longArgument,
    $longerArgument,
    $muchLongerArgument
) use (
    $longVar1,
    $longerVar2,
    $muchLongerVar3
) {
   // 代码主体
};

3.4 命名空间

namespace 声明后 必须 插入一个空白行。
所有 use 必须namespace 后声明。
每条 use 声明语句 必须 只有一个 use 关键词。
use 声明语句块后 必须 要有一个空白行。

<?php
namespace Vendor\Package;

use FooClass;
use BarClass as Bar;
use OtherVendor\OtherPackage\BazClass;

// 更多的代码...

3.5 注释与空行

单行注释 应该 放在行末,与代码之间 应该 保留 2 个空格,而较长的注释,应该 放在相应代码的上一行;注释符号(//)与注释之间 应该 保留 1 个空格。

较复杂的代码,应该 详细说明。

<?php

/**
 * 算法标题
 *
 * 首先,调用...取得...数据
 * 其次,对...数据进行循环处理,得到...
 * 最后,按...格式输出数据
 */

清楚明了的代码 不该 增加注释:

<?php

$criteria = new CDbCriteria;
// 仅获取id,parentid字段
$criteria->select = 'id,parentid';
// 排除已删除节点
$criteria->addCondition('is_delete=0');

$list = $modelClassName::model()->findAll($criteria);

代码块之间 应该 用一个空行分隔,如类的方法之间使用一个空行分隔。

附0 开发工具推荐

PhpStorm
PHPDesigner
PhpED

附1 Git 使用

提交时简明扼要地描述所变更代码的主要内容
至少形成一个基本逻辑再提交
不相关的修改应该分开提交
不必要的修改不要提交(如调试时多加了个空行,提交时查看变更内容可避免)
开发周期较长(超过一周)的需求应该新建一个分支

提交信息格式:
修改Bug:BF(###): xxxxxxx
完成需求:PR(###): xxxxxxx

参考文档

  1. PSR - PHP标准规范

Python 装饰器详解

什么是装饰器

装饰器是一个函数,一个用来包装函数的函数,接收一个函数对象,返回一个新的函数。新函数在原函数的基础上,增加了附加的功能。

1. 最简单的装饰器

def foo():
    print("foo() called.")

这是一个简单的函数,如果我们想在函数执行前做点事,可以通过以下装饰器来实现:

def decorator(func):
    def wrapper():
        print("before %s" % func.__name__)
        ret = func()
        return ret
    return wrapper

装饰器函数 decorator 接收一个函数对象作为参数,然后返回一个内部函数,内部函数的作用是在原函数执行前打印一行说明。这一行可以是其它更复杂的逻辑,当然也可以在原函数执行后做一些操作。

foo = decorator(foo)
foo()

现在执行 foo() 就看到我们要的效果了。

装饰器的实现,归功于 Python 对内部函数的支持,在函数内部创建另外一个函数,同时函数可以作为函数的参数。

2. 使用 Python 语法糖

上面对 foo 的修饰动作,有一种语法支持它。

@decorator
def foo():
    print("foo() called.")

即在函数声明前一行加上 @ 标记,这看起来更加简洁,逻辑更紧密。这个语法使得,foo 在声明时,就执行了 foo = decorator(foo)。

类的静态方法(staticmethod)和类方法(classmethod)的实现,通常装饰器使代码更直观。

3. 装饰带参数的函数

其实就是让返回的函数带参数,同时将参数传递到原函数让其调用。

def deco(func):
    def wrapper(*args, **kwargs):
        print("before %s" % func.__name__)
        ret = func(*args, **kwargs)
        return ret
    return wrapper

以上是通用的不确定参数的例子,如果参数确定,则将 wrapper 的形参定义与 原函数 func 相同即可。

4. 带参数的装饰器

装饰器自身带参数,使得装饰器更灵活。这需要在上面不带参数装饰器的基础上再包裹一层,意思是装饰器先调用自己的参数返回一新的装饰器,这个装饰器和上面的装饰器一样。

def deco(*args2, **kwargs2):
    # 这里可以使用参数 args2, kwargs2 处理一些逻辑,外部函数的参数在内部函数均可使用
    def _deco(func):
        def wrapper(*args, **kwargs):
            print("before %s" % func.__name__)
            ret = func(*args, **kwargs)
            return ret
        return wrapper
    return _deco

这也是一个通用的带参数的装饰器。使用示例:

def check_session(is_login=False):
    def _deco(func):
        def wrapper(*args, **kwargs):
            if is_login:
                # 根据相关条件判断是否已登录,若未登录则抛出错误
                pass
            return func(*args, **kwargs)
        return wrapper
    return _deco

@check_session(is_login=True)
def profile(user_id):
    pass

这个装饰器通过 is_login 参数来决定是否需要登录,才能获取用户的资料。

5. 多层装饰

对一个函数使用多个装饰器,也是合法的。

@deco2
@deco1
def foo(*args, **kwargs):
    pass

它相当于 foo = deco2(deco1(foo))。如果装饰器自身带参数,那么同样是先调用自身参数返回一个新装饰器,再执行上面的多层装饰逻辑。

6. 类也可以被装饰

类的装饰器,返回一个新的类。

def deco(cls):
    def show(self):
        print(self.__doc__)
    cls.show = show
    return cls

@deco
class MyObject:
    ''' class MyObject sample '''
    pass

这个装饰器给类增加一个 show() 方法,打印类的文档说明。

7. 包装内部函数

使用上面的装饰器,装饰过的函数,返回的是类似下面的对象:

<function deco.<locals>.wrapper at 0x000000C129C6F158>
<function deco.<locals>._deco.<locals>.wrapper at 0x000000C129C6F268>

foo.__name__ 返回的是 'wrapper' 字符串。

但我们希望 foo 仍像装饰前一样。这时,我们需要使用 functools.wraps 对内部函数再次包装。

from functools import wraps

def deco(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("before %s" % func.__name__)
        ret = func(*args, **kwargs)
        return ret
    return wrapper

它相当于 wrapper = wraps(func)(wrapper)。functools.wraps 装饰器能将被装饰的函数的特殊属性保留下来,这样就可以正常地对 foo 使用反射相关特性。

8. 装饰器也是一种设计模式

装饰模式,是动态地给一个对象添加额外的功能,它比生成子类更灵活。

装饰模式,使任何对象被装饰前后,遵循相同的交互方式。装饰后的对象,可以说是原对象的鸭子类型(duck typing)。

装饰器的作用

装饰器可以在被装饰的函数前执行预备代码,在它之后执行清理工作。

装饰器有点继承的感觉,它将函数的通用功能剥离出来,成为装饰器。然后在使用时,给需要某通用功能的函数装饰上去,让代码更简洁。

因此,装饰器擅长于记录日志、通过计时来测试代码性能、实现函数的事务处理等。

支付模块后端设计

现在很多 App 都有应用内购买项目,因此这些 App 都需要一个支付模块。

一般地,我们都是接入第三方支付,如支付宝、微信、网银等。支付的通用流程如下:

  1. 用户在 App 内发起支付请求;
  2. App 通过自己的服务端创建订单;
  3. App 将订单号和应付金额等信息,传递给第三方支付的 SDK,随即显示支付界面;
  4. 用户在第三方支付界面完成支付;
  5. 返回 App 界面,查看支付结果。

下面是一个使用支付宝支付的流程图
支付宝支付流程

App 这边主要维护一个订单表,当然需要自己的服务器,订单数据保存在服务器上。
订单表:

  • 订单号:需要生成一个全局唯一的订单号
  • 用户ID:标明该订单是哪个用户创建的
  • 商品名称
  • 商品描述
  • 商品价格:这是打折前的价格
  • 应付金额:用户实付金额,传递给第三方支付 SDK
  • 创建时间
  • 更新时间:该条记录更新的时间
  • 支付状态:NEW 新创建,PAID 支付成功,FAILURE 支付失败,DONE 订单完成

核心问题

1. 创建订单

关键是生成全局唯一的订单号。最简单的办法是直接使用数据库的自增字段。

一般我们会设计一个有意义的订单号:订单标识 + 时间标识(YYYYMMDDHHMMSS 或者时间戳) + 序号。
订单标识,是预定义好的,作为订单号的前缀,可用于区别不同的业务。
序号,可以是递增的序号,也可以是无意义的随机值,但要保证在同一秒内不重复。递增的序号,可以使用NoSQL 数据库生成,例如 redis 的 incr 操作。

2. 支付回调

上面的通用流程中,显示的支付结果,是同步结果(由第三方支付 SDK 返回的结果)。这可以明确告诉用户,支付已经成功。但是对订单的发货操作,需要等待服务端订单的更新。这就是服务端的异步回调,由第三方支付的服务器向 App 的服务器发送支付结果通知(即上图中的第 13 步),然后 App 的服务端更新订单,执行发货操作。

一般回调请求的参数是带签名或者加密的,App 服务端需要验证签名,以确保通知的合法性。

这里关键是并发的问题,如果同一个订单有多个支付通知同时发过来,而更新订单的逻辑里包括发货操作,且不是原子性的。这就是导致重复发货。对于虚拟商品,如游戏中的金币,就会多次给用户发放金币。所以更新订单的逻辑必须加锁处理,或者采用队列的方式。

Python Socket 编程

Socket (套接字)是进程通信的一种方式,主要应用于不同主机的进程之间的数据交换。

Unix/Linux 的基本哲学之一是:一切皆文件。遵循以下模式:

open -> write/read -> close

而 Socket 就是上述模式的一种实现方式。

一、如何标识进程

用一个三元组标识进程:(IP地址, 协议, 端口)。我们称之为半相关,它指定一个连接的半个部分。

一个完整的网间通信由两个进程组成,并且只能使用同一种高层协议,称之为全相关:(协议, 本地IP地址, 本地端口, 远程IP地址, 远程端口)。

操作系统的端口号,是一个 16 位的编号(2^16 = 65536),范围是 0~65535。一个进程在通讯时均会占用一个端口号。

1~255 知名端口、默认端口,如 80 是 Web 服务的端口,21 是 FTP 服务的端口。
256~1023 Unix 系统占用的端口,普通用户无权使用。
1024~5000 临时端口,一般客户端随机分配的端口在此范围。

协议包括地址族(Address Family)和套接字类型。

Address Family:

AF_UNIX / AF_LOCAL  本地进程间通信,以绝对路径作为地址
AF_INET / AF_INET6  网络间进程通信,用 ipv4/6 和端口号组合成地址
AF_NS
AF_ROUTE

套接字类型:

SOCK_STREAM  TCP流式套接字,面向连接,提供可靠的数据传输服务。
SOCK_DGRAM  UDP,数据报式套接字,无连接的,数据包以独立形式发送,可能丢失或重复,且不按顺序。
SOCK_RAW  原始套接字,允许使用较低层次协议,如 ICMP/IGMP 等。
SOCK_PACKET
SOCK_SEQPACKET  可靠的连续数据包服务。

二、相关函数

以下是 C 语言相关函数:

socket() 创建一个 socket
bind() 绑定本地地址
listen() 监听连接
accept() 接受连接,返回收到的套接字
send() 发送数据
recv() 接收数据
select() 输入输出多路复用
closesocket() 关闭套接字
connect() 与远程进程建立连接,它会自动给本地 socket 分配端口号

三、Python Socket 编程

Python 提供了两个基本的模块:

socket 提供了标准的 BSD Sockets API。
socketserver 提供了服务器相关方法,如开发网络服务器。

创建 socket 函数:

socket(family,type[,protocal]) # 使用给定的地址族、套接字类型、协议编号(默认为0)来创建套接字。

创建 TCP socket:

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

创建 UDP socket:

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

1. 服务端 socket 函数

s.bind(address)
将套接字绑定到地址, 在AF_INET下,以元组(host,port)的形式表示地址。

s.listen(backlog)
开始监听TCP传入连接。backlog指定在拒绝连接之前,操作系统可以挂起的最大连接数量。该值至少为1,一般设为 5。

s.accept()
接受TCP连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。如果没有收到连接,则会挂起,一直等待下去。

2. 客户端 socket 函数

s.connect(address)
连接到address处的套接字。一般address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。hostname 可以是域名或者IP地址。

s.connect_ex(adddress)
功能与connect(address)相同,但是成功返回0,失败返回errno的值。

3. 公共 socket 函数

s.recv(bufsize[,flag])
接收TCP套接字的数据。数据以字符串形式返回,bufsize指定要接收的最大数据量。flag提供有关消息的其他信息,通常可以忽略。

s.send(string[,flag])
发送TCP数据。将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。

s.sendall(string[,flag])
完整发送TCP数据。将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。

s.recvfrom(bufsize[,flag])
接收UDP套接字的数据。与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。

s.sendto(string[,flag],address)
发送UDP数据。将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。

s.close()
关闭套接字。

s.getpeername()
返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。

s.getsockname()
返回套接字自己的地址。通常是一个元组(ipaddr,port)

s.setsockopt(level,optname,value)
设置给定套接字选项的值。

s.getsockopt(level,optname[,buflen])
返回套接字选项的值。

s.settimeout(timeout)
设置套接字操作的超时时间,timeout 是一个浮点数,单位是秒。值为None表示没有超时时间。一般,超时时间应该在刚创建套接字时设置,因为它们可能用于连接的操作(如connect())

s.gettimeout()
返回当前超时时间的值,单位是秒,如果没有设置,则返回None。

s.fileno()
返回套接字的文件描述符。

s.setblocking(flag)
如果flag为0,则将套接字设为非阻塞模式,否则将套接字设为阻塞模式(默认值)。非阻塞模式下,如果调用recv()没有发现任何数据,或send()调用无法立即发送数据,那么将引起socket.error异常。

s.makefile()
创建一个与该套接字相关连的文件。

说明:
TCP 发送数据时,已建立好连接,所以不需要指定地址。
UDP 是无连接的,因此每次发送数据都要指定地址。

参考文档:Python Socket 网络编程