什么是socket
我们写的网络编程属于应用层,当我们写好了程序后,需要将数据发送给运输层,
应用层和运输层通信需要遵循相关协议,我们不了解运输层的协议,因此提供了Socket层
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它负责处理复杂的TCP/IP协议族
让Socket去组织数据,以符合指定的协议,我们只负责编写逻辑即可,不用去管TCP/IP具体的格式,Sockect会帮我们做。
套接字有两种(或者称为有两个种族),分别是基于文件型的和基于网络型的。
基于文件型
套接字家族的名字:AF_UNIX
因为起源于unix,unix中一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成通信,两个应用程序通过一个文件进行交流,一个应用程序往文件中写,另一个读取
基于网络型
套接字家族的名字:AF_INET
(还有AF_INET6被用于ipv6,还有一些其他的地址家族,不过,他们要么是只用于某个平台,要么就是已经被废弃,或者是很少被使用,或者是根本没有实现,所有地址家族中,AF_INET是使用最广泛的一个,python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我们只使用AF_INET)
socket参数详解
socket.socket(family=AF_INET,type=SOCK_STREAM)
- family:地址系列应为AF_INET(默认值),AF_INET6,AF_UNIX,AF_CAN或AF_RDS。(AF_UNIX 域实际上是使用本地 socket 文件来通信)
- type:套接字类型应为SOCK_STREAM(默认值),SOCK_DGRAM,SOCK_RAW或其他SOCK_常量之一。
SOCK_STREAM 是基于TCP的,有保障的(即能保证数据正确传送到对方)面向连接的SOCKET,多用于资料传送。
SOCK_DGRAM 是基于UDP的,无保障的面向消息的socket,多用于在网络上发广播信息。
本文章只介绍基于TCP协议的socket
服务端
先看代码
import socket
a = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
a.bind(('127.0.0.1', 9000))
a.listen(5)
b, c = a.accept()
print('客户端地址', c)
d = b.recv(102400)
d = d.decode('utf-8')
print('接收到的消息', d)
z=input('发送的消息')
z=z.encode('utf-8')
b.send(z)
b.close()
这是一个基本的服务端,首先创建一个socket套接字,然后设置使用的ip地址,和使用的端口。
a = socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
a.bind(('127.0.0.1', 9000))
变量a就代编这个服务端
然后使用套接字的listen方法设置监听,就是使服务端设置成一个被动状态,等待其他设备主动连接,括号内写的是最大的连接数量
a.listen(5)
如果有客户端主动连接,服务端使用accept()方法进行与客户端的连接
b, c = a.accept()
print('客户端地址', c)
accept()方法会返回两个值,一个是对方的IP地址与端口号,另一个使根据该IP地址和端口号建立的套接字连接对象,通过这个对象与客户端进行数据传输
d = b.recv(102400)
d = d.decode('utf-8')
print('接收到的消息', d)
z=input('发送的消息')
z=z.encode('utf-8')
b.send(z)
b.close()
- 使用recv()方法接收对方发送过来的数据,括号内使接受数据的最大值。
- 使用send()方法向对方发送数据,括号内是要发送的数据。
数据在网络中传输用的是二进制的数据流,所以在接收或者发送数据前,都要对数据进行编译,分别是encode()编译和decode()解译,括号内写的是使用的编码格式。
不要忘记在数据传输结束后要使用close()方法关闭这个套接字连接,双方都要有关闭的动作,否则互相都不知道是否传输完成
客户端
import socket
a=socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
a.connect(('127.0.0.1',9000))
z=input('发送的消息')
z=z.encode('utf-8')
a.send(z)
li=a.recv(1024)
li=li.decode('utf-8')
print('接收到的消息',li)
a.close()
同样需要先创建一个socket套接字
a=socket.socket(family=socket.AF_INET, type=socket.SOCK_STREAM)
然后与目标服务端主动的发起连接
a.connect(('127.0.0.1',9000))
发起连接的过程,会使用自己的IP地址,然后随机占用一个端口。
使用connect()方法与目标服务端口进行连接,括号内填的是目标服务端的IP地址和端口组成的元组。
connect()会带着自己使用的IP地址和端口号向目标服务端发起一个连接请求,然后服务端就会知道是谁(IP地址和端口号)要与他连接
这个过程服务端会通过客户端发过去的请求建立一个套接字对象用来与客户端进行连接与数据收发,客户端使用自己的套接字对象与服务端进行数据的收发。
(每个套接字对象都要绑定一个IP地址与端口,套接字的收发数据就是往自己绑定的IP地址与端口上发送的。)
(如果绑定的不是自己使用的IP地址与端口那么这个套接字是用来与这个IP地址和端口收发数据的,如果绑定的是自己使用的IP地址与端口就是用来管理自己的IP地址与端口)
这个链接过程前提是服务器什么都不知道只是被动的等待别人去主动找他,而客户端需要知道服务器的IP地址还有开放的端口,然后主动发起连接。
建立连接之后进行正常的数据收发
z=input('发送的消息')
z=z.encode('utf-8')
a.send(z)
li=a.recv(1024)
li=li.decode('utf-8')
print('接收到的消息',li)
a.close()
接受数据和发送数据使用的方法服务端与客户端都相同
- 使用recv()方法接收对方发送过来的数据,括号内使接受数据的最大值。
- 使用send()方法向对方发送数据,括号内是要发送的数据。
注:收发数据也需要进行转换
总结
Socket是应用层与TCP/IP协议族通信的中间软件抽象层,并不存在,是我们抽象出来的,使用他的目的是可以让我们不去管复杂的协议,只要简单的表达我们要做的事情,让Socket去加工数据。