python socket编程

一、socket基础知识

在网络编程中的一个基本组件就是套接字(socket).
1.有阻塞(同步网络编程)
2.有非阻塞(异步网络编程)
套接字基本上是两个端点的程序之间的"信息通道".程序可能分布在不同计算机上,通过套接字相互发送信息.

套接字包括两个:
1.服务套接字
创建一个服务套接字后:
1.让它等待连接,这样它就在某个网络地址处(ip地址和端口号的组合)监听,直到有客户端套接字连接.
连接完成后,两者就可以进行交互了.
2.使用bind(host,port)方法后,再调用listen(只有一个参数,服务器未处理连接的长度(即允许排队等待的连接数目,
在这些连接禁用之前))去监听某个特定位置的地址.
3.服务端开始监听后,便可以接受客户端连接.这个步骤使用accept方法来完成.这个方法会阻塞(等待)
直到客户连接,然后该方法返回一个格式为(client,address)的元组,
4.服务器处理完一个与客户端连接后,再次调用accept方法开始等待下一个连接.这个过程通常是个无限循环
2.客户机套接字
1.客户机简单的连接,完成事务,断开连接.
2.客户端使用connect(host,port)方法连接到服务器,
3.client是客户端套接字,address是一个前面解释过的地址.

一个套接字就是socket模块中socket类的一个实例.
这个实例需要三个参数
1.地址族(默认是socket.AF_INET)
2.流(socket.SOCK_STREAM,默认值)或数据报(socket.SOCK_DGRAM)套接字
3.使用协议(默认是0,使用默认值即可)

套接字有两个方法
1.send发送
2.revc接收
用于接收,用于传输数据,revc参数用来选择最大字节数来接收1024是个好选择。

二、Socket 类型

socket参数的详解

socket.socket(family=AF_INET,type=SOCK_STREAM,proto=0,fileno=None)
 创建socket对象的参数说明:
 

套接字格式:

socket(family,type[,protocal]) 使用给定的地址族、套接字类型、协议编号(默认为0)来创建套接字。

三、Socket 函数

注意点:

1)TCP发送数据时,已建立好TCP连接,所以不需要指定地址。UDP是面向无连接的,每次发送要指定是发给谁。

2)服务端与客户端不能直接发送列表,元组,字典。需要字符串化repr(data)。

 

四、socket编程思路

1.基于TCP协议的socket

注 :启动时应该先启动 server端 ,再启动 client端。

TCP服务端:

1 创建套接字,绑定套接字到本地IP与端口

   # socket.socket(socket.AF_INET,socket.SOCK_STREAM) , s.bind()

2 开始监听连接                   #s.listen()

3 进入循环,不断接受客户端的连接请求              #s.accept()

4 然后接收传来的数据,并发送给对方数据         #s.recv() , s.sendall()

5 传输完毕后,关闭套接字                     #s.close()

import socket
sk = socket.socket()  # 创建了一个socket对象
sk.bind(('127.0.0.1',8080))  # 绑定一台机器的(ip,端口)
# 回环地址 - 指向自己这台机器
sk.listen()    # 建立监听等待别人连接
conn,addr = sk.accept()       # 阻塞:在这里等待直到接到一个连接
# conn是连接
# addr是对方的地址
print(conn)
print(addr)
conn.send(b'hello')              # 和对方打招呼
msg = conn.recv(1024)                 #接收数据,并把接收的数据实例化
# 有发必有收 收发必相等
print(msg)
conn.close()                      # 挂电话
sk.close()                        # 关机

TCP客户端:

1 创建套接字,连接远端地址

       # socket.socket(socket.AF_INET,socket.SOCK_STREAM) , s.connect()

2 连接后发送数据和接收数据          # s.sendall(), s.recv()

3 传输完毕后,关闭套接字          #s.close()

import socket
sk = socket.socket()  # 买个手机
sk.connect(('127.0.0.1',8080))  # 拨号
ret = sk.recv(1024)
print(ret)
sk.send(b'byebye!')
sk.close()

问题:在启动服务端时可能会遇到 端口报错 这时是因为此端口可能已被占用,无法重复使用,那该怎么解决呢?

#加入一条socket配置,重用ip和端口
import socket
from socket import SOL_SOCKET,SO_REUSEADDR
sk = socket.socket()
sk.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
sk.bind(('127.0.0.1',8898))  #把地址绑定到套接字
sk.listen()          #监听链接
conn,addr = sk.accept() #接受客户端链接
ret = conn.recv(1024)   #接收客户端信息
print(ret)              #打印客户端信息
conn.send(b'hi')        #向客户端发送信息
conn.close()       #关闭客户端套接字
sk.close()        #关闭服务器套接字(可选)
解决方法

2.基于UDP协议的socket

使用UDP协议时,不需要建立连接,只需要知道对方的IP地址和端口号,就可以直接发数据包。但是,能不能到达就不知道了。

虽然用UDP传输数据不可靠,但它的优点是和TCP比,速度快,对于不要求可靠到达的数据,就可以使用UDP协议。

server端:

import socket
sk = socket.socket(type=socket.SOCK_DGRAM)  # 建立一个socket对象,
# 指定以UDP协议的形式来连接
sk.bind(('127.0.0.1',8080))
# 指定服务的地址
 
msg,addr = sk.recvfrom(1024) # 接收消息,发送端的地址
print(msg,addr)
sk.sendto(b'HELLO',addr)   # 给发送端回复消息
 
sk.close()  # 关闭socket连接

client端:

import socket
 
sk = socket.socket(type=socket.SOCK_DGRAM)
 
sk.sendto(b'hello',('127.0.0.1',8080))   # 直接给服务器发送一段消息
 
msg,addr = sk.recvfrom(1024)   # 接收对面的回信
 
print(msg)
 
sk.close()

 

聊天工具应用:

TCP server端

import socket
sk = socket.socket()
sk.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
sk.bind(('127.0.0.1',9000))
sk.listen()
while True:
    conn,addr = sk.accept()  # 接收连接 三次握手conn
    while True:
        inp = input('>>>')
        if inp == 'q':
            conn.send(inp.encode('utf-8'))
            break
        conn.send(inp.encode('utf-8'))
        msg = conn.recv(1024)
        if msg == b'q':break
        print(msg.decode('utf-8'))
    conn.close()    # 四次挥手
sk.close()
TCP-server

TCP client端

import socket
sk = socket.socket()
sk.connect(('127.0.0.1',9000))
while True:
    msg = sk.recv(1024)
    print(msg.decode('utf-8'))
    if msg == b'q':break
    inp = input('>>>')
    if inp == 'q':
        sk.send(inp.encode('utf-8'))
        break
    sk.send(inp.encode('utf-8'))
sk.close()
TCP-client

UDP server端

import socket
sk = socket.socket(type=socket.SOCK_DGRAM)
sk.bind(('127.0.0.1',9090))
while True:
    msg,addr = sk.recvfrom(1024)
    print('来自[%s:%s]的消息--%s'%(addr[0],addr[1],msg.decode('utf-8')))

    inp = input('>>>')
    sk.sendto(inp.encode('utf-8'),addr)

sk.close()
UDP-server

UDP client

import socket
sk = socket.socket(type=socket.SOCK_DGRAM)
addr = ('127.0.0.1',9090)
while True:
    msg = input('>>>')
    sk.sendto(msg.encode('utf-8'),addr)
    msg_recv,addr = sk.recvfrom(1024)
    print(msg_recv.decode('utf-8'))
sk.close()
UDP-cliend

时间同步服务器:(适合使用UDP协议)

# 需求
#     写一个时间同步的服务器
#     服务端接收请求
#     按照client端发送的时间格式,将服务器时间转换成对应格式
#     发送给客户端
import time
import socket

sk = socket.socket(type=socket.SOCK_DGRAM)
sk.bind(('127.0.0.1',9000))
while True:
    msg,addr = sk.recvfrom(1024)
    # msg 客户端发送给server端的时间格式 "%Y-%m-%d %H:%M-%S"
    time_format = msg.decode('utf-8')
    time_str = time.strftime(time_format)
    sk.sendto(time_str.encode('utf-8'),addr)

sk.close()
server端
# client端每隔一段时间发送请求到服务端
# 发送时间的格式
import time
import socket
sk = socket.socket(type = socket.SOCK_DGRAM)
sk.sendto('%Y-%m-%d %H:%M:%S'.encode('utf-8'),('127.0.0.1',9000))
msg,addr = sk.recvfrom(1024)
print(msg.decode('utf-8'))
sk.close()


# 方式一
# 操作系统的定时任务 + python代码的形式

# 方式二
# while True + time.sleep的形式
client端
原文地址:https://www.cnblogs.com/strive-man/p/8611554.html