一、UDP套接字编程
采用python的socket库作为编程方式
1.1socket库的函数
函数名 | 参数 | 作用 |
---|---|---|
gethostname() | 无参数 | 返回主机名称 |
gethostbyname(‘主机名’) | 主机名 | 将主机名转换为ipv4地址 |
gethostbyname_ex(‘主机名’) | 主机名 | 返回主机名、主机别名列表、主机IP地址列表 |
gethostbyaddr(‘ip地址’) | ip地址 | 返回三元组(主机名,主机别名列表,主机IP地址列表) |
getservbyport(‘端口号’,‘协议名’) | 端口号,协议名 | 返回该端口号的服务名,仅限于本机 |
getservbyname(‘服务名’,‘协议名’) | 服务名,协议名 | 返回该服务所在的端口号 |
getsockname() | 无参数 | 返回自己捆绑的IPv4地址 |
getaddrinfo(‘主机号’,‘端口号’) | 主机名,端口号 | 返回五元组,用于获取主机名的地址信息,兼容IPv4和IPv6 |
socket(family,type) | family socket.AF_INET(IPv4) socket.AF_INET6(IPv6) socket.AF_UNIX(同一台机器之间的通信) socket.SOCK_STREAM(流式for TCP) socket.SOCK_DGRAM(报文式数据包for UDP) socket.SOCK_RAW(原始套接字,普通的套接字无法处理ICMP,IGMP等报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头) socket.SOCK_SEQPACKET(可靠的连续数据包格式) | 创建一个已知的IP地址和协议类型的连接 |
connect((host,port)) | (主机名,端口号) | 与此主机的端口进行远程连接,注意括号 |
gettimeout() | 无参数 | 返回socket超时时间,单位为s |
settimeout(timeout) | 超时时间 | 设置socket超时时间 |
1.2socket库的方法
1.2.1connect(address):
链接到一个address对应的远程socket。如果连接被中断,这个方法会等待直到连接完成,或者抛出一个socket.timeout错误。
1.2.2accept():
接受一个连接,但是前提是socket必须已经绑定了一个地址,在等待建立连接。在默认情况下,socket是阻塞式的,意思就是socket的方法的调用在任务完成之前是不会返回的。
调用accept方法时,socket会进入“waiting”状态。客户请求连接时方法建立连接并返回服务器。accept方法返回的是一个含有两个元素的元组(connection,address)。第一个元素connection是新的socket对象,服务器必须通过他与客户通信;第二个元素address是客户的IP地址。
1.2.3recv(bufsize):
从socket接收数据,注意是byte类型,bufsize指定一次最多接受的数据大小。
1.2.4recvfrom(bufsize):
与上一个recv方法的区别为返回值除了数据还有发送数据的地址,返回值是一个数据,地址对(data,address)。
1.2.5send(bytes):
发送数据到socket,前提是已经链接到远程socket,返回值是发送数据的量,检查数据是否发送完是应用的责任。
1.2.6sendto(bytes,flags,address):
基本与socket.send()相同
1.2.7close()
关闭连接,当socket.close()执行的时候,与这个链接相关的底层操作也会关闭(如文件描述符),一旦关闭,再对相关的文件对象操作都会失败。
1.2.8bind(address):
将socket对象绑定到一个地址,但是这个地址必须是没有被占用的,否则会连接失败。这里的address一般是一个(ip,port)对
1.2.9listen([backlog]):
监听,使得服务器能接受服务端链接,如果backlog指定了(最少是0,如果比0小,系统默认改成0),限制可以连接的数量,如果没有指定,将指派一个默认的合理值。
1.3客户端代码
from socket import *
serverName = '192.168.100.129'
serverPort = 12000
# AF_UNIX AF_INEF AF_INEF6
# SOCK_STREAM SOCK_DGRAM SOCK_RAW SOCK_SEQPACKET
clientSocket = socket(AF_INET,SOCK_DGRAM)
MESSAGE = input("Input lowercase sentence:")
clientSocket.sendto(MESSAGE.encode(),(serverName,serverPort))
message,address=clientSocket.recvfrom(2048)
print(message.decode())
1.4 服务端代码
from socket import *
serverPort=12000
serverSocket=socket(AF_INET,SOCK_DGRAM)
serverSocket.bind(('',serverPort))
print("THE SERVER IS OK !")
while 1:
message,address=serverSocket.recvfrom(2048)
print(address[0])
serverSocket.sendto(message.decode().upper().encode(),address)
print(message.decode())
print(address)
编程的时候犯了一个错误,就是服务器socket.recvfrom返回的address中是一个包含源IP地址和发送方端口号的,在客户端程序中的clientSocket.sendto(MESSAGE.encode(),(serverName,serverPort))这一行代码中serverPort指的是服务器的端口,而我在服务器代码中还用这个serverPort作为serverSocket.sendto()的端口参数,程序不会报错,但是我在客户端就收不到回传的信息了,因为我的客户端端口并不是serverPort。而且serverSocket.sendto()他需要两个参数,第一个参数是发送的内容,第二个参数是一个目的地址IP和目的地址端口号组成的元组,不能分开。
二、TCP套接字编程
2.1客户端代码
from socket import *
serverName = '192.168.100.129'
serverPort = 12000
# AF_UNIX AF_INEF AF_INEF6
# SOCK_STREAM SOCK_DGRAM SOCK_RAW SOCK_SEQPACKET
clientSocket = socket(AF_INET,SOCK_STREAM)
clientSocket.connect((serverName,serverPort))
MESSAGE = input("Input lowercase sentence:")
clientSocket.sendto(MESSAGE.encode(),(serverName,serverPort))
message,address=clientSocket.recvfrom(2048)
print(message.decode())
2.2服务端代码
from socket import *
serverPort=12000
serverSocket=socket(AF_INET,SOCK_STREAM)
serverSocket.bind(('',serverPort))
serverSocket.listen(1)
print("THE SERVER IS OK !")
while 1:
connectionSocket,address=serverSocket.accept()
message=connectionSocket.recv(2048)
connectionSocket.sendto(message.decode().upper().encode(),address)
print(message.decode())
print(address)
主要注意量两点。第一点是TCP需要经历三次握手,在python的socket包中包装为socket.connect()和socket.accept()方法。第二个就是要注意服务器在建立连接之前生成的是名为serverSocket的TCP欢迎套接字对象,但是这个对象只负责TCP三次握手,握手成功后会交由名为connectionSocket的连接套接字进行处理。欢迎套接字是所有要与服务器通信的客户的起始接触点,连接套接字是随后为每个客户通信而生成的套接字。