【网络接口API】Python Socket与Linux Socket

Python Socket与Linux Socket

[TOC]python

socket: Python的底层网络接口,通常状况程序员不须要接触到这个模块。有更多的高级模块,好比requests能够直接使用。本文章试图从Python的socket模块和linux socket api的角度来对Python实现网络通信方式进行分析,提升对TCP,UDP通信方式的理解。最后用Python实现一个hello/hi的简单的网络聊天程序。linux

1. socket

在Python中若是想要使用一个本身的网络应用层协议,或者说想使用纯原生TCP,UDP来实现通信,就须要使用Python的socket模块。git

import socket

socket模块提供了访问BSD套接字的接口。在全部现代Unix系统、Windows、macOS和其余一些平台上可用。程序员

1.1 socket()方法

# 使用socket()方法返回一个socket对象
s = socket.socket([family[, type, proto, fileno]])

重要参数:github

  • family: 套接字家族,如ipv4,ipv6,unix系统进程间通讯
  • type: 套接字类型,如tcp,upd
参数 描述
family
socket.AF_INET(默认) IPv4
socket.AF_INET6 IPv6
socket.AF_UNIX Unix系统进程间通讯
type
socket.SOCK_STREAM 流式套接字,TCP
socket.SOCK_DGRAM 数据报套接字,UDP
socket.SOCK_RAW 原始套接字

socket方法与Linux Socket的socket函数对应编程

// socket(协议域,套接字类型,协议)
int socket(int domain, int type, int protocol);

经过s = socket.socket()方法,获得了一个socket对象。api

Python中的socket对象的成员方法,是对套接字系统调用的高级实现,每每比C语言更高级。服务器


2. TCP

2.1 bind()方法

一般若是是服务器,须要绑定一个总所周知的地址用于提供服务,因此须要绑定一个(IP:PORT),客户端能够经过链接这个地址来得到服务。而客户端则直接经过链接,由系统随机分配一个端口号。网络

python中bind()方法传入一个地址和端口的元组dom

s.bind((host: str, port: int))

linux socket将套接字做为对象,传入一个套接字和地址结构体

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

2.2 listen()方法

开始监听,backlog指定在拒绝链接以前,操做系统能够挂起的最大链接数,默认为1

s.listen(backlog: int)

linux socket一样须要额外传入套接字参数

int listen(int sockfd, int backlog);

2.3 connect()方法

connect方法是客户端用发起某个链接的,接受一个目标主机名和端口号的元组参数

# address -> (hostname, port)
s.connect(address)
# connect_ex是connect的扩展方法,不一样在于返回错误代码,而不是抛出错误
s.connect_ex(address)

linux socket中,参数分别为客户端套接字socket描述符,服务器socket地址,socket地址长度

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

2.4 accpet()方法

服务器依次调用socket(), bind(), listen()后就会监听指定地址,客户端经过connect()向服务器发起链接请求。服务器监听到请求后会调用accept()函数接受请求。这样端与端的链接就创建好了

python中,accept()方法阻塞进程,等待链接,返回一个新的套接字对象和链接请求者地址信息。

# accept() -> (socket object, address info)
s.accept()

linux socket中,第一个参数是服务器套接字描述符,第二个为一个地址指针,用于返回客户端协议地址,第三个参数是协议地址长度。若是链接成功,函数返回值为内核自动生成的一个全新描述符

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

服务器的监听套接字通常只建立一个,而accept函数会返回一个链接套接字,服务器与客户端之间的通讯是在链接套接字上进行。每来一个服务请求新建一个链接套接字与请求者通讯,而监听套接字只有一个,完成服务后对应的链接套接字就会被关闭。(能够理解成,监听套接字是专门的接线员,只负责将电话转接给别的部门)

2.5 recv()与send()

send(data[, flags]) ->count

发送数据到socket,发送前要将数据转换为utf-8的二进制格式。返回发送数据长度,由于网络可能繁忙,致使数据没有所有发送完毕,因此要再次对剩下的数据进行发送。

python还有一个sendall()

sendall(data[, flags])

做用是不停调用send()函数,直到全部数据发送完毕

linux socket中的send()

ssize_t send(int sockfd, const void *buf, size_t len, int flags);
  • send()函数先检查协议是否正在发送缓冲区数据,等待协议发送完毕或则缓冲区已没有数据,那么send比较sockfd缓冲区剩余空间大小和发送数据的len。
  • 若是len大于剩余空间大小,则等待协议发送缓冲中数据
  • 若len小于剩余空间,则将buf中数据拷贝到剩余空间
  • 若发送数据长度大于套接字发送缓冲区长度,则返回-1

python中,从已链接套接字读取数据的函数为recv()

s.recv(bufsize: int)

从套接字接受数据,若是没有数据到达套接字,将会阻塞直到来数据或则远程链接关闭。

若是远程链接关闭且数据已所有读取,则抛出一个错误。

linux socket也有读取数据函数recv()

ssize_t recv(int sockfd, void *buf, size_t len, int flags);
  • recv()等待s发送缓冲区发送完毕
  • 检查套接字s的接受缓冲区,若协议正在接收数据,则等待接受完毕。
  • 将接收缓冲区的数据拷到buf中,接受数据可能大于buf长度,因此须要屡次调用recv()

3. UDP

在无链接的状况下,端到端须要使用另外的数据发送和接受方式

3.1 sendto()

python中发送UDP数据,将数据data发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。

s.sendto(data,address)

linux socket: 因为本地socket并无与远端机器创建链接,因此在发送数据时应指明目的地址

ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen);

该函数比send()函数多了两个参数,dest_addr表示目地机的IP地址和端口号信息,而addrlen是地址长度。

3.2 recvfrom()

s.recvfrom() -> (data, address)

接收UDP数据,与recv()相似,但返回值是(data,address)。其中data是包含接收的数据,address是发送数据的套接字地址。

linux socket: recvfrom()的状况与sendto()相似,须要指针来存放发送数据的套接字地址

ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);

4. close()

python和linux socket都须要对套接字关闭

python:

s.close()

linux socket:

int close(int socketfd)

5. Python实现hello/hi的简单的网络聊天程序

5.1 server.py

#! /usr/bin/env python3

import socket
from threading import Thread
import traceback

HOST = "127.0.0.1"
PORT = 65432

def recv_from_client(conn):
    try:
        content = conn.recv(1024)
        return content
    except Exception:
        return None

class ServiceThread(Thread):
    def __init__(self, conn, addr):
        super().__init__()
        self.conn = conn
        self.addr = addr

    def run(self):
        try:
            while True:
                content = recv_from_client(self.conn)
                if not content:
                    break
                print(f"{self.addr}: {content.decode('utf-8')}")
                self.conn.sendall(content)
            self.conn.close()
            print(f"{self.addr[0]}:{self.addr[1]} leave.")
        except Exception:
            traceback.print_exc()

if __name__ == "__main__":
    s = None
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        s.bind((HOST, PORT))
        s.listen()
        print("Repeater server started successfully.")
        while True:
            conn, addr = s.accept()
            print(f"Connected from {addr}")
            service_thread = ServiceThread(conn, addr)
            service_thread.daemon = True
            service_thread.start()
    except Exception:
        traceback.print_exc()
    s.close()

5.2 client.py

#! /usr/bin/env python3

import socket
from threading import Thread

HOST = "127.0.0.1"
PORT = 65432

class ReadFromConnThread(Thread):
    def __init__(self, conn):
        super().__init__()
        self.conn = conn

    def run(self):
        try:
            while True:
                content = self.conn.recv(1024)
                print(f"\n({HOST}:{PORT}): {content.decode('utf-8')}\nYOUR:", end="")
        except Exception:
            pass

if __name__ == "__main__":
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((HOST, PORT))
    read_thread = ReadFromConnThread(s)
    read_thread.daemon = True
    read_thread.start()
    while True:
        content = input("YOUR:")
        if content == "quit":
            break
        s.sendall(content.encode("utf-8"))
    s.close()

5.3 运行截图

  • 服务器

server

  • 客户端

client

做者:SA19225176,万有引力丶

参考资料来源:USTC Socket网络编程

相关文章
相关标签/搜索