socket模块源码略看
来源:互联网 发布:绘画教程软件 编辑:程序博客网 时间:2024/05/17 22:53
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结构。
- socket模块源码略看
- 从源码看angular/material2 中dialog模块的实现
- STL的排序算法略看
- Dubbo|Dubbox 源码解读略析
- 学习笔记: 源码 pooling_layer.cpp 略懂
- 学习笔记: 源码 relu_layer.cpp 略见
- 学习笔记: 源码 inner_product_layer.cpp 略识
- 学习笔记: 源码 accuracy_layer.cpp 略明
- 学习笔记: 源码 softmax_layer.cpp 略通
- 学习笔记: 源码 multinomial_logistic_loss_layer.cpp 略晓
- 学习笔记: 源码 softmax_loss_layer.cpp 略析
- socket源码
- 见证三十载辉煌 三大战略看未来
- 在sqlplus 起用autotrace,并略看执行计划。
- 功能强大的IOCP Socket Server模块源码——完成端口通讯服务器(IOCP Socket Server)设计(六)
- 功能强大的IOCP Socket Server模块源码——完成端口通讯服务器(IOCP Socket Server)设计(六)
- node.js实现即时聊天室,使用模块(express+socket.io),附源码
- PYTHON模块之socket
- Codevs 3269 混合背包(二进制优化)
- Class.forName()的作用与使用总结
- 基于thinkcmf制作长江文明馆框架总结
- 最小生成树
- 操作系统思考
- socket模块源码略看
- 一组常用的弹出窗口用法
- transition/animation与visibility/display
- java重载/重写(覆盖)/重构比较及其与C++差异
- 简论select()的接口设计与内核实现的得失(2)——The C10M Problem
- Java动态编程初探——Javassist
- C#中取得Web的当前目录
- 根据网站地址将网页保存为图片(网页快照)
- 【总结】"浪潮杯"第七届ACM山东省省赛山师场总结