Python_Socket(套接字)

相关含义:

TCP是面向连接的通信,所以在通信之前,客户端与服务器端必须通过三次握手建立连接,然后在通信完毕,还要通过四次挥手断开连接。

三次握手建立连接:server端调用socket(),bind(),listen()创建监听套接字并完成初始化,然后调用accept()阻塞式等待客户连接。客户端创建一个套接字初始化后,调用connect连接server,连接过程:调用connect()发出SYN段并阻塞等待服务器应答(client:我想要连接你),服务器应答一个SYN-ACK(server:好,我准备好了,你连接吧),客户端收到从connect()返回,同时应答一个ACK给server(client:太好了,我连接好了),服务器收到ACK,从accept返回。

数据传输过程:建立连接后,TCP可以提供全双工的通信,server先读再写,client先写在读,用read()和write()阻塞式的等待一个写一个读。一直循环下去。

四次挥手关闭连接:假设Client端发起中断连接请求,也就是发送FIN报文。Server端接到FIN报文后,意思是说"Client端没有数据要发给你了",但是如果你还有数据没有发送完成,则不必急着关闭Socket,可以继续发送数据。所以你先发送ACK"告诉Client端,你的请求我收到了,但是我还没准备好,请继续你等我的消息"。这个时候Client端就进入FIN_WAIT状态,继续等待Server端的FIN报文。当Server端确定数据已发送完成,则向Client端发送FIN报文,"告诉Client端,好了,我这边数据发完了,准备好关闭连接了"Client端收到FIN报文后,"就知道可以关闭连接了,但是他还是不相信网络,怕Server端不知道要关闭,所以发送ACK后进入TIME_WAIT状态,如果Server端没有收到ACK则可以重传。Server端收到ACK后,"就知道可以断开连接了"Client端等待了2MSL后依然没有收到回复,则证明Server端已正常关闭,那好,我Client端也可以关闭连接了。OkTCP连接就这样关闭了!如果一方调用shutdown()则连接处于半关闭状态,仍可接受对方的数据。

相关函数

1.创建套接字

server = socket.socket()

domain:地址类型,ipv4ipv6unix的地址类型分别定义为常数AF_INETAF_INET6AF_UNIX.

type:socket传输类型,tcp通信是面向字节流的,所以为SOCK_STREAM

在网络通信时,我们的数据要从本主机通过网络发送到对端主机,数据在内存中存放的形式有大端或者小端两种形式,所以在向网络中传输数据是,网络就要按照一定的规定收发数据。TCP/IP协议规定,网络字节流应按照大端字节流,即低地址高字节。

网络数据流的地址规定:先发出的数据是低地址,后发出的数据是高低址,因为网络字节流为大端,也就是先发送数据的高位字节,在发送低位字节。

3.将套接字与socket结构体绑定,socket结构体会指定ip,端口号,还有地址类型

address = ("localhost", 6666)

server.bind(address)

一般服务器的端口号和ip是绑定的,众所周知的,而客户端的端口号可以随机分配的

sockfd设置为监听套接字,并通过参数2是指明最多可以监听多少个套接字。

4.server.listen(5)(两个参数)

监听套接字的作用:server服务器启动后,会源源不断的有客户端来连接,这时候就需要一个监听套接字,来把一个个来访的socket按顺序存起来,并按顺序交给accept的去处理并返回newsockfd去收发数据,这样做可以保证在server满负荷的处理其他socket时,其他客户端要访问服务器时,可以通过监听队列等待一会,有其他客户端断开连接了,他就可以连接了。

5. conn, addr = server.accept()

阻塞式等待客户端连接,监听套接字一直在监听是否有新连接到来连接,如果有链接则接受对方连接,连接之后由返回值new_sock收发数据

6.new_sock(1) = accept(监听套接字(2)struct socket_in输出型参数(3),输入输出型参数(4))

当客户端向服务器端返回ACK时候,会返回一个新的socket对象,数据的传输会在这个新返回的对象中进行操作,之前定义的socket对象会继续监听其它访问该服务器的客户端

7. client.connect(address)

client不需要被别人连接,只需要连接别人,所以使用connect来连接服务器

补充:

存在问题:但是在运行的时候发现一个问题,server启动后,然后启动client建立连接后,然后直接ctrl+c终止掉server,无法立即重启,必须等待半分钟才能重新启动

原因:这是因为在四次挥手断开连接时,主动断开的一方会进入TIME_WAIT状态,这是server还没有完全断开连接,还占着8080号端口,所以再次启动时创建监听套接字就无法在绑定上8080号端口。

但是这是不合理的,因为在实际生活中,服务器一旦挂了,不能立即重启,可能会影响许多客户的体验,会造成很大的损失,那么如何解决。

解决:解决这个问题的方法是使用setsockopt()设置socket描述符的,设置选项SO_REUSEADDR1,表示允许创建端口号相同但IP地址不同的多个socket描述符。在server代码的socket()bind()之间插入。

EG1

注意:12tcp的例子,三次握手建立连接,四次挥手关闭连接

1.客户端传输一个,服务器端接受接受依次数据,并且返回

         服务器端

#无限接受客户端的连接请求,一个客户端可接受1次发送的信息,无挂起
import socket

address = ("localhost", 6666)
server = socket.socket()
server.bind(address)
server.listen()
conn, addr = server.accept()
data = conn.recv(1024)
print("data", data)
conn.send(b"000000099999999")
server.close()

客户端

#一个客户端可发送1次信息给服务器
import socket

address = ("localhost",6666)
client = socket.socket()
client.connect(address)
name = input(">>").strip()
print(len(name))
client.send(name.encode('utf-8'))
data = client.recv(1024)
print("data:", data)
client.close()

EG2:客户端发送四次信息给服务器端,无限接受客户端的连接请求,但是一个客户端可接受4次发送的信息,最高挂起5

服务器端

import socket

address = ("localhost", 6666)
server = socket.socket()
server.bind(address)
server.listen(5)
while True:
	conn, addr = server.accept()
	count = 1
	while count < 4:
		data = conn.recv(1024)
		print("data",data)
		print(len(data))
		count += 1
	print("*******")
	conn.send(b"132456798")
server.close()

客户端

import socket, time

address = ("localhost",6666)
client = socket.socket()
client.connect(address)
count = 1
while count <4:
	if count == 4:
		break
	name = input(">>").strip()
	print(len(name))
	client.send(name.encode('utf-8'))
	count += 1
print("len(name) == 0")
data = client.recv(1024)
print("data:", data)
client.close()

EG3:udpServer的代码

服务器

import socket 
 
HOST='192.168.0.37' 
PORT=50001 
BUFFER=4096 
sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) 
sock.bind((HOST,PORT)) 
#sock.listen(0) 
print('tcpServer listen at: %s:%s\n\r' %(HOST,PORT)) 
while True: 
  #client_sock,client_addr=sock.accept() 
  #print('%s:%s connect' %client_addr) 
  while True: 
    recv,client_addr=sock.recvfrom(BUFFER) 
    if not recv: 
       
      break 
    print('[Client %s:%s said]:%s' % (client_addr[0],client_addr[1],recv)) 
    sock.sendto('tcpServer has received your message',client_addr) 
sock.close()

客户端

import socket 
 
HOST='192.168.0.37' 
PORT=50001 
BUFFER=4096 
 
sock=socket.socket(socket.AF_INET,socket.SOCK_DGRAM) 
sock.connect((HOST,PORT)) 
sock.send('hello, tcpServer!') 
recv=sock.recv(BUFFER) 
print('[tcpServer said]: %s' % recv) 
sock.close()

补充:你会发现由于udp是非连接的,不需要三次握手,所以不需要进行listen,也不需要accept,直接通信就可以了。还有就是初始化socket的时候,通过指定socket.SOCK_DGRAM来实现初始化的socket是基于udp协议的。如果初始化的是udp协议的socket,就不需要listen,也不存在accept,双方通信的同时指明对方的地址和端口就可以了。

 

详细:https://blog.csdn.net/dream_1996/article/details/77924466

深度理解:https://blog.csdn.net/gocy015/article/details/45055589

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值