socket模块源码略看

来源:互联网 发布:绘画教程软件 编辑:程序博客网 时间:2024/05/17 22:53
socket模块源码略看

python中,想查看某个模块的源码位置:

import 模块名

help(模块名),在其中有个file项,就是源码或者dll的位置

或者:模块名.__file__

例如:

import socket

help(socket)或者socket.__file__

>>> import socket>>> socket.__file__'D:\\Python32\\lib\\socket.py'

可知,其源码在D:\Python32\lib中的socket.py文件中。

打开socket.py发现,其中并没有诸如socket(),accept(),listen(),connect()函数的直接实现,它们均是继承而来:

复制代码
import _socketfrom _socket import *..................class socket(_socket.socket):..................
复制代码

接着,查看_socket模块来自哪里:

>>> import _socket>>> _socket.__file__'D:\\Python32\\DLLs\\_socket.pyd'

可以看见_socket模块来自D:\Python32\DLLs的_socket.pyd文件,_socket.pyd是python的一个动态模块,实际上dll文件,只是改了个后缀。这个文件是C编译而来。

此时,查看Python中的_socket模块:

查看socketmodule.c和socketmodule.h:

1.查看套接字对象的类型对象即sock_type(Modules/socketmodule.c):

声明:

/* A forward reference to the socket type object.   The sock_type variable contains pointers to various functions,   some of which call new_sockobject(), which uses sock_type, so   there has to be a circular reference. */static PyTypeObject sock_type;

定义:

复制代码
/* Type object for socket objects. */static PyTypeObject sock_type = {    PyVarObject_HEAD_INIT(0, 0)         /* Must fill in type value later */    "_socket.socket",                           /* tp_name */    sizeof(PySocketSockObject),                 /* tp_basicsize */    0,                                          /* tp_itemsize */    (destructor)sock_dealloc,                   /* tp_dealloc */    0,                                          /* tp_print */    0,                                          /* tp_getattr */    0,                                          /* tp_setattr */    0,                                          /* tp_reserved */    (reprfunc)sock_repr,                        /* tp_repr */    0,                                          /* tp_as_number */    0,                                          /* tp_as_sequence */    0,                                          /* tp_as_mapping */    0,                                          /* tp_hash */    0,                                          /* tp_call */    0,                                          /* tp_str */    PyObject_GenericGetAttr,                    /* tp_getattro */    0,                                          /* tp_setattro */    0,                                          /* tp_as_buffer */    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */    sock_doc,                                   /* tp_doc */    0,                                          /* tp_traverse */    0,                                          /* tp_clear */    0,                                          /* tp_richcompare */    0,                                          /* tp_weaklistoffset */    0,                                          /* tp_iter */    0,                                          /* tp_iternext */    sock_methods,                               /* tp_methods */    sock_memberlist,                            /* tp_members */    0,                                          /* tp_getset */    0,                                          /* tp_base */    0,                                          /* tp_dict */    0,                                          /* tp_descr_get */    0,                                          /* tp_descr_set */    0,                                          /* tp_dictoffset */    sock_initobj,                               /* tp_init */    PyType_GenericAlloc,                        /* tp_alloc */    sock_new,                                   /* tp_new */    PyObject_Del,                               /* tp_free */};
复制代码

 

2.套接字对象PySocketSockObject(Modules/socketmodule.h):

复制代码
typedef struct {    PyObject_HEAD    SOCKET_T sock_fd;           /* Socket file descriptor */    int sock_family;            /* Address family, e.g., AF_INET */    int sock_type;              /* Socket type, e.g., SOCK_STREAM */    int sock_proto;             /* Protocol type, usually 0 */    PyObject *(*errorhandler)(void); /* Error handler; checks                                        errno, returns NULL and                                        sets a Python exception */    double sock_timeout;                 /* Operation timeout in seconds;                                        0.0 means non-blocking */} PySocketSockObject;
复制代码

 

3.函数sock_new(Modules/socketmodule.c):创建一个新的、未初始化的套接字对象

复制代码
/* Create a new, uninitialized socket object. */static PyObject *sock_new(PyTypeObject *type, PyObject *args, PyObject *kwds){    PyObject *new;    new = type->tp_alloc(type, 0);    if (new != NULL) {        ((PySocketSockObject *)new)->sock_fd = -1;        ((PySocketSockObject *)new)->sock_timeout = -1.0;        ((PySocketSockObject *)new)->errorhandler = &set_error;    }    return new;}
复制代码

 

4.函数sock_initobj(Modules/socketmodule.c):初始化新的套接字对象

复制代码
/* Initialize a new socket object. *//*ARGSUSED*/static intsock_initobj(PyObject *self, PyObject *args, PyObject *kwds){    PySocketSockObject *s = (PySocketSockObject *)self;    PyObject *fdobj = NULL;    SOCKET_T fd = INVALID_SOCKET;    int family = AF_INET, type = SOCK_STREAM, proto = 0;    static char *keywords[] = {"family", "type", "proto", "fileno", 0};    if (!PyArg_ParseTupleAndKeywords(args, kwds,                                     "|iiiO:socket", keywords,                                     &family, &type, &proto, &fdobj))        return -1;    if (fdobj != NULL && fdobj != Py_None) {#ifdef MS_WINDOWS        /* recreate a socket that was duplicated */        if (PyBytes_Check(fdobj)) {            WSAPROTOCOL_INFO info;            if (PyBytes_GET_SIZE(fdobj) != sizeof(info)) {                PyErr_Format(PyExc_ValueError,                    "socket descriptor string has wrong size, "                    "should be %zu bytes.", sizeof(info));                return -1;            }            memcpy(&info, PyBytes_AS_STRING(fdobj), sizeof(info));            Py_BEGIN_ALLOW_THREADS            fd = WSASocket(FROM_PROTOCOL_INFO, FROM_PROTOCOL_INFO,                     FROM_PROTOCOL_INFO, &info, 0, WSA_FLAG_OVERLAPPED);            Py_END_ALLOW_THREADS            if (fd == INVALID_SOCKET) {                set_error();                return -1;            }            family = info.iAddressFamily;            type = info.iSocketType;            proto = info.iProtocol;        }        else#endif        {            fd = PyLong_AsSocket_t(fdobj);            if (fd == (SOCKET_T)(-1) && PyErr_Occurred())                return -1;            if (fd == INVALID_SOCKET) {                PyErr_SetString(PyExc_ValueError,                                "can't use invalid socket value");                return -1;            }        }    }    else {        Py_BEGIN_ALLOW_THREADS        fd = socket(family, type, proto);        Py_END_ALLOW_THREADS        if (fd == INVALID_SOCKET) {            set_error();            return -1;        }    }    init_sockobject(s, fd, family, type, proto);    return 0;}
复制代码

其中:

fd = socket(family, type, proto);为系统调用中的socket()函数。

函数原型为:

/* 以Linux为例 */#include <sys/socket>int socket(int domain, int type, int protocol);

若函数调用成功,返回一个表示这个套接字的套接字文件描述符;否则返回-1。

 

init_sockobject(Modules/socketmodule.c)函数的定义:

复制代码
/* Initialize a new socket object. */static double defaulttimeout = -1.0; /* Default timeout for new sockets */static voidinit_sockobject(PySocketSockObject *s,                SOCKET_T fd, int family, int type, int proto){    s->sock_fd = fd;    s->sock_family = family;    s->sock_type = type;    s->sock_proto = proto;    s->errorhandler = &set_error;#ifdef SOCK_NONBLOCK    if (type & SOCK_NONBLOCK)        s->sock_timeout = 0.0;    else#endif    {        s->sock_timeout = defaulttimeout;        if (defaulttimeout >= 0.0)            internal_setblocking(s, 0);    }}
复制代码

 

5.结构体数组sock_methods(Modules/socketmodule.c):列出套接字对象拥有的方法

sock_methods的结构体类型为PyMethodDef,其源码结构见:http://www.cnblogs.com/fortwo/archive/2013/04/25/3042502.html

复制代码
/* List of methods for socket objects */static PyMethodDef sock_methods[] = {    {"_accept",           (PyCFunction)sock_accept, METH_NOARGS,                      accept_doc},    {"bind",              (PyCFunction)sock_bind, METH_O,                      bind_doc},    {"close",             (PyCFunction)sock_close, METH_NOARGS,                      close_doc},    {"connect",           (PyCFunction)sock_connect, METH_O,                      connect_doc},    {"connect_ex",        (PyCFunction)sock_connect_ex, METH_O,                      connect_ex_doc},    {"detach",            (PyCFunction)sock_detach, METH_NOARGS,                      detach_doc},    {"fileno",            (PyCFunction)sock_fileno, METH_NOARGS,                      fileno_doc},#ifdef HAVE_GETPEERNAME    {"getpeername",       (PyCFunction)sock_getpeername,                      METH_NOARGS, getpeername_doc},#endif    {"getsockname",       (PyCFunction)sock_getsockname,                      METH_NOARGS, getsockname_doc},    {"getsockopt",        (PyCFunction)sock_getsockopt, METH_VARARGS,                      getsockopt_doc},#if defined(MS_WINDOWS) && defined(SIO_RCVALL)    {"ioctl",             (PyCFunction)sock_ioctl, METH_VARARGS,                      sock_ioctl_doc},#endif#if defined(MS_WINDOWS)    {"share",         (PyCFunction)sock_share, METH_VARARGS,                      sock_share_doc},#endif    {"listen",            (PyCFunction)sock_listen, METH_O,                      listen_doc},    {"recv",              (PyCFunction)sock_recv, METH_VARARGS,                      recv_doc},    {"recv_into",         (PyCFunction)sock_recv_into, METH_VARARGS | METH_KEYWORDS,                      recv_into_doc},    {"recvfrom",          (PyCFunction)sock_recvfrom, METH_VARARGS,                      recvfrom_doc},    {"recvfrom_into",  (PyCFunction)sock_recvfrom_into, METH_VARARGS | METH_KEYWORDS,                      recvfrom_into_doc},    {"send",              (PyCFunction)sock_send, METH_VARARGS,                      send_doc},    {"sendall",           (PyCFunction)sock_sendall, METH_VARARGS,                      sendall_doc},    {"sendto",            (PyCFunction)sock_sendto, METH_VARARGS,                      sendto_doc},    {"setblocking",       (PyCFunction)sock_setblocking, METH_O,                      setblocking_doc},    {"settimeout",    (PyCFunction)sock_settimeout, METH_O,                      settimeout_doc},    {"gettimeout",    (PyCFunction)sock_gettimeout, METH_NOARGS,                      gettimeout_doc},    {"setsockopt",        (PyCFunction)sock_setsockopt, METH_VARARGS,                      setsockopt_doc},    {"shutdown",          (PyCFunction)sock_shutdown, METH_O,                      shutdown_doc},#ifdef CMSG_LEN    {"recvmsg",           (PyCFunction)sock_recvmsg, METH_VARARGS,                      recvmsg_doc},    {"recvmsg_into",      (PyCFunction)sock_recvmsg_into, METH_VARARGS,                      recvmsg_into_doc,},    {"sendmsg",           (PyCFunction)sock_sendmsg, METH_VARARGS,                      sendmsg_doc},#endif    {NULL,                      NULL}           /* sentinel */};
复制代码

例如其中的一个结构体如下:

{"_accept", (PyCFunction)sock_accept, METH_NOARGS,accept_doc}

说明套接字对象有一个名为_accept的方法,该方法对应的函数指针指向sock_accept函数,结合标志为METH_NOARGS(诸如此类标志在Include/methodobject.h定义),该方法的文档为accept_doc。

sock_accept函数的定义(Modules/socketmodule.c):

复制代码
/* s._accept() -> (fd, address) */static PyObject *sock_accept(PySocketSockObject *s){    sock_addr_t addrbuf;/* 用于accept函数,存储客户端地址信息 */    SOCKET_T newfd = INVALID_SOCKET;    socklen_t addrlen;    PyObject *sock = NULL;    PyObject *addr = NULL;    PyObject *res = NULL;    int timeout;    if (!getsockaddrlen(s, &addrlen))        return NULL;    memset(&addrbuf, 0, addrlen);    if (!IS_SELECTABLE(s))        return select_error();    BEGIN_SELECT_LOOP(s)    Py_BEGIN_ALLOW_THREADS    timeout = internal_select_ex(s, 0, interval);    if (!timeout) {        newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen);/* accept调用成功返回newfd文件描述符,客户端的地址信息存储在addrbuf中 */    }    Py_END_ALLOW_THREADS    if (timeout == 1) {        PyErr_SetString(socket_timeout, "timed out");        return NULL;    }    END_SELECT_LOOP(s)    if (newfd == INVALID_SOCKET)        return s->errorhandler();    sock = PyLong_FromSocket_t(newfd);/* 根据套接字文件描述符生成一个PyObject对象 */    if (sock == NULL) {        SOCKETCLOSE(newfd);        goto finally;    }    /* 获取socket地址,makesockaddr返回(host,port)二元组 */    addr = makesockaddr(s->sock_fd, SAS2SA(&addrbuf),                        addrlen, s->sock_proto);    if (addr == NULL)        goto finally;    res = PyTuple_Pack(2, sock, addr);/* 将指向Pyobject对象的指针和地址组合成二元组 */finally:    Py_XDECREF(sock);/* 销毁引用 */    Py_XDECREF(addr);    return res;}PyDoc_STRVAR(accept_doc,"_accept() -> (integer, address info)\n\\n\Wait for an incoming connection.  Return a new socket file descriptor\n\representing the connection, and the address of the client.\n\For IP sockets, the address info is a pair (hostaddr, port).");/* s.setblocking(flag) method.  Argument:   False -- non-blocking mode; same as settimeout(0)   True -- blocking mode; same as settimeout(None)*/
复制代码

详细分析如下

newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen);是系统调用中的accpet函数。

函数原型为:

/* 以Linux为例 */#include <sys/socket>int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

函数成功调用后,返回一个新的套接字描述符表示客户端的连接,并且将客户端的IP地址、端口和协议族等信息存储在参数addr中。

服务器端接受请求后,与客户端进行通信的是accept()函数返回的新的套接字文件描述符,而不是通过在开始建立套接字时的文件描述符。

 

在python中的socket.accept()方法返回一个客户端套接字和地址的元组。现在分析下这个两部分的组合过程:

(1)newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen);这里已经分析过了,主要是newfd和addrbuf。

(2)sock = PyLong_FromSocket_t(newfd);根据套接字文件描述符生成一个PyOject对象

socketmodule.h:

#define PyLong_FromSocket_t(fd) PyLong_FromLong((SOCKET_T)(fd))

longobject.c:

复制代码
/* Create a new long int object from a C long int */PyObject *PyLong_FromLong(long ival){    PyLongObject *v;    unsigned long abs_ival;    unsigned long t;  /* unsigned so >> doesn't propagate sign bit */    int ndigits = 0;    int sign = 1;    CHECK_SMALL_INT(ival);    if (ival < 0) {        /* negate: can't write this as abs_ival = -ival since that           invokes undefined behaviour when ival is LONG_MIN */        abs_ival = 0U-(unsigned long)ival;        sign = -1;    }    else {        abs_ival = (unsigned long)ival;    }    /* Fast path for single-digit ints */    if (!(abs_ival >> PyLong_SHIFT)) {        v = _PyLong_New(1);        if (v) {            Py_SIZE(v) = sign;            v->ob_digit[0] = Py_SAFE_DOWNCAST(                abs_ival, unsigned long, digit);        }        return (PyObject*)v;    }#if PyLong_SHIFT==15    /* 2 digits */    if (!(abs_ival >> 2*PyLong_SHIFT)) {        v = _PyLong_New(2);        if (v) {            Py_SIZE(v) = 2*sign;            v->ob_digit[0] = Py_SAFE_DOWNCAST(                abs_ival & PyLong_MASK, unsigned long, digit);            v->ob_digit[1] = Py_SAFE_DOWNCAST(                  abs_ival >> PyLong_SHIFT, unsigned long, digit);        }        return (PyObject*)v;    }#endif    /* Larger numbers: loop to determine number of digits */    t = abs_ival;    while (t) {        ++ndigits;        t >>= PyLong_SHIFT;    }    v = _PyLong_New(ndigits);    if (v != NULL) {        digit *p = v->ob_digit;        Py_SIZE(v) = ndigits*sign;        t = abs_ival;        while (t) {            *p++ = Py_SAFE_DOWNCAST(                t & PyLong_MASK, unsigned long, digit);            t >>= PyLong_SHIFT;        }    }    return (PyObject *)v;}
复制代码

 

(3)addr = makesockaddr(s->sock_fd, SAS2SA(&addrbuf),addrlen, s->sock_proto);根据addrbuf生成(host,port)元组。

查看makesockaddr函数(Modules/socketmodule.c)截取用到的部分:

复制代码
/*ARGSUSED*/static PyObject *makesockaddr(SOCKET_T sockfd, struct sockaddr *addr, size_t addrlen, int proto){    if (addrlen == 0) {        /* No address -- may be recvfrom() from known socket */        Py_INCREF(Py_None);        return Py_None;    }    switch (addr->sa_family) {    case AF_INET:    {        struct sockaddr_in *a;        PyObject *addrobj = makeipaddr(addr, sizeof(*a));/* 获取addr中的host */        PyObject *ret = NULL;        if (addrobj) {            a = (struct sockaddr_in *)addr;            ret = Py_BuildValue("Oi", addrobj, ntohs(a->sin_port));/* a->sin_port获得端口号,然后将网络字节序转换为主机字节序, host和port组成元组*/            Py_DECREF(addrobj);/* 销毁addrobj引用 */        }        return ret;    }..................
复制代码

以上函数包含了makeipaddr函数。

查看makeipaddr函数(Modules/socketmodule.c):获取addr中的host部分信息

复制代码
static PyObject *makeipaddr(struct sockaddr *addr, int addrlen){    char buf[NI_MAXHOST];    int error;    error = getnameinfo(addr, addrlen, buf, sizeof(buf), NULL, 0,        NI_NUMERICHOST);    if (error) {        set_gaierror(error);        return NULL;    }    return PyUnicode_FromString(buf);}
复制代码

系统调用getnameinfo函数原型:

/* 以Linux为例 */#include <netdb.h>int getnameinfo(const struct sockaddr *sockaddr, socklen_t addrlen, char *host, socklen_t hostlen, char *serv, socklen_t servlen, int flags);

该函数以套接字地址为参数,返回其中的主机名(点分十进制字符串IP或域名)和服务名(端口号)。

说明这里的调用error = getnameinfo(addr, addrlen, buf, sizeof(buf), NULL, 0,NI_NUMERICHOST);中,buf中存储的是主机名。

 

6.结构体数组sock_memberlist(Modules/socketmodule.c):

sock_memberlist的结构体类型为PyMemberDef,其源码结构见:http://www.cnblogs.com/fortwo/archive/2013/04/25/3042502.html

复制代码
/* SockObject members */static PyMemberDef sock_memberlist[] = {       {"family", T_INT, offsetof(PySocketSockObject, sock_family), READONLY, "the socket family"},       {"type", T_INT, offsetof(PySocketSockObject, sock_type), READONLY, "the socket type"},       {"proto", T_INT, offsetof(PySocketSockObject, sock_proto), READONLY, "the socket protocol"},       {"timeout", T_DOUBLE, offsetof(PySocketSockObject, sock_timeout), READONLY, "the socket timeout"},       {0},};
复制代码

 7.sock_doc(Modules/socketmodule.c):套接字对象的文档

复制代码
/* Socket object documentation */PyDoc_STRVAR(sock_doc,"socket([family[, type[, proto]]]) -> socket object\n\\n\Open a socket of the given type.  The family argument specifies the\n\address family; it defaults to AF_INET.  The type argument specifies\n\whether this is a stream (SOCK_STREAM, this is the default)\n\or datagram (SOCK_DGRAM) socket.  The protocol argument defaults to 0,\n\specifying the default protocol.  Keyword arguments are accepted.\n\\n\A socket object represents one endpoint of a network connection.\n\\n\Methods of socket objects (keyword arguments not allowed):\n\\n\_accept() -- accept connection, returning new socket fd and client address\n\bind(addr) -- bind the socket to a local address\n\close() -- close the socket\n\connect(addr) -- connect the socket to a remote address\n\connect_ex(addr) -- connect, return an error code instead of an exception\n\_dup() -- return a new socket fd duplicated from fileno()\n\fileno() -- return underlying file descriptor\n\getpeername() -- return remote address [*]\n\getsockname() -- return local address\n\getsockopt(level, optname[, buflen]) -- get socket options\n\gettimeout() -- return timeout or None\n\listen(n) -- start listening for incoming connections\n\recv(buflen[, flags]) -- receive data\n\recv_into(buffer[, nbytes[, flags]]) -- receive data (into a buffer)\n\recvfrom(buflen[, flags]) -- receive data and sender\'s address\n\recvfrom_into(buffer[, nbytes, [, flags])\n\  -- receive data and sender\'s address (into a buffer)\n\sendall(data[, flags]) -- send all data\n\send(data[, flags]) -- send data, may not send all of it\n\sendto(data[, flags], addr) -- send data to a given address\n\setblocking(0 | 1) -- set or clear the blocking I/O flag\n\setsockopt(level, optname, value) -- set socket options\n\settimeout(None | float) -- set or clear the timeout\n\shutdown(how) -- shut down traffic in one or both directions\n\if_nameindex() -- return all network interface indices and names\n\if_nametoindex(name) -- return the corresponding interface index\n\if_indextoname(index) -- return the corresponding interface name\n\\n\ [*] not available on all platforms!");
复制代码

其中:

PyDoc_STRVAR的定义(Include/pymacro.h):

复制代码
/* Define macros for inline documentation. */#define PyDoc_VAR(name) static char name[]#define PyDoc_STRVAR(name,str) PyDoc_VAR(name) = PyDoc_STR(str)#ifdef WITH_DOC_STRINGS#define PyDoc_STR(str) str#else#define PyDoc_STR(str) ""#endif
复制代码

 

 

补充:Linux网络编程中,通用的套接字地址结构和实际使用的套接字地址结构:

(1)通用套接字地址结构:

/* 以Linux为例 */#incude <sys/socket.h>struct sockaddr{    sa_family_t   sa_family;    char          sa_data[14];};

(2)实际使用的套接字地址结构:

复制代码
/* 以Linux为例 */#incude <netinet/in.h>struct sockaddr_in{    sa_family_t         sin_family;    in_port_t           sin_port;    struct in_addr      sin_addr;    unsigned char       sin_zero[8];};
复制代码

(3)IP地址结构:

/* 以Linux为例 */#incude <netinet/in.h>typedef uint32_t in_addr_t;struct in_addr{    in_addr_t s_addr;};

由于结构体struct sockaddr和struct sockaddr_in的大小完全一致。通常使用struct sockaddr_in进行地址设置,之后将其强制转换struct sockaddr结构。

0 0