python socket--"套接字"

一:TCP连接之三次握手与四次挥手

    TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议。所以,两台遵循TCP的主机在彼此交换数据包之前必须先建立一个TCP连接。

    TCP通过三次握手建立连接:

    1,客户端发送SYN(SEQ=x)报文给服务器端,进入SYN_SEND状态。
    2,服务器端收到SYN报文,回应一个SYN (SEQ=y)ACK(ACK=x+1)报文,进入SYN_RECV状态。
    3,客户端收到服务器端的SYN报文,回应一个ACK(ACK=y+1)报文,进入Established状态。

    三次握手完成,TCP客户端和服务器端成功地建立连接,可以开始传输数据了。



    TCP终止一个连接要经过四次挥手,这是由TCP的半关闭(half-close)造成的:

    1,某个应用进程首先调用close,称该端执行“主动关闭”(active close)。该端的TCP于是发送一个FIN分节,表示数据发送完毕。
    2, 接收到这个FIN的对端执行 “被动关闭”(passive close),这个FIN由TCP确认。
          FIN的接收也作为一个文件结束符(end-of-file)传递给接收端应用进程,放在已排队等候该应用进程接收的任何其他数据之后
    3,一段时间后,接收到这个文件结束符的应用进程将调用close关闭它的套接字。这导致它的TCP也发送一个FIN
    4,接收这个最终FIN的原发送端TCP(即执行主动关闭的那一端)确认这个FIN。

    在步骤2与步骤3之间,从执行被动关闭一端到执行主动关闭一端流动数据是可能的,这称为“半关闭”(half-close)。


扫描二维码关注公众号,回复: 955207 查看本文章



二:何谓 "套接字"--socket

    伯克利套接字(Internet Berkeley sockets) ,又称为BSD 套接字(BSD sockets)是一种应用程序接口(API),用于网际套接字( socket)与Unix域套接字,包括了一个用C语言写成的应用程序开发库,主要用于实现进程间通讯,在计算机网络通讯方面被广泛使用。

    Berkeley套接字应用程序接口形成了事实上的网络套接字的标准精髓。这套应用程序接口也被用于Unix域套接字(Unix domain sockets),后者可以在单机上为进程间通讯(IPC)的接口。

    本篇主要讲的是网际套接字socket,它大多源自Berkeley套接字标准。
    socket是一种操作系统提供的进程间通信机制,也是操作系统为应用程序提供的一种应用程序接口(API)
    在套接字接口中,以IP地址及通信端口组成套接字地址(socket address)。远程的套接字地址,以及本地的套接字地址完成连接后,再加上使用的协议(protocol),这个五元组(five-element tuple),作为套接字对(socket pairs),之后就可以彼此交换数据

     socket本质是编程接口(API),是对TCP/IP协议族传输层及其以下层的封装

     通过socket这个编程接口,可以实现端口到端口的通信,即能实现任何应用程序之间的通信



三:python中socket的实现--socket模块

    TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议。

#创建socket对象最常用方法(还有很多其他创建方法)
socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)
    family(不全):
    socket.AF_INET        IPv4(默认)
    socket.AF_INET6      IPv6
    socket.AF_UNIX       只能够用于单一的Unix系统进程间通信

    type(不全):
    socket.SOCK_STREAM    流式socket , for TCP (默认)
    socket.SOCK_DGRAM     数据报式socket , for UDP

    socket.SOCK_RAW              原始套接字

    常用方法:

socket.bind(address)
Bind the socket to address.

socket.listen([backlog])
Enable a server to accept connections.
If not specified, a default reasonable value is chosen--for backlog

socket.accept()
Accept a connection;The return value is a pair (conn, address) where conn is a new socket object
usable to send and receive data on the connection, and address is the address bound to the socket
on the other end of the connection

socket.connect(address)
Connect to a remote socket at address.

socket.send(bytes[, flags])    TCP method
Send data to the socket. The socket must be connected to a remote socket
Returns the number of bytes sent. Applications are responsible for checking that all data has been sent;
if only some of the data was transmitted, the application needs to attempt delivery of the remaining data

socket.sendall(bytes[, flags])
Send data to the socket. The socket must be connected to a remote socket.
this method continues to send data from bytes until either all data has been sent or an error occurs. 
None is returned on success.

socket.sendto(bytes[, flags], address)   UDP method
Send data to the socket. The socket should not be connected to a remote socket, since the destination socket
is specified by address.Return the number of bytes sent

socket.recv(bufsize[, flags])
Receive data from the socket. The return value is a bytes object representing the data received.
Note: For best match with hardware and network realities, the value of bufsize should be a relatively
small power of 2, for example, 4096

socket.recvfrom(bufsize[, flags])
Receive data from the socket. The return value is a pair (bytes, address)

socket.close()
Mark the socket closed
Note: close() releases the resource associated with a connection but does not necessarily close the
connection immediately. If you want to close the connection in a timely fashion, call shutdown() before close().




#服务器端 Sever

import socket
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(('127.0.0.1',8080))    # 服务器需要绑定地址
server.listen(5)    # 可以挂起的最大连接请求数量,不传则自动选择一个合理的默认值

while True:
    connection,client_addr = server.accept()    # 建立连接
    print("客户端地址:",client_addr)
    while True:
        try:
            msg = connection.recv(1024)  # 1024表示一次从内存中取1024字节的数据
            if not msg:break   # 针对linux系统
            connection.send(msg.upper())
        except ConnectionError:  # 捕捉客户端断开连接异常
            break
    connection.close()   # 断开连接
server.close()   # 关闭socket对象
# 客户端 Client

import socket

client = socket.socket()   # 默认family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None
client.connect(('127.0.0.1',8080))    # 与服务器建连接

while True:
    msg = input('>>:').strip()
    if not msg:continue
    if msg == 'q':
        break
    client.send(msg.encode('utf-8'))   #只能发送bytes类型的数据,所有这里需要先编码
    data = client.recv(1024)        # 接收到的是bytes类型
    print(data.decode('utf-8'))
client.close()
    下图模拟了5台客户端依次访问服务器的过程:


    UDP是一种无连接的,不可靠的,面向报文的传输层通信协议

# UDP Server

import socket
server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
server.bind(('127.0.0.1',8080))

while True:
    msg,client_addr= server.recvfrom(1024)
    print(msg.decode('utf-8'),client_addr)      # hello ('127.0.0.1', 51705)
    server.sendto(msg.upper(),client_addr)
# UDP Client

import socket
client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

while True:
    msg = input('>>:').strip()
    client.sendto(msg.encode('utf-8'),('127.0.0.1',8080))
    data,server_addr = client.recvfrom(1024)
    print(data.decode('utf-8'),server_addr)   # HELLO ('127.0.0.1', 8080)

四:TCP粘包现象及其处理方法

    TCP粘包是指发送方发送的若干包数据到接收方接收时粘成一包,从接收缓冲区看,后一包数据的头紧接着前一包数据的尾,其本质原因是TCP是基于字节流的,数据之间没有相应分界

    会造成粘包:
    1发送端需要等缓冲区满才发送出去,造成粘包

    2接收方不及时接收缓冲区的包,造成多个包一起接收


    为TCP数据加上保护边界,可预防粘包:
    (1)发送固定长度的数据
    (2)把数据的大小与数据一块发送
    (3)使用特殊标记来区分数据间隔,如给每个数据包加上一个定制的包头


猜你喜欢

转载自blog.csdn.net/hua1011161696/article/details/80018535