python 套接字Socket详解

socket简介

1. 什么是socket ?

socket(简称 套接字) 是进程间通信的一种方式,它与其他进程间通信的一个主要不同是:
它能实现不同主机间的进程间通信,我们网络上各种各样的服务大多都是基于 Socket 来完成通信的
例如我们每天浏览网页、QQ 聊天、收发 email 等等
2.注意:
  • 所谓进程指的是:运行的程序以及运行时用到的资源这个整体称之为进程
  • 所谓进程间通信指的是:运行的程序之间的数据共享

3. 创建socket

在 Python 中 使用socket 模块的函数 socket 就可以完成:
import socket
socket.socket(AddressFamily, Type)

说明:

函数 socket.socket 创建一个 socket,该函数带有两个参数:
  • Address Family:可以选择 AF_INET(用于 Internet 进程间通信) 或者 AF_UNIX(用于同一台机器进程间通信),实际工作中常用AF_INET
  • Type:套接字类型,可以是 SOCK_STREAM(流式套接字,主要用于 TCP 协议)或者 SOCK_DGRAM(数据报套接字,主要用于 UDP 协议)
创建一个udp socket(udp套接字)
 
import socket
 
# 创建udp的套接字
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
 
# ...这里是使用套接字的功能(省略)...
 
# 不用的时候,关闭套接字
s.close()

 

创建一个tcp socket(tcp套接字)
import socket
 
# 创建tcp的套接字
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
 
# ...这里是使用套接字的功能(省略)...
 
# 不用的时候,关闭套接字
s.close()
一、 网络—udp通信:
 
udP通信模型如图:
 

1. udp网络程序-发送数据:

  1. 创建客户端套接字
  2. 发送/接收数据
  3. 关闭套接字
示例代码如下:
#coding=utf-8

from socket import *

# 1. 创建udp套接字
udp_socket = socket(AF_INET, SOCK_DGRAM)

# 2. 准备接收方的地址# '192.168.133.1'表示目的ip地址# 8080表示目的端口
dest_addr = ('192.168.133.1', 8080) # 注意 是元组,ip是字符串,端口是数字

# 3. 从键盘获取数据
send_data = input("请输入要发送的数据:")

# 4. 发送数据到指定的电脑上的指定程序中
udp_socket.sendto(send_data.encode('utf-8'), dest_addr)

# 5. 关闭套接字
udp_socket.close()
 
运行网络调试助手:

 

 2. udp网络程序-发送、接收数据
#coding=utf-8

from socket import *

# 1. 创建udp套接字
udp_socket = socket(AF_INET, SOCK_DGRAM)

# 2. 准备接收方的地址
dest_addr = ('192.168.133.1', 8080)

# 3. 从键盘获取数据
send_data = input("请输入要发送的数据:")

# 4. 发送数据到指定的电脑上
udp_socket.sendto(send_data.encode('utf-8'), dest_addr)

# 5. 等待接收对方发送的数据
recv_data = udp_socket.recvfrom(1024) # 1024表示本次接收的最大字节数

# 6. 显示对方发送的数据# 接收到的数据recv_data是一个元组# 第1个元素是对方发送的数据
# 第2个元素是对方的ip和端口
print(recv_data[0].decode('gbk'))
print(recv_data[1])

# 7. 关闭套接字
udp_socket.close()

运行网络调试助手:

 
在pycharm中运行程序:
 
 

应用:udp聊天器

说明

  • 在一个电脑中编写1个程序,有2个功能
  • 1.获取键盘数据,并将其发送给对方
  • 2.接收数据并显示
  • 并且功能数据进行选择以上的2个功能调用
 代码如下:
import socket


def send_msg(udp_socket):
"""获取键盘数据,并将其发送给对方"""
  # 1. 从键盘输入数据
  msg = input("请输入要发送的数据:")
  # 2. 输入对方的ip地址
  dest_ip = input("请输入对方的ip地址:")
  # 3. 输入对方的port
  dest_port = int(input("请输入对方的port:"))
  # 4. 发送数据
  udp_socket.sendto(msg.encode("utf-8"), (dest_ip, dest_port))


def recv_msg(udp_socket):
"""接收数据并显示"""
  # 1. 接收数据
  recv_msg = udp_socket.recvfrom(1024)
  # 2. 解码
  recv_ip = recv_msg[1]
  recv_msg = recv_msg[0].decode("utf-8")
  # 3. 显示接收到的数据
  print(">>>%s:%s" % (str(recv_ip), recv_msg))


def main():
  # 1. 创建套接字
  udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  # 2. 绑定本地信息
  udp_socket.bind(("", 7890))
  while True:
    # 3. 选择功能
    print("="*30)
    print("1:发送消息")
    print("2:接收消息")
    print("="*30)
    op_num = input("请输入要操作的功能序号:")

    # 4. 根据选择调用相应的函数
    if op_num == "1":
      send_msg(udp_socket)
    elif op_num == "2":
      recv_msg(udp_socket)
    else:
      print("输入有误,请重新输入...")

if __name__ == "__main__":
  main()

 

二、 网络—tcp通信:
 
TCP通信模型如图:

tcp客户端构建流程:

1.创建socket  
2. 建立连接 connect  
3. 发送 send  
4. 接收recv 
示例代码:
from socket import *

# 创建socket
tcp_client_socket = socket(AF_INET, SOCK_STREAM)

# 目的信息
server_ip = input("请输入服务器ip:")
server_port = int(input("请输入服务器port:"))

# 链接服务器
tcp_client_socket.connect((server_ip, server_port))

# 提示用户输入数据
send_data = input("请输入要发送的数据:")

tcp_client_socket.send(send_data.encode("gbk"))

# 接收对方发送过来的数据,最大接收1024个字节
recvData = tcp_client_socket.recv(1024)
print('接收到的数据为:', recvData.decode('gbk'))

# 关闭套接字
tcp_client_socket.close()

运行网络调试助手:

 

在pycharm中运行程序:

 
tcp服务器构建流程:
  1. socket创建一个套接字
  2. bind绑定ip和port
  3. listen使套接字变为可以被动链接
  4. accept等待客户端的链接
  5. recv/send接收发送数据
示例代码:
from socket import *

# 创建socket
tcp_server_socket = socket(AF_INET, SOCK_STREAM)

# 本地信息
address = ('', 7788)

# 绑定
tcp_server_socket.bind(address)

# 使用socket创建的套接字默认的属性是主动的,使用listen将其变为被动的,这样就可以接收别人的链接了
tcp_server_socket.listen(128)

# 如果有新的客户端来链接服务器,那么就产生一个新的套接字专门为这个客户端服务
# client_socket用来为这个客户端服务# tcp_server_socket就可以省下来专门等待其他新客户端的链接
client_socket, clientAddr = tcp_server_socket.accept()

# 接收对方发送过来的数据
recv_data = client_socket.recv(1024) # 接收1024个字节
print('接收到的数据为:', recv_data.decode('gbk'))

# 发送一些数据到客户端
client_socket.send("thank you !".encode('gbk'))

# 关闭为这个客户端服务的套接字,只要关闭了,就意味着为不能再为这个客户端服务了,如果还需要服务,
# 只能再次重新连接
client_socket.close()

运行流程:

 <1>tcp服务器:

 <2>网络调试助手:

 

tcp注意点:

  1. tcp服务器一般情况下都需要绑定,否则客户端找不到这个服务器
  2. tcp客户端一般不绑定,因为是主动链接服务器,所以只要确定好服务器的ip、port等信息就好,本地客户端可以随机
  3. tcp服务器中通过listen可以将socket创建出来的主动套接字变为被动的,这是做tcp服务器时必须要做的
  4. 当客户端需要链接服务器时,就需要使用connect进行链接,udp是不需要链接的而是直接发送,但是tcp必须先链接,只有链接成功才能通信
  5. 当一个tcp客户端连接服务器时,服务器端会有1个新的套接字,这个套接字用来标记这个客户端,单独为这个客户端服务
  6. listen后的套接字是被动套接字,用来接收新的客户端的链接请求的,而accept返回的新套接字是标记这个新客户端的
  7. 关闭listen后的套接字意味着被动套接字关闭了,会导致新的客户端不能够链接服务器,但是之前已经链接成功的客户端正常通信。
  8. 关闭accept返回的套接字意味着这个客户端已经服务完毕
  9. 当客户端的套接字调用close后,服务器端会recv解堵塞,并且返回的长度为0,因此服务器可以通过返回数据的长度来区别客户端是否已经下线
  10. 先运行server的代码,再运行client的代码。

案例:文件下载器

 服务器 参考代码如下:

from socket import *
import sys


def get_file_content(file_name):
"""获取文件的内容"""
  try:
    with open(file_name, "rb") as f:
    content = f.read()
    return content
  except:
    print("没有下载的文件:%s" % file_name)


def main():

  if len(sys.argv) != 2:
    print("请按照如下方式运行:python3 xxx.py 7890")
    return
  else:
    # 运行方式为python3 xxx.py 7890
    port = int(sys.argv[1])


  # 创建socket
  tcp_server_socket = socket(AF_INET, SOCK_STREAM)   # 本地信息   address = ('', port)   # 绑定本地信息   tcp_server_socket.bind(address)   # 将主动套接字变为被动套接字   tcp_server_socket.listen(128)   while True:     # 等待客户端的链接,即为这个客户端发送文件     client_socket, clientAddr = tcp_server_socket.accept()     # 接收对方发送过来的数据     recv_data = client_socket.recv(1024) # 接收1024个字节     file_name = recv_data.decode("utf-8")     print("对方请求下载的文件名为:%s" % file_name)     file_content = get_file_content(file_name)     # 发送文件的数据给客户端     # 因为获取打开文件时是以rb方式打开,所以file_content中的数据已经是二进制的格式,因此不需要encode编码     if file_content:       client_socket.send(file_content)       # 关闭这个套接字       client_socket.close()   # 关闭监听套接字   tcp_server_socket.close() if __name__ == "__main__":   main()

 

客户端 参考代码如下:

from socket import *


def main():

  # 创建socket
  tcp_client_socket = socket(AF_INET, SOCK_STREAM)

  # 目的信息
  server_ip = input("请输入服务器ip:")
  server_port = int(input("请输入服务器port:"))

  # 链接服务器
  tcp_client_socket.connect((server_ip, server_port))

  # 输入需要下载的文件名
  file_name = input("请输入要下载的文件名:")

  # 发送文件下载请求
  tcp_client_socket.send(file_name.encode("utf-8"))

  # 接收对方发送过来的数据,最大接收1024个字节(1K)
  recv_data = tcp_client_socket.recv(1024)
  # print('接收到的数据为:', recv_data.decode('utf-8'))
  # 如果接收到数据再创建文件,否则不创建
  if recv_data:
    with open("[接收]"+file_name, "wb") as f:
    f.write(recv_data)

  # 关闭套接字
  tcp_client_socket.close()


if __name__ == "__main__":
  main()
posted @ 2018-11-17 22:12  皮皮虾打怪兽  阅读(887)  评论(0编辑  收藏  举报