Python——socket网络编程


socket编程

应用程序架构的分类:
C/S 客户端/服务器
B/S 浏览器/服务器

C/S架构和B/S架构的区别
C/S架构的优点:

  1. 个性化更容易实现
  2. 更安全
  3. 占用网络资源少

B/S结构的优点:

  1. 更新方便
  2. 使用方便
  3. 几乎不占用本地资源

@


前言

  1. C/S架构与socket的关系:我们学习socket的目的是为了实现C/S架构;
  2. 还有一种架构是基于socket来实现的,写一个服务器端软件,客户端软件不用写,客户端就是浏览器(浏览器的本质是套接字写的客户端);

一、什么是网络

在一台计算机上由硬件,硬件之上是操作系统,操作系统之上是应用程序(为客户端软件)
在另一台计算机上硬件之上是操作系统,在操作系统之上是应用程序(服务器端软件),这两台计算机是基于网络通信的

什么是网络:

  1. 物理介质:网线;

  2. 协议(就像两个人打电话,物理介质是电话线,两个人处于不同的地区,两个人说的话彼此听不懂,怎么解决?定义一个标准,称之为互联网协议)

总结:
在计算机通信中,计算机除了物理链接介质外,想要通信,计算机必须有一个标准,称之为互联网协议
即(网络就是物理链接介质和互联网协议)
接下来写一个客户端软件和服务器软件,两者之间基于网络通信(网络包含:物理链接介质,(是网络工程师干的);我们要干的事情是客户端软件给服务器软件发消息,必须让服务器软件和客户端软件遵循互联网协议)

二、互联网协议按照功能不同分为osi七层或tcp/ip五层或tcp/ip四层

(我们主要学习五层)
在这里插入图片描述
数据的封装与解封装详情见:https://www.cnblogs.com/huoxc/p/13611394.html
在网络层:IP协议
传输层:TCP/UDP协议
数据链路层:以太网协议
应用层:http协议,DNS协议,ftp协议

三:什么是socket

在这里插入图片描述

  • 应用层软件是我们写的,想要发数据传给传输层,应用层规定好的协议其他层控制不了,是客户端和服务器端规定好的,没人管,但是往外发数据,必须按照UDP或者TCP协议;如果用TCP协议必须按照TCP的协议来,必须把TCP协议学会以及IP协议等;那么效率比较低;
  • 怎么解决:对于这种情况把TCP,IP协议等疯涨起来,对于我来说TCP以下我都不打交道了,我只跟应用层打交道,socket就是在应用层和传输层之间的,传输层一下的都封装到socket中,对于我来遵循套接字的标准,那么我们基于socket来开发的;针对这种协议,python中有一个模块,socket

四:套接字的工作流程

(TCP协议的套接字)
在这里插入图片描述
首先:
1:服务器端先拿到一个套接字对象
2:接下来绑定IP和端口
3:接下来监听
4:等待客户端的链接
其次:
1:客户端拿到一个套接字对象
2:客户端和服务器建立链接(即3次握手,双向链接)
3:客户端向服务器发送数据
4:服务器对请求进行处理,处理完毕后再响应给客户端
5:关闭客户端

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

服务端

import socket
ip_port=('127.0.0.1',9000)  
BUFSIZE=1024                #收发消息的大小
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #其中socket.AF_INET:基于网络通信的套接字;
#socket.SOCK_STREAM:流式套接字又称之为TCP协议
s.bind(ip_port) #绑定的IP和端口(IP为服务器的IP)
s.listen(5)     #监听,如打电话,在跟别人通话的时候,还可能来多个电话,这个链接处于挂起的状态,如果当前的链接以结束,立马就跟刚刚挂起的电话通信,在TCP协议中被称之为连接池的大小


conn,addr=s.accept()            #手机接电话(accept是一种阻塞操作,什么时候变成非阻塞状态,有人链接就会变成非阻塞状态)返回一个链接对象,客户端的IP地址和端口
# print(conn)
# print(addr)
print('接到来自%s的电话' %addr[0])

msg=conn.recv(BUFSIZE)             #接受消息
print(msg,type(msg))

conn.send(msg.upper())          #发消息,

conn.close()                    #关闭链接

s.close()                       #关闭套接字

客户端:

import socket
ip_port=('127.0.0.1',9000)
BUFSIZE=1024
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

s.connect(ip_port)           #建立链接

s.send('lheiehei '.encode('utf-8'))         #发消息(基于网络发送的是二进制格式)

feedback=s.recv(BUFSIZE)                           #收消息
print(feedback.decode('utf-8'))

s.close()                                       #关闭链接

有可能还会出现以下的情况:
在这里插入图片描述
怎么解决:
这个是由于你的服务端仍然存在四次挥手的time_wait状态在占用地址(如果不懂,请深入研究1.tcp三次握手,四次挥手 2.syn洪水攻击 3.服务器高并发情况下会有大量的time_wait状态的优化方法)

#加入一条socket配置,重用ip和端口  或者重新写一个非著名端口

phone=socket(AF_INET,SOCK_STREAM)
phone.setsockopt(SOL_SOCKET,SO_REUSEADDR,1) #就是它,在bind前加
phone.bind(('127.0.0.1',8080))

如果在Linux中解决方法如下:

发现系统存在大量TIME_WAIT状态的连接,通过调整linux内核参数解决,
vi /etc/sysctl.conf

编辑文件,加入以下内容:
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 30
 
然后执行 /sbin/sysctl -p 让参数生效。
 
net.ipv4.tcp_syncookies = 1 表示开启SYN Cookies。当出现SYN等待队列溢出时,启用cookies来处理,可防范少量SYN攻击,默认为0,表示关闭;

net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;

net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。

net.ipv4.tcp_fin_timeout 修改系統默认的 TIMEOUT 时间

加上链接循环与通信循环:

服务端:
import socket
ip_port=('127.0.0.1',9000)  #电话卡
BUFSIZE=1024                #收发消息的尺寸
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM) #买手机
print(s)      #s是套接字对象:这个套接字对象主要用来建立三次握手
s.bind(ip_port) #手机插卡
s.listen(5)     #手机待机


conn,addr=s.accept()            #手机接电话
# print(conn)
# print(addr)
while True:
	msg=conn.recv(BUFSIZE)             #听消息,听话(这种套接字主要用来收发消息,跟一个客户端收发消息,因为跟一个客户端建号的三次握手)
	conn.send(msg.upper())          #发消息,说话

conn.close()                    #挂电话

s.close()
客户端:
import socket
ip_port=('127.0.0.1',9000)
BUFSIZE=1024
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

s.connect(ip_port)           #拨电话
while True:
	s.send('linhaifeng nb'.encode('utf-8'))         #发消息,说话(只能发送字节类型)
	feedback=s.recv(BUFSIZE)                           #收消息,听话
	print(feedback.decode('utf-8'))

s.close()                                       #挂电话

总结:

服务端套接字有几种形式:

  1. conn这种套接字(这种套接字主要用来收发消息,跟一个客户端收发消息,因为跟一个客户端建号的三次握手)
    如果再换一个客户端链接,就是一个新的conn对象;
  2. 刚开始的时候,拿到的s套接字对象,这个套接字对象主要用来建立三次握手;

如果客户端发的是空,则直接卡住了,在客户端没有卡住,在服务器端卡住了,服务端一直在等着,怎么解决?,在客户端解决;

import socket
ip_port=('127.0.0.1',9000)
BUFSIZE=1024
s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)

s.connect(ip_port)           #拨电话
while True:
	msg=input('>>>:').strip()
	if not msg:continue   #当为空的时候,继续让用户输入
		

s.close()                                       #挂电话

粘包:

须知:只有TCP有粘包现象,UDP永远不会粘包,首先需要掌握一个socket收发消息的原理

在这里插入图片描述

解决粘包:https://www.cnblogs.com/huoxc/p/13999288.html

有志者,事竟成,破釜沉舟,百二秦关终属楚; 苦心人,天不负,卧薪尝胆,三千越甲可吞吴。 想到与得到中间还有两个字——做到。
原文地址:https://www.cnblogs.com/huoxc/p/13606374.html