千家信息网

10.python网络编程(startin part 1)

发表于:2024-11-22 作者:千家信息网编辑
千家信息网最后更新 2024年11月22日,一.什么是socket?socket就是为了实现C/S架构而生的,socket位于应用层和传输层之间,是传输层和应用层之间的一组接口,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说
千家信息网最后更新 2024年11月22日10.python网络编程(startin part 1)

一.什么是socket?

socket就是为了实现C/S架构而生的,socket位于应用层和传输层之间,是传输层和应用层之间的一组接口,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议,所以,我们无需深入理解tcp/udp协议,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的。

也有人将socket说成ip+port,ip是用来标识互联网中的一台主机的位置,而port是用来标识这台机器上的一个应用程序,ip地址是配置到网卡上的,而port是应用程序开启的,ip与port的绑定就标识了互联网中独一无二的一个应用程序。

补充:有人会把socket和程序的pid弄混,程序的pid是同一台机器上不同进程或者线程的标识。


二.socket种类。

  1. 基于文件型套接字。

    AF_UNIX:基于文件型的套接字,就是通过底层的文件系统来取数据,两个套接字进程运行在同一台机器,可以通过访问同一个文件系统来实现两个程序之间的通信。

  2. 基于网络型套接字。

三.socket工作流程。

tcp服务端建立连接流程:

socket()实例化出一个套接字对象→bind()绑定ip地址和端口→listen()开始监听之前绑定的ip地址和端口→accept()开始被动接收tcp客户端发来的连接,会一直等连接的到来(如果没有连接,就会一直等,直到有客户端连过来。

tcp客户端建立连接流程:

socket()客户端也初始化一个套接字对象→connect()主动去连接服务端(主动初始化与tcp服务器的连接)如果连接成功,客户端和服务器之间就可以互相收发数据了。(其实connect就相当于去回应服务端的accept,服务端的accept一直在等待客户端去connect)

客户端发送数据请求,服务器端接收请求并处理请求,然后把回应数据发送给客户端,客户端读取数据,最后关闭连接,一次交互结束。



四.socket模块的用法。

import socket

socket.socket(socket_family,socket_type,protocal=0)

socket_family 可以是 AF_UNIX 或 AF_INET。socket_type 可以是 SOCK_STREAM 或 SOCK_DGRAM。protocol 一般不填,默认值为 0。

获取tcp/ip套接字

tcpSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

获取udp/ip套接字

udpSock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

由于 socket 模块中有太多的属性。我们在这里破例使用了'from module import *'语句。使用 'from socket import *',我们就把 socket 模块里的所有属性都带到我们的命名空间里了,这样能 大幅减短我们的代码。

例如tcpSock = socket(AF_INET, SOCK_STREAM)


服务端常用专属套接字方法:

s.bind():绑定(主机,端口号)到套接字。

s.listen():开始进行tcp监听。#listen方法中可以指定一个backlog参数,这个参数用于设置tcp连接池的大小(关于tcp连接池和三次握手的知识后面会做补充。)

s.accept():被动接受TCP客户的连接,(阻塞式)等待连接的到来。


客户端常用专属套接字方法:

s.connect():主动初始化TCP服务器连接

s.connect_ex() : connect()函数的扩展版本,出错时返回出错码,而不是抛出异常。


客户端和服务端共同具有的常用套接字方法:

s.recv():接收tcp数据。#需要指定每次收多少字节。

s.send(): 发送tcp数据,#发送TCP数据(send在待发送数据量大于对端缓存区剩余空间时,数据丢失,不会发完)

s.sendall(): 发送tcp数据,发送完整的TCP数据(本质就是循环调用send,sendall在待发送数据量大于对端缓存区剩余空间时,数据不丢失,循环调用send直到发完)(个人分析,本质上是调用了循环)

s.recvfrom():接收UDP数据

s.sendto() : 发送UDP数据

s.getpeername() : 连接到当前套接字的远端的地址

s.getsockname() : 当前套接字的地址

s.getsockopt() :返回指定套接字的参数

s.setsockopt() : 设置指定套接字的参数

s.close() :关闭套接字


五.socket用法示例。

服务端:

#!/usr/bin/python2.7

# -*- coding:utf-8 -*-

import socket

address_and_port = ('127.0.0.1',8888)

s1 = socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)

s1.bind(address_and_port)

s1.listen(3)

conn,addr = s1.accept() #执行了socket的accept方法后,开始阻塞,等待客户端的连接.

#执行了accept方法后,会返回一个元祖,这个元祖里面包含了一个客户端的连接对象,还有客户端的ip地址和端口.

while True:

data = conn.recv(1024) #用于接收tcp数据,后面的1024表示,recv一次最多可以接收1024字节.(执行到recv,如果对端没有发来数据,或者内容为空,程序会阻塞住,不继续向下执行。)

print data

conn.send(data.upper())

conn.close() #关闭与客户端的连接

s1.close() #关闭服务端套接字连接


客户端:

#!/usr/bin/python2.7

# -*- coding:utf-8 -*-

import socket

s1 = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

s1.connect(('127.0.0.1',8888))

while True:

msg = raw_input(">>>").strip()

s1.send(msg.encode(('utf-8')))

print "客户端已发送数据!"

data = s1.recv(1024)

print data

#上面两个代码,可以简单理解下socket的工作流程。


六.解决sockt 通信客户端断开服务端崩溃等问题的解决方法(连接循环,通信循环,以及异常捕捉)

服务端:

#!/usr/bin/python2.7

# -*- coding:utf-8 -*-

import socket

address_and_port = ('127.0.0.1',8888)

s1 = socket.socket(family=socket.AF_INET,type=socket.SOCK_STREAM)

s1.bind(address_and_port)

s1.listen(3)

while True: #此处问连接循环,会循环等连接。

conn,addr = s1.accept() #执行了socket的accept方法后,开始阻塞,等待客户端的连接.

#执行了accept方法后,会返回一个元祖,这个元祖里面包含了一个客户端的连接对象,还有客户端的ip地址和端口.

while True: #数据传输循环

try: #防止客户端断开导致的服务断抛异常。

data = conn.recv(1024) #用于接收tcp数据,后面的1024表示,recv一次最多可以接收1024字节.

if not data:

break

print data

conn.send(data.upper())

except Exception:

break

conn.close() #关闭与客户端的连接

s1.close() #关闭服务端套接字连接


客户端:

#!/usr/bin/python2.7

# -*- coding:utf-8 -*-

import socket

s1 = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

s1.connect(('127.0.0.1',8888))

while True:

msg = raw_input(">>>").strip()

if not msg: #防止客户端发送空数据

continue

s1.send(msg.encode(('utf-8')))

print "客户端已发送数据!"

data = s1.recv(1024)

print data

s1.close()


#以上是防止服务端崩溃的一些解决方法。






七.关于udp套接字。

服务端大概操作流程:

ss = socket() #创建一个服务器的套接字

ss.bind() #绑定服务器套接字

inf_loop: #服务器无限循环

cs = ss.recvfrom()/ss.sendto() # 对话(接收与发送)

ss.close() # 关闭服务器套接字



客户端大概的操作流程:

cs = socket() # 创建客户套接字

comm_loop: # 通讯循环

cs.sendto()/cs.recvfrom() # 对话(发送/接收)

cs.close() # 关闭客户套接字


下面是udp套接字的一个简单的示例:

示例1:

服务端:

import socket

udp_server = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

udp_server.bind(('127.0.0.1',8888))

while True:

msg,addr = udp_server.recvfrom(1024)

print msg ,addr

udp_server.sendto(msg.upper(),addr)


客户端:

import socket

udp_client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

while True:

msg = raw_input(">>:").strip()

if not msg:

continue

udp_client.sendto(msg.encode('utf-8'),("127.0.0.1",8888))

back_msg,addr = udp_client.recvfrom(1024)

print back_msg.decode('utf-8') ,addr



八.关于tcp套接字和udp套接字你需要知道的。

  1. 首先有几点必须明确!

    1.1 所有的收发消息,都是在操作自己的tcp/udp缓冲区,发消息是将数据发送到自己的缓冲区中,收消息同样也是从缓冲区中收取。

    1.2 tcp 使用send发消息,使用recv收消息

    1.3udp 使用sendto发消息,使用recvfrom收消息

  2. tcp与udp

    2.1 tcp是基于流的,udp是基于报的

2.2 使用send发送数据流的时候,若数据流为空,那么tcp buffer(缓冲区)也为空,操作系统不会控制tcp发包。

sendinto(bytes_data,ip_port):发送数据报,bytes_data为空,还有ip_port,所有即便是发送空的bytes_data,数据报其实也不是空的,自己这端的缓冲区收到内容,操作系统就会控制udp协议发包。

3.关于tcp的补充

tcp基于链接通信:

基于链接,则需要listen(backlog),指定半连接池的大小

基于链接,必须先运行的服务端,然后客户端发起链接请求

对于mac系统:如果一端断开了链接,那另外一端的链接也跟着完蛋recv将不会阻塞,收到的是空(解 决方法是:服务端在收消息后加上if判断,空消息就break掉通信循环)

对于windows/linux系统:如果一端断开了链接,那另外一端的链接也跟着完蛋recv将不会阻塞,收到 的是空(解决方法是:服务端通信循环内加异常处理,捕捉到异常后就break掉通讯循环)


udp通信。

无链接,因而无需listen(backlog),更加没有什么连接池之说了

无链接,udp的sendinto不用管是否有一个正在运行的服务端,可以己端一个劲的发消息,只不过数据丢失

recvfrom收的数据小于sendinto发送的数据时,在mac和linux系统上数据直接丢失,在windows系统上发送的比接收的大直接报错

只有sendinto发送数据没有recvfrom收数据,数据丢失。


注意:

1.你单独运行上面的udp的客户端,你发现并不会报错,相反tcp却会报错,因为udp协议只负责把包发出去,对方收不收,我根本不管,而tcp是基于链接的,必须有一个服务端先运行着,客户端去跟服务端建立链接然后依托于链接才能传递消息,任何一方试图把链接摧毁都会导致对方程序的崩溃。

2.上面的udp程序,你注释任何一条客户端的sendinto,服务端都会卡住,为什么?因为服务端有几个recvfrom就要对应几个sendinto,哪怕是sendinto(b'')那也要有。


0