Python----socket编程

基于TCP的套接字编程:

socket层的位置:socket在传输层和应用层之间

socket是什么:

Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

所以,我们无需深入理解tcp/udp协议,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的。

 套接字分类:1、基于文件类型的套接字家族;套接字家族的名字:AF_UNIX

                       2、基于网络类型的套接字家族;套接字家族的名字:AF_INET

 套接字工作流程:

先从服务器端说起。服务器端先初始化Socket,然后与端口绑定(bind),对端口进行监听(listen),调用accept阻塞,等待客户端连接。在这时如果有个客户端初始化一个Socket,然后连接服务器(connect),如果连接成功,这时客户端与服务器端的连接就建立了。客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。

socket模块的应用: 

服务端套接字函数

s.bind() 绑定(主机,端口号)到套接字 、s.listen() 开始TCP监听、s.accept() 被动接受TCP客户的连接,(阻塞式)等待连接的到来。

s.close()  关闭服务器。con.close()关闭链接

import socket

# 明确配置变量
ip_port = ('127.0.0.1',8080)
back_log = 5
buffer_size = 1024
# 创建一个TCP套接字
ser = socket.socket(socket.AF_INET,socket.SOCK_STREAM)   # 套接字类型AF_INET, socket.SOCK_STREAM   tcp协议,基于流式的协议
ser.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)  # 对socket的配置重用ip和端口号
# 绑定端口号
ser.bind(ip_port)  #  写哪个ip就要运行在哪台机器上
# 设置半连接池
ser.listen(back_log)  # 最多可以连接多少个客户端
while 1:
    # 阻塞等待,创建连接
    con,address = ser.accept()  # 在这个位置进行等待,监听端口号 
    while 1:
        try:
            # 接受套接字的大小,怎么发就怎么收
            msg = con.recv(buffer_size)
            if msg.decode('utf-8') == '1':
                # 断开连接
                con.close()
            print('服务器收到消息',msg.decode('utf-8'))
        except Exception as e:
            break
# 关闭服务器
ser.close()

客户端:p.connect(ip地址,端口号)  连接服务器

import socket

p = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
p.connect(('127.0.0.1',8080))
while 1:
    msg = input('please input')
    # 防止输入空消息
    if not msg:
        continue
    p.send(msg.encode('utf-8'))  # 收发消息一定要二进制,记得编码
    if msg == '1':
        break
p.close()

※用户态:专门存放应用程序。内核态:专门存放操作系统的内核。

※在socket 里面  from socket import *  这样可以减少代码量。

当发送回车换行,内容为空,没有必要发所以就会卡顿。当自己这一端 的内核态没东西会卡住recv()。加一个判断是否为空解决。

※在Windows下面杀掉一个链接,会报错。有的系统下面不会报错,这样的话判断是否收空。

※按照socket数据--->内核态----->网卡的顺序发送
send()\recv()都是发送socket数据
send()和recv()都是往自己的内存里面收发。

※端口号+ip地址+mac地址 =  哪个应用程序+哪台电脑+哪个房间(一一对应) 标示互联网上唯一的一个程序

 udp套节字:

由于udp是无连接的所以比TCP更简洁。

服务端:recefrom() 接受的结果是发送的信息,和发送方的IP和端口号。sendto(信息,目标主机IP和端口号)

from socket import *

udp_ser = socket(AF_INET, SOCK_DGRAM)  # 数据报式的套接字
udp_ser.bind(('127.0.0.1', 8080))

while 1:
    data = udp_ser.recvfrom(1024)
    print(data)
    udp_ser.sendto('data'.encode('utf-8'),data[1])
# udp_ser.close()


>>> (b'2333', ('127.0.0.1', 58113))

客户端:

from socket import *

s = socket(AF_INET, SOCK_DGRAM)
while 1:
    msg = input('input-1')
    s.sendto(msg.encode('utf-8'), ('127.0.0.1', 8080))
    data,addr = s.recvfrom(1024)
    print(data.decode('utf-8'))
# s.close()

※recv在自己这端缓冲区为空时,阻塞。
   recvfrom在自己这端的缓冲区为空时,就收一个空。   

用udp实现两个不同客户端的交流:

服务端:

from socket import *

udp_ser = socket(AF_INET, SOCK_DGRAM)  # 数据报式的套接字
udp_ser.bind(('127.0.0.1', 8080))
data_1, addr_1 = udp_ser.recvfrom(1204)
data_2, addr_2 = udp_ser.recvfrom(1204)
while 1:
    # udp_ser.sendto('你好'.encode('utf-8'))
    data1 = udp_ser.recvfrom(1204)
    udp_ser.sendto(data1[0],addr_2)
    data2 = udp_ser.recvfrom(1204)
    # udp_ser.sendto(data,)
    udp_ser.sendto(data2[0], addr_1)

客户端:

from socket import *

s = socket(AF_INET, SOCK_DGRAM)
s.sendto('hello'.encode('utf-8'), ('127.0.0.1', 8080))
while 1:
    msg = input('input-1')
    s.sendto(msg.encode('utf-8'), ('127.0.0.1', 8080))
    data,addr = s.recvfrom(1024)
    print(data.decode('utf-8'))

客户端2:

from socket import *

s = socket(AF_INET,SOCK_DGRAM)
s.sendto('hello'.encode('utf-8'),('127.0.0.1', 8080))
while 1:
    data,addr= s.recvfrom(1024)
    print(data.decode('utf-8'))
    msg = input('input-2')
    s.sendto(msg.encode('utf-8'),('127.0.0.1', 8080))

HTTP三次握手、四次挥手:

三次握手(因为刚开始没有数据传输所以可以合并),四次挥手(因为客户端到服务端数据传完可以断开,但是服务端的数据不一定发完,所以不能一次性断开)
握手:1.客户端发送连接请求(syn)
          2.服务器回应确认发送(ack),第一条客户端到服务器端的连接建好,并且向客户端发送连接请求(syn)
          3.客户端回应确认发送(ack),第二条服务器端到客户端的连接建好。
挥手(谁先发完,谁就断开连接):1.客户端发送请求断开连接(seq)  2.服务端回应确认(ack)此时客户端到服务端的链接断开

                                                          3.服务端发送请求断开连接(seq) 4.客户端回应确认(ack) 此时服务端到客户端的链接断开

                                                          至此双向连接都已断开。

猜你喜欢

转载自blog.csdn.net/weixin_41678001/article/details/82830732