一、网络协议

客户端/服务器架构

1.硬件C/S架构(打印机)

2.软件C/S架构(互联网中处处是C/S架构):B/S架构也是C/S架构的一种,B/S是浏览器/服务器

C/S架构与socket的关系:我们用socket就是为了完成C/S架构的开发

osi七层

引子:

须知一个完整的计算机系统是由硬件、操作系统、应用软件三者组成,具备了这三个条件,一台计算机系统就可以自己跟自己玩了(打个单机游戏,玩个扫雷啥的)

如果你要跟别人一起玩,那你就需要上网了,什么是互联网?

互联网的核心就是由一堆协议组成,协议就是标准,比如全世界人通信的标准是英语

如果把计算机比作人,互联网协议就是计算机界的英语。所有的计算机都学会了互联网协议,那所有的计算机都就可以按照统一的标准去收发信息从而完成通信了。

人们按照分工不同把互联网协议从逻辑上划分了层级,

详见网络通信原理:http://www.cnblogs.com/linhaifeng/articles/5937962.html

为何学习socket一定要先学习互联网协议?

  首先C/S架构是基于网络通信的

  然后网络的核心即一堆网络协议,也就是协议标准。如果你想开发一款基于网络通信的软件,就必须遵循这些标准

socke层

二、socket是什么?

socket是应用层与TCP/IP协议通信的中间软件抽象层,它是一组接口。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/IP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

所以,我们无需深入理解tcp/udp协议,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的。

三、基于TCP协议的socket

套接字的分类:

  基于文件类型的套接字家族:AF_UNIX(在Unix系统上,一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程同时运行在同一机器,可以通过访问同一个文件系统间接完成通信)

  基于网络类型的套接字家族:AF_INET  (python支持很多种地址家族,但是由于我们只关心网络编程,所以大部分时候我们只使用AF_INET)

套接字的工作流程:

下面我们举个打电话的小例子来说明一下

如果你要给你的一个朋友打电话,先拨号,朋友听到电话铃声后提起电话,这时你和你的朋友就建立起了连接,就可以讲话了。等交流结束,挂断电话结束此次交谈。 生活中的场景就解释了这工作原理。

(如果你去一家餐馆吃饭,假设哪里的老板就是服务端,而你自己就是客户端,当你去吃饭的时候,你肯定的知道那个餐馆,也就是服务端的地址吧,但是对于你自己来说,餐馆的老板不需要知道你的地址吧)

套接字函数

#服务端套接字函数
1 import socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# .服务端套接字函数
phone.bind('主机ip地址',端口号) #绑定到(主机,端口号)套接字
phone.listen() #开始TCP监听
phone.accept() #被动接受TCP客户的连接,等待连接的到来
 .客户端套接字函数
import socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#买手机
phone.connect() #主动连接服务端的ip和端口
phone.connect_ex() #connect()函数的扩展版本,出错的时候返回错码,而不是抛出异常
 .服务端和客户端的公共用途的嵌套字函数
phone.recv() #接受TCP数据
phone.send() #发送TCP数据
phone.recvfrom() #接受UDP数据
phone.sendto() #发送UDP数据
phone.getpeername() #接收到当前套接字远端的地址
phone.getsockname() #返回指定套接字的参数
phone.setsockopt() #设置指定套接字的参数
phone.close() #关闭套接字
 面向锁的套接字方法
phone.setblocking() #设置套接字的阻塞与非阻塞模式
phone.settimeout() #设置阻塞套接字操作的超时时间
phone.gettimeout() #得到阻塞套接字操作的超时时间
 面向文件的套接字函数
phone.fileno() # 套接字的文件描述符
phone.makefile() #创建一个与该套接字相关的文件

sk = socket.socket(socket.AF_INET,socket.SOCK_STREAM,0)

参数一:地址簇

  socket.AF_INET IPv4(默认)
  socket.AF_INET6 IPv6

  socket.AF_UNIX 只能够用于单一的Unix系统进程间通信

参数二:类型

  socket.SOCK_STREAM  流式socket , for TCP (默认)
  socket.SOCK_DGRAM   数据报式socket , for UDP

  socket.SOCK_RAW 原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。
  socket.SOCK_RDM 是一种可靠的UDP形式,即保证交付数据报但不保证顺序。SOCK_RAM用来提供对原始协议的低级访问,在需要执行某些特殊操作时使用,如发送ICMP报文。SOCK_RAM通常仅限于高级用户或管理员运行的程序使用。
  socket.SOCK_SEQPACKET 可靠的连续数据包服务

参数三:协议

  0  (默认)与特定的地址家族相关的协议,如果是 0 ,则系统就会根据地址格式和套接类别,自动选择一个合适的协议

sk.bind(address)

  s.bind(address) 将套接字绑定到地址。address地址的格式取决于地址族。在AF_INET下,以元组(host,port)的形式表示地址。

sk.listen(backlog)

  开始监听传入连接。backlog指定在拒绝连接之前,可以挂起的最大连接数量。

      backlog等于5,表示内核已经接到了连接请求,但服务器还没有调用accept进行处理的连接个数最大为5
这个值不能无限大,因为要在内核中维护连接队列 sk.setblocking(bool)   是否阻塞(默认True),如果设置False,那么accept和recv时一旦无数据,则报错。 sk.accept()   接受连接并返回(conn,address),其中conn是新的套接字对象,可以用来接收和发送数据。address是连接客户端的地址。   接收TCP 客户的连接(阻塞式)等待连接的到来 sk.connect(address)   连接到address处的套接字。一般,address的格式为元组(hostname,port),如果连接出错,返回socket.error错误。 sk.connect_ex(address)   同上,只不过会有返回值,连接成功时返回 ,连接失败时候返回编码,例如: sk.close()   关闭套接字 sk.recv(bufsize[,flag])   接受套接字的数据。数据以字符串形式返回,bufsize指定最多可以接收的数量。flag提供有关消息的其他信息,通常可以忽略。 sk.recvfrom(bufsize[.flag])   与recv()类似,但返回值是(data,address)。其中data是包含接收数据的字符串,address是发送数据的套接字地址。 sk.send(string[,flag])   将string中的数据发送到连接的套接字。返回值是要发送的字节数量,该数量可能小于string的字节大小。即:可能未将指定内容全部发送。 sk.sendall(string[,flag])   将string中的数据发送到连接的套接字,但在返回之前会尝试发送所有数据。成功返回None,失败则抛出异常。 内部通过递归调用send,将所有内容发送出去。 sk.sendto(string[,flag],address)   将数据发送到套接字,address是形式为(ipaddr,port)的元组,指定远程地址。返回值是发送的字节数。该函数主要用于UDP协议。 sk.settimeout(timeout)   设置套接字操作的超时期,timeout是一个浮点数,单位是秒。值为None表示没有超时期。一般,超时期应该在刚创建套接字时设置,
因为它们可能用于连接的操作(如 client 连接最多等待5s ) sk.getpeername()   返回连接套接字的远程地址。返回值通常是元组(ipaddr,port)。 sk.getsockname()   返回套接字自己的地址。通常是一个元组(ipaddr,port) sk.fileno()   套接字的文件描述符

TCP是基于链接的,必须先启动服务器,然后再启动客户端去链接服务端

服务端:

import socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#买手机
phone.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,) #可以多次启动
#执行多次的时候会报错,那么怎么办呢、?就在绑卡前面加上上面那句setsockopt方法就ok了
phone.bind(('192.168.20.44',))#绑定手机卡(ip,端口)
# 端口号在1024以前的是系统用的,1024以后的都是你自己写的程序去定义的端口 print('starting run......')
phone.listen() #开机 5代表的是最多挂起5个,也可以好多个
while True: #链接循环
coon,client_addr=phone.accept()#等待接电话,(coon是建立的链接,客户端的ip和端口号组成的元组)
print(coon,client_addr) #收发消息
while True: #通信循环
try: #如果不加try...except ,就会报错,因为它不知道你什么时候断开链接的,服务器还以为你在运行
data = coon.recv() #收了1024个字节的消息
print('client data 收到消息:%s'%data.decode('utf-8'))
coon.send(data.upper()) #发消息
except Exception: #因为你不知道客户端什么时候断开链接,
break
coon.close() #挂电话
phone.close() #关机 # 处理逻辑错误的两种方式:
# if 判断
# try...except 异常处理
# 异常处理
# 当你知道直接错误的条件时就用if判断了
# 当程序错误一定发生,但是你又预知不了它出错的条件是什么的时候,就用try...except 服务端

客户端:

import socket
phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#买手机
phone.connect(('192.168.20.44',)) #直接连接服务端的ip和端口 # 发收消息
while True:
msg = input('>>:').strip() #用户输入
if not msg:continue #如果为空就继续输
phone.send(msg.encode('utf-8')) # 发送你输入的消息
# phone.send('hello'.encode('utf-8'))
data = phone.recv() #在接收一下
print('server back res服务端返回结果:>>%s'%data.decode('utf-8')) phone.close() 客户端

注意:

如果你在重启服务端的时候可能遇到这样的问题:

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

 #加入一条socket配置,重用ip和端口

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

四、基于TCP协议模拟ssh远程执行命令

 import socket
import subprocess
phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)#买手机
phone.bind(('192.168.20.44',))#绑定手机卡
phone.listen()#阻塞的最大个数
print('starting....')
while True:
conn,addr=phone.accept()#等待连接
print(addr,conn)
while True:
cmd=conn.recv()#接收的最大值
# if not cmd :break
print('接收的是:%s'%cmd.decode('utf-8'))
#处理过程
res=subprocess.Popen(cmd.decode('utf-8'),shell=True, #Popen是执行命令的方法
stdout=subprocess.PIPE,
stderr=subprocess.PIPE )
stdout=res.stdout.read()
stuerr=res.stderr.read()
conn.send(stdout+stuerr)
conn.close()
phone.close() 服务端

服务端

 import socket

 phone=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
phone.connect(('192.168.20.44',))#绑定端口
while True:
cmd=input('>>请输入').strip()
if not cmd: continue
phone.send(cmd.encode('utf-8'))
data=phone.recv()
print('返回的是%s'%data.decode('gbk'))
phone.close()

客户端

五、基于UDP协议的socket

from socket import *
udp_server = socket(AF_INET,SOCK_DGRAM)
udp_server.bind(('127.0.0.1',)) #绑定
while True:#通讯循环
msg,client_addr= udp_server.recvfrom()
print('收到的消息是:%s'%msg.decode('utf-8'))
udp_server.sendto(msg.upper(),client_addr)
udp_server.close()

服务端

# udp 无链接,所以不需要连接
from socket import *
udp_client = socket(AF_INET,SOCK_DGRAM) while True:
msg = input('>>:').strip()
udp_client.sendto(msg.encode('utf-8'),('127.0.0.1',))
res,sever_addr = udp_client.recvfrom()
print('返回的结果是:%s'%res.decode('utf-8'))
udp_client.close()

客户端

基于UDP协议的socket的应用(模拟QQ聊天)

from socket import *
udp_server= socket(AF_INET,SOCK_DGRAM)
udp_server.setsockopt(SOL_SOCKET,SO_REUSEADDR,)
udp_server.bind(('127.0.0.1',))
print('start running...') while True:
qq_msg,addr = udp_server.recvfrom()
print('来自[%s:%s]的一条消息:\033[44m%s\033[0m'%(addr[],addr[],qq_msg.decode('utf-8')))
back_msg = input('回复消息:>>').strip()
udp_server.sendto(back_msg.encode('utf-8'),addr)
udp_server.close()
from socket import *
udp_client = socket(AF_INET,SOCK_DGRAM)
qq_name_dic = {
'阿涵':('127.0.0.1',),
'阿水':('127.0.0.1',),
'刘德华':('127.0.0.1',),
'孙龙':('127.0.0.1',)
}
while True:
qq_name = input('请输入聊天对象:>>').strip()
if qq_name not in qq_name_dic: continue
while True:
msg = input('请输入消息,回车发送:').strip()
if msg=='quit':break
if not msg or not qq_name or qq_name not in qq_name_dic:continue
udp_client.sendto(msg.encode('utf-8'),qq_name_dic[qq_name])
back_msg,addr = udp_client.recvfrom()
print('来自[%s:%s]的一条消息:\033[41m%s\033[0m'%(addr[],addr[],back_msg.decode('utf-8')))
udp_client.close()

运行结果:

subprocess子进程模块

import subprocess
#Popen方法是用来执行系统命令的,直接把结果打印到终端了
res =subprocess.Popen(r'dir',shell=True,
#r'dsfsdfr',shell=True,
# stdin= #标准输入(不常用)
stdout=subprocess.PIPE,#stdout 标准输出
stderr=subprocess.PIPE) #stderr 标准错误
# 拿到的是‘gbk’编码的结果,
# 这个命令可能有正确结果,也可能有错误结果
print(res.stdout.read().decode('gbk'))
print('========')
print(res.stdout.read().decode('gbk')) #说明只能读一次
print(res.stderr.read().decode('gbk')) #如果是错误的就会提示

struct模块

 #该模块可以把一个类型,如数字,转成固定长度的bytes类型
import struct
# res = struct.pack('i',)
# print(res,len(res),type(res)) #长度是4 res2 = struct.pack('i',)
print(res2,len(res2),type(res2)) #长度也是4 unpack_res =struct.unpack('i',res2)
print(unpack_res) #(,)
# print(unpack_res[]) #

网络(socket)编程的更多相关文章

  1. UDP协议网络Socket编程(java实现C/S通信案例)

    我的博客园:https://www.cnblogs.com/chenzhenhong/p/13825286.html 我的CSDN博客:https://blog.csdn.net/Charzous/a ...

  2. Java:基于TCP协议网络socket编程(实现C/S通信)

    目录 一.前言:TCP原理简介 二.Socket编程通信 三.TCP服务器端(具体代码) 四.TCP客户端(具体代码) 五.通信效果演示 六."创意"机器人:价值一个亿的AI核心代 ...

  3. java多线程实现TCP网络Socket编程(C/S通信)

    目录 开篇必知必会 一.多线程技术 二.实现多线程接收 1.单线程版本 2.多线程版本 三.多线程与进程的关系 四.客户端界面完整代码 五.多线程通信对比 最后 开篇必知必会 在前一篇<Java ...

  4. 【java】网络socket编程简单示例

    package 网络编程; import java.io.IOException; import java.io.PrintStream; import java.net.ServerSocket; ...

  5. 网络Socket编程及实例

    1 TCP和UDP介绍 在介绍TCP和UDP之前,有必要先介绍下网络体系结构的各个层次. 1.1  网络体系结构 协议:控制网络中信息的发送和接收.定义了通信实体之间交换报文的格式和次序,以及在报文传 ...

  6. C#网络Socket编程

    1.什么是Socket Sockets 是一种进程通信机制,是一个通信链的句柄(其实就是两个程序通信用的) 2.分类 流式套接字(SOCK_STREAM):提供了一种可靠的.面向连接的双向数据传输服务 ...

  7. python之网络socket编程

    一.网络协议 客户端/服务器架构 1.硬件C/S架构(打印机) 2.软件C/S架构(互联网中处处是C/S架构):B/S架构也是C/S架构的一种,B/S是浏览器/服务器 C/S架构与socket的关系: ...

  8. Linux下网络socket编程——实现服务器(select)与多个客户端通信

    一.关于socket通信 服务器端工作流程: 调用 socket() 函数创建套接字 用 bind() 函数将创建的套接字与服务端IP地址绑定 调用listen()函数监听socket() 函数创建的 ...

  9. Beej网络socket编程指南

    bind()函数 一旦你有一个套接字,你可能要将套接字和机器上的一定的端口关联 起来.(如果你想用listen()来侦听一定端口的数据,这是必要一步--MUD 告 诉你说用命令 "telne ...

  10. 【VS开发】网络SOCKET编程INADDR_ANY选项

    INADDR_ANY选项 网络编程中常用到bind函数,需要绑定IP地址,这时可以设置INADDR_ANY INADDR_ANY就是指定地址为0.0.0.0的地址,这个地址事实上表示不确定地址,或&q ...

随机推荐

  1. java 软件安装

    myeclipse 2014 安装 https://www.cnblogs.com/plus301/articles/5633540.html

  2. sql语句格式化数字(前面补0)

    将一个数字例如33,或1使用t-sql语句转换成033或001 以下是详细分析: .,)得到1000 . as varchar) 将1000转换类型 .,) 从右边取3个字符得到033 将1格式化同上 ...

  3. python之SQLAlchemy组件

    介绍 SQLAlchemy 是一个 ORM 框架,可以帮助我们使用面向对象的方式快速实现数据库操作. 组成部分: Engine,框架的引擎 Connection Pooling ,数据库连接池 Dia ...

  4. 小程序 navigator 无法跳转 tabBar上的页面

    解决方法一: navigator 的 open-type 设置为 switchTab 解决方法二: 使用 wx.switchTab({ url: ‘../cart/index’ }) 进行跳转

  5. CurrentHashMap、HashMap、HashTable的区别

    HashTable 底层数组+链表实现,无论key还是value都不能为null,线程安全,实现线程安全的方式是在修改数据时锁住整个HashTable,效率低,ConcurrentHashMap做了相 ...

  6. 逆向 AWS API 设计

    由于AWS并没有像Google一样公开出一份API Design Guide,所以只能根据 API 的模样去逆向工程最初的设计考量.既然上一篇介绍了很多 REST 的缺陷,那么这里也会介绍一下 AWS ...

  7. Docker 基础 (二)

    网络管理 容器网络模式 Docker支持5种网络模式 bridge 默认网络,Docker启动后默认创建一个docker0网桥,默认创建的容器也是添加到这个网桥中 host  容器不会获得一个独立的n ...

  8. h5 input 的验证

    <input type="text" id="a" required/> <input type="text" id=&q ...

  9. Linux 创建自定义命令

    Linux 创建自定义命令 Linux 可以创建自定义使用命令 这里我们采取使用“alias”命令.这里我们首先了解两个文件,通过这两个文件我们可以根据环境配置相应的自定义命令. 该文件内创建的自定义 ...

  10. vue中父子组件之间的传值、非父子组件之间的传值

    在Vue实例中每个组件之间都是相互独立的,都有自己的作用域,所以组件之间是不能直接获取数据.在项目开发中一个组件可能需要获取另一个组件的值,我们可以通过其他方法间接的获取.所以,获取的方法有以下几种: ...