socket套接字编程(一)

拈花ヽ惹草 提交于 2020-02-23 01:40:43

套接字介绍

  1. 套接字:实现网络编程进行数据传输的一种技术手段
  2. Python实现套接字编程:import socket
  3. 套接字分类:
  • 流式套接字(SOCK_STREAM):以字节流方式传输数据(可以理解为像水流一样传输),实现tcp网络传输方案。(面向连接–tcp协议–可靠的–流式套接字)
  • 数据报套接字(SOCK_DGRAM):以数据报形式传输数据(可以理解为像一瓶一瓶的水一样传输),实现udp网络传输方案。(无连接–udp协议–不可靠–数据报套接字)

TCP套接字编程

服务端流程

Created with Raphaël 2.2.0socketbindlistenacceptsend/recvclose

socket表示创建一个套接字(买了一个电话)
bind用于绑定主机ip地址等信息(好比办了一张电话卡)
listen设置监听,可以被其他套接字或程序连接的功能(充话费)
accept处于等待状态(让电话处于待机状态)
send/recv发送/接收(用电话)
close销毁套接字(把电话摔了)

  1. 创建套接字

sockfd=socket.socket(socket_family=AF_INET,socket_type=SOCK_STREAM,proto=0)

功能:创建套接字
参数:socket_family 网络地址类型 :AF_INET表示ipv4(默认值)、AF_INET6表示ipv6
socket_type 套接字类型: SOCK_STREAM表示流式(默认值)、SOCK_DGRAM表示数据报
proto 通常为0 ,选择子协议(一般用不上)
返回值:套接字对象

  1. 绑定地址

sockfd.bind(addr)

功能:绑定本机网络地址
参数:二元元祖(ip,port)。比如:(‘0.0.0.0’,8888)

  1. 设置监听

sockfd.listen(n)

功能:将套接字设置为监听套接字,确定监听队列的大小
参数:监听队列大小(linux下固定的)

  1. 等待处理客户端连接请求

connfd, addr = sockfd.accept()

功能:阻塞等待处理客户端请求
返回值:connfd 客户端连接套接字(为每个客户端创建一个套接字),addr 连接的客户端地址

  1. 消息收发

data = connfd.recv(buffersize)

功能:接收客户端消息
参数:每次最多接收消息的大小(单位:字节)
返回值:接收到的内容

n = connfd.send(data)

功能:发送消息
参数:要发送的内容,bytes格式(字节串)
返回值:发送的字节数

  1. 关闭套接字

connfd.close()
sockfd.close()

功能:关闭套接字

重点代码:

'''
TCP 套接字服务端重点代码
'''

import socket

# 创建流式套接字
sockfd=socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 绑定本机地址
sockfd.bind(('0.0.0.0', 8888))

# 设置监听
sockfd.listen(5)

# 等待客户端连接
print('Waiting for connect...')
connfd, addr = sockfd.accept()

# 收发消息
data = connfd.recv(1024)
print('接收到的消息: ', data.decode())

n = connfd.send(b'Receive your message')# 注意发送字节串,也可以encode
print('发送了%d个字节数据' % n)

# 关闭套接字
connfd.close()
sockfd.close()

客户端流程

Created with Raphaël 2.2.0socketbind(可选,一般不写)connectsend/recvclose
  1. 创建套接字
    注意:只有相同类型的套接字才能进行通信

  2. 请求连接

sockfd.connect(server_addr)

功能:连接服务器
参数:元祖 server_addr =(服务器地址,端口号)

  1. 收发信息
    注意:防止两端都阻塞,recv send要配合(一收一发,或者一发一收)

  2. 关闭套接字

重点代码:

from socket import *

# 创建tcp套接字
sockfd = socket()#此处如果是import socket方式,就和上述

# 发起连接
server_addr = ('XXX.XXX.XXX.XXX', 8888) # 服务端的网络地址
sockfd.connect(server_addr)

# 收发消息
sockfd.send('来自客户端的消息'.encode())
data = sockfd.recv(1024)
print('From Server:', data.decode())

# 关闭
sockfd.close()

tcp套接字数据传输特点

  • tcp连接中当一端退出,如果另一端堵塞在recv,此时recv会立即返回一个空字符串
  • tcp连接中如果一端已经不存在,仍然试图通过send发送则会产生BrokenPipeError
  • 一个监听套接字可以同时监听多个客户端,也能够重复被连接

网络收发缓冲区

  1. 网络缓冲区有效协调了消息的收发速度
  2. send和recv实际是向缓冲区发送接收消息,当缓冲区不为空recv就不会阻塞。
    在这里插入图片描述

tcp粘包

  • 原因:tcp以字节流方式传输,没有消息边界。多次发送的消息被一次接收,此时就会形成粘包。(通俗来说,就是发送方发的太快了,接收方收的太慢了,同时接收方发的慢,发送方收太快,造成粘包。比如发送方一次发送15字节,接收方一次从缓冲区接收5字节,接收方就返回三次receive message到发送方的缓冲区,但是发送方接收还比较快,一次性把这三次receive message全接收了,造成粘包。)
  • 影响:如果每次发送内容是一个独立的含义,需要接收端独立解析,此时粘包有会影响。(文件、音频、视频下载没什么影响)
  • 处理方法:1. 人为的添加消息边界;2. 控制发送速度

UDP套接字编程

服务端流程

  1. 创建数据报套接字

sockfd = socket(AF_INET, SOCK_DGRAM)

  1. 绑定地址

sockfd.bind(addr)

  1. 消息收发

data, addr = sockfd.recvfrom(buffersize)

功能:接收UDP消息
参数:每次最多接收多少字节
返回值:data 接收到的内容;addr消息发送方的地址

n=sockfd.sendto(data,addr)

功能:发送UDP消息
参数:data 发送的内容(bytes格式);addr 目标地址
返回值:发送的字节数

  1. 关闭套接字

sockfd.close()

重点代码:

'''
UDP 套接字服务端重点代码
'''

from socket import *

# 创建数据报套接字
sockfd = socket(AF_INET, SOCK_DGRAM)

# 绑定地址
server_addr = ('0.0.0.0', 8888)
sockfd.bind(server_addr)

# 收发消息
while True:
    data, addr = sockfd.recvfrom(1024)
    print("收到的消息:",data.decode())
    sockfd.sendto(b'Thanks',addr)

# 关闭套接字
sockfd.close()

客户端流程

  1. 创建套接字
  2. 收发消息
  3. 关闭套接字

重点代码

'''
UDP 套接字客户端重点代码
'''

from socket import *

# 服务器地址
HOST = '127.0.0.1'
PORT = 8888
ADDR = (HOST, PORT)

# 创建套接字
sockfd = socket(AF_INET, SOCK_DGRAM)

# 收发消息
while True:
    data = input('Msg>>')
    if not data:
        break
    sockfd.sendto(data.encode(), ADDR)
    msg, addr = sockfd.recvfrom(1024)
    print('From server:', msg.decode())

sockfd.close()

总结(tcp套接字和udp套接字编程区别)

  1. 流式套接字是以字节流方式传输数据,数据报套接字是以数据报形式传输
  2. tcp套接字会有粘包,udp套接字有消息边界不会粘包
  3. tcp套接字保证消息的完整性,udp则不能
  4. tcp套接字依赖listen accept建立连接才能收发消息,udp套接字不需要
  5. tcp套接字使用send,recv收发消息,udp套接字使用sendto,recvfrom

补充(socket模块方法和socket套接字属性)

此部分了解即可

  1. 部分socket模块方法(socket.方法名)
  • gethostname() 获取计算机名
  • gethostbyname(‘www.baidu.com’) 获取主机ip地址
  • getservbyname(‘mysql’) 获取服务端口号
  • getservbyport(3306)
  • inet_aton(‘192.168.1.2’) 将IP地址转换为bytes字节串
  • inet_ntoa(b’\xc0\xa8\x01’)将bytes字节串转换为IP地址
  1. 套接字属性
  • sockfd.type 套接字类型
  • sockfd.family 套接字地址类型
  • sockfd.getsockname() 获取套接字绑定地址
  • sockfd.fileno() 获取套接字的文件描述符
  • connfd.getpeername() 获取连接套接字客户端地址(连接套接字调用)
  • sockfd.setsockopt(level,option,value) 设置套接字选项,level 选项类别;option 具体选项内容;value 选项值(比如:设置套接字端口立即重用,放在创建套接字的下面:sockfd.setsockopt(SOL_SOCKET, SO_REUSEADDR, True) )
  • sockfd.getsockopt(level,option) 获取套接字选项值,返回值为value

网络编程TCP、UDP代码已上传,欢迎大家下载、学习、交流,资源地址:https://download.csdn.net/download/xiecheng1995/12182529

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!