网络编程:socket 编程

网络编程:socket 编程socket编程-客户端/服务器架构:即C/S架构1,硬件C/S架构(打印机)2,软件C/S架构(web服务)C/S架构与socket的关系:socket就是为了完成C/S架构的开

大家好,又见面了,我是你们的朋友全栈君。

socket 编程

-客户端/服务器架构 :即 C/S架构

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

2, 软件C/S 架构(web服务)

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

 

-osi 七层:

应用层–运输层–网络层–链路层–物理层

<span role="heading" aria-level="2">网络编程:socket 编程

 

 

 socket 抽象层在应用层和运输层之间

 

 

socket概念(socket也是套接字)

socket是应用层和TCP/IP协议中间通信的软件层,它是一组接口,在设计模式中,socket其实就是一个门面模式,它把复杂的TCP/IP协议封装隐藏在socket接口后,让socket去组织数据,以符合指定协议,所以只需遵循socket规定去编程就可以。

套接字分为2种:

-基于文件型的套接字家族 AF_UNIX

用于一台机器的不同程序之间

linux 一切皆文件,基于文件的套接字调用的是底层的文件系统来取数据,2个套接字进程运行在同一个机器,可以通过访问同一个文件系统来间接完成通信

-基于网络类型的套接字家族 AF_INET

用于网络编程

通过网络来实现2个程序通讯

 

socket 基于tcp运行流程图如下:

<span role="heading" aria-level="2">网络编程:socket 编程

  

例如:

服务端:

import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#socket.SOCK_STREAM 是基于流的通讯方式,也就是TCP
#socket.AF_INET 代表是网络嵌套家族类型

phone.bind(('192.168.1.4',8000))
#括号内写IP地址+端口 自己电脑IP是 192.168.1.4 ,监听端口是8000

phone.listen(5) #代表同时可以接5个电话

conn,addr =phone.accept()
msg=conn.recv(1024)# 收消息 ,1024代表可以接收多少字节的信息
print('客户端发来的消息是',msg)
conn.send(msg.upper())#发消息

conn.close()
phone.close()

客户端

import socket

phone = socket.socket(socket.AF_INET,socket.SOCK_STREAM)

phone.connect(('192.168.1.4',8000))

phone.send('hello'.encode('utf-8'))#发消息
data =phone.recv(1024) #收消息
print('收到服务端发来的消息',data)

运行结果是:

服务端:客户端发来的消息是 b’hello’

客户端:收到服务端发来的消息 b’HELLO’

 

socket 底层工作原理

 

<span role="heading" aria-level="2">网络编程:socket 编程

  客户端服务端循环发送接收消息

服务端代码:

from socket import *

ip_import =('192.168.1.3',8000)
back_log = 5
buffer_size = 1024
tcp_server = socket(AF_INET,SOCK_STREAM)
tcp_server.bind(ip_import)
tcp_server.listen(back_log)

conn,add = tcp_server.accept()
print('双向链接是',conn)
print('客户端的地址是',add)

while True:
msg = conn.recv(buffer_size)
print('客户发来的是',msg.decode('utf-8'))
conn.send(msg.upper())

conn.close()
tcp_server.close()

 服务端运行结果是:

双向链接是 <socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=(‘192.168.1.3’, 8000), raddr=(‘192.168.1.3’, 52951)>
客户端地址是 (‘192.168.1.3’, 52951)
客户发来的是 nihao
客户发来的是 hi
客户发来的是 how are you

 

 

 客户端代码:

from socket import *

ip_port=('192.168.1.3',8000)
back_log=5
buffer_size=1024

tcp_client = socket(AF_INET,SOCK_STREAM)
tcp_client.connect(ip_port)

while True:
client_msg = input('>>:').strip()
if not client_msg: continue
tcp_client.send(client_msg.encode('utf-8'))
print('客户端已发送消息')
data = tcp_client.recv(buffer_size)
print('收到服务端发来的消息',data.decode('utf-8'))

 

 客户端结果如下:

>>:nihao
收到服务端发来的消息 NIHAO
>>:hi
收到服务端发来的消息 HI
>>:how are you
收到服务端发来的消息 HOW ARE YOU

 注意:如果输入可以是空格,但不可以是空,是空的话会继续要求输入。

 

 socket 收发消息原理刨析

 

客户端发消息:是从应用程序发送到用户态内存,然后发送到内核态内存然后再通过网卡发出

客户端消息:是从网卡进入内核态内存然后发送到用户态内存

服务端同样如此。

 <span role="heading" aria-level="2">网络编程:socket 编程

 

 服务端多次接收双向连接

windows 系统和linux/mac系统下,socket 不同之处:

断开客户端,windows 系统下服务端会报错,而linux/mac系统下,服务端接收的是空

 windows 系统下:

服务端:from socket import *


ip_port =('192.168.1.3',8001)
back_log = 5
buffer_size = 1024

tcp_server = socket(AF_INET,SOCK_STREAM)
tcp_server.bind(ip_port)
tcp_server.listen(back_log)
while True:
conn,add = tcp_server.accept()
print('接收的链接是',conn)
print('地址是',add)

while True:
print('服务端开始运行了')
try:
data = conn.recv(buffer_size)

print('客户发来的是',data.decode('utf-8'))
conn.send(data.upper())
print('服务端已发送',data.upper())
except Exception:
break
conn.close()
tcp_server.close()

 客户端

from socket import *

ip_port=('192.168.1.3',8001)
back_log=5
buffer_size=1024

tcp_client = socket(AF_INET,SOCK_STREAM)
tcp_client.connect(ip_port)

while True:
client_msg = input('>>:').strip()
if not client_msg: continue
tcp_client.send(client_msg.encode('utf-8'))
print('客户端已发送消息')
data = tcp_client.recv(buffer_size)
print('收到服务端发来的消息',data.decode('utf-8'))

tcp_client.close()

 

linux 系统下:

from socket import *

ip_port =('192.168.1.3',8001)
back_log = 5
buffer_size = 1024

tcp_server = socket(AF_INET,SOCK_STREAM)
tcp_server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
#在bind 之前进行socket 设置,使其不卡在time_wait占用地址

tcp_server.bind(ip_port)
tcp_server.listen(back_log)
while True:
conn,add = tcp_server.accept()
print('接收的链接是',conn)
print('地址是',add)

while True:
print('服务端开始运行了')
try:
data = conn.recv(buffer_size)
if not data: break
print('客户发来的是',data.decode('utf-8'))
conn.send(data.upper())
print('服务端已发送',data.upper())
except Exception:
break

tcp_server.close()

 客户端代码如上,不变。

 

总结:服务端,客户端基本要求:

 <span role="heading" aria-level="2">网络编程:socket 编程

 <span role="heading" aria-level="2">网络编程:socket 编程

 

 基于udp 的套接字

udp没有连接

 服务端

from socket import *
ip_port = ('192.168.1.5',8080)
buffer_size = 1024


udp_server = socket(AF_INET,SOCK_DGRAM) #SOCK_DGRAM 数据报
udp_server.bind(ip_port)


while True:
data,addr = udp_server.recvfrom(buffer_size)
print(data.decode('utf-8'))
udp_server.sendto(data.upper(),addr)
udp_server.close()

 

 客户端

from socket import *
ip_port = ('192.168.1.5',8080)
buffer_size = 1024


udp_client = socket(AF_INET,SOCK_DGRAM) #SOCK_DGRAM 数据报

while True:
msg = input('>>:').strip()
udp_client.sendto(msg.encode('utf-8'),ip_port)

data,addr = udp_client.recvfrom(buffer_size)
print(data.decode('utf-8'))
udp_client.close()

 

运用基于udp的套接字,来制作时间服务器(ntp),代码如下:

时间服务端:

from socket import *
import time
ip_port = ('192.168.1.5',8080)
buffer_size = 1024

time_server = socket(AF_INET,SOCK_DGRAM)
time_server.bind(ip_port)

while True:
data,addr = time_server.recvfrom(buffer_size)
if not data:
fmt = '%Y-%m-%d-%X'
else:
fmt = data.decode('utf-8')

back_time = time.strftime(fmt)
time_server.sendto(back_time.encode('utf-8'),addr)
time_server.close()

时间客户端:

from socket import *
ip_port = ('192.168.1.5',8080)
buffer_size = 1024


time_client = socket(AF_INET,SOCK_DGRAM) #SOCK_DGRAM 数据报

while True:
msg = input('>>:').strip()
time_client.sendto(msg.encode('utf-8'),ip_port)

data,addr = time_client.recvfrom(buffer_size)
print('服务器的标准时间是:',data.decode('utf-8'))
time_client.close()

 recv 在自己这段的缓冲区为空时,会阻塞

recvfrom 在自己这段的缓冲区为空时,就收一个空

<span role="heading" aria-level="2">网络编程:socket 编程 

 

 基于tcp实现远程命令

 subprocess 模块

代码:变量名=subprocess.Popen(命令,shell=True,

            stderr=subprocess.PIPE,

            stdin=subprocess.PIPE,

            stdout=subprocess.PIPE)


 将命令结果封装在管道(PIPE)中,stdin 代表输入,stdout 代表输出,stderr代表报错。

想要读取内容:代码:变量名.stdrr.read()

 服务端

from socket import *
import subprocess

ip_port = ('192.168.1.5',8080)
back_log = 5
buffer_size = 1024

tcp_sever = socket(AF_INET,SOCK_STREAM)
tcp_sever.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1) #设置socket,使其不卡在time wait,占用地址
tcp_sever.bind(ip_port)
tcp_sever.listen(back_log)

while True:
conn,addr = tcp_sever.accept()
print('新的客户端连接',addr)

while True:
try:
#收消息
cmd = conn.recv(buffer_size)
if not cmd:break
print('收到客户端的命令',cmd)

#执行命令,得到命令的结果cmd_res
res = subprocess.Popen(cmd.decode('utf-8'),shell=True,
stderr=subprocess.PIPE,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
err = res.stderr.read()

if err:
cmd_res = err
else:
cmd_res = res.stdout.read()
#发消息
       if not cmd_res:
        cmd_res = '操作成功'.encode('utf-8') #此代码代表,如果cmd_res 为空的话,也会显示结果
            conn.send(cmd_res)
except Exception as e:
print(e)
break

tcp_sever.close()

 

客户端 

 

from socket import *

ip_port = ('192.168.1.5',8080)
back_log = 5
buffer_size = 1024

tcp_client = socket(AF_INET,SOCK_STREAM)
tcp_client.connect(ip_port)

while True:
cmd = input('>>:').strip()
if not cmd:continue
if cmd == 'quit':break

tcp_client.send(cmd.encode('utf-8'))
cmd_res = tcp_client.recv(buffer_size)
print('命令的执行结果是',cmd_res.decode('utf-8'))

tcp_client.close()

实操结果如下:

<span role="heading" aria-level="2">网络编程:socket 编程

 

 粘包

 注意:只有tcp 会粘包,udp不会粘包。

粘包问题主要还是因为接收方不知道消息之间的界限,不知道一次性提取多少字节的数据造成的。

<span role="heading" aria-level="2">网络编程:socket 编程

 

 

解决粘包

方式一:比较低端一些

服务端:

from socket import *
import subprocess

ip_port = ('192.168.1.5',8080)
back_log = 5
buffer_size = 1024

tcp_sever = socket(AF_INET,SOCK_STREAM)
tcp_sever.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
tcp_sever.bind(ip_port)
tcp_sever.listen(back_log)

while True:
conn,addr = tcp_sever.accept()
print('新的客户端连接',addr)

while True:
try:
#收消息
cmd = conn.recv(buffer_size)
if not cmd:break
print('收到客户端的命令',cmd)

#执行命令,得到命令的结果cmd_res
res = subprocess.Popen(cmd.decode('utf-8'),shell=True,
stderr=subprocess.PIPE,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
err = res.stderr.read()

if err:
cmd_res = err
else:
cmd_res = res.stdout.read()
#发消息
if not cmd_res:
cmd_res = '操作成功'.encode('utf-8')

length =len(cmd_res)
conn.send(str(length).encode('utf-8'))
client_ready = conn.recv(buffer_size)
if client_ready == b'ready':
conn.send(cmd_res)
except Exception as e:
print(e)
break

tcp_sever.close()

客户端

from socket import *

ip_port = ('192.168.1.5',8080)
back_log = 5
buffer_size = 1024

tcp_client = socket(AF_INET,SOCK_STREAM)
tcp_client.connect(ip_port)

while True:
cmd = input('>>:').strip()
if not cmd:continue
if cmd == 'quit':break

tcp_client.send(cmd.encode('utf-8'))

length= tcp_client.recv(buffer_size)
tcp_client.send(b'ready')
length = int(length.decode('utf-8'))

recv_msg = b''
recv_size = 0
while recv_size < length:
recv_msg +=tcp_client.recv(buffer_size)
recv_size = len(recv_msg)

print('命令的执行结果是',recv_msg.decode('utf-8'))

tcp_client.close()

 方式二:比较高端一些 

 服务端:

from socket import *
import subprocess
import struct

ip_port = ('192.168.1.2',8080)
back_log = 5
buffer_size = 1024

tcp_sever = socket(AF_INET,SOCK_STREAM)
tcp_sever.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
tcp_sever.bind(ip_port)
tcp_sever.listen(back_log)

while True:
conn,addr = tcp_sever.accept()
print('新的客户端连接',addr)

while True:
try:
#收消息
cmd = conn.recv(buffer_size)
if not cmd:break
print('收到客户端的命令',cmd)

#执行命令,得到命令的结果cmd_res
res = subprocess.Popen(cmd.decode('utf-8'),shell=True,
stderr=subprocess.PIPE,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
err = res.stderr.read()

if err:
cmd_res = err
else:
cmd_res = res.stdout.read()
#发消息
if not cmd_res:
cmd_res = '操作成功'.encode('utf-8')

length =len(cmd_res)
conn.send(struct.pack('i',length))
conn.send(cmd_res)
except Exception as e:
print(e)
break

tcp_sever.close()

 客户端:

from socket import *
import struct
from functools import partial

ip_port = ('192.168.1.2',8080)
back_log = 5
buffer_size = 1024

tcp_client = socket(AF_INET,SOCK_STREAM)
tcp_client.connect(ip_port)

while True:
cmd = input('>>:').strip()
if not cmd:continue
if cmd == 'quit':break

tcp_client.send(cmd.encode('utf-8'))

length_data= tcp_client.recv(4)
length = struct.unpack('i',length_data)[0]

recv_msg = ''.join(iter(partial(tcp_client.recv, buffer_size), b''))

print('命令的执行结果是',recv_msg.decode('utf-8'))

tcp_client.close()

 

 总结:

<span role="heading" aria-level="2">网络编程:socket 编程

 

 <span role="heading" aria-level="2">网络编程:socket 编程

 

 <span role="heading" aria-level="2">网络编程:socket 编程

 

                                                     

socket 并发socketserver

socketserver-tcp套接字 并发

并发:即多个客户端与服务端的同时交互

 服务端:

import socketserver


class MyServer(socketserver.BaseRequestHandler):
def handle(self):
# print('conn is:', self.request) # self.request相当于conn
# print('addr is:', self.client_address) # self.client_address相当于addr

while True:
try:
# 收消息
data = self.request.recv(1024)
if not data: break
print('收到客户端的消息是:', data,self.client_address)

# 发消息
self.request.send(data.upper())
except Exception as e:
print(e)
break

if __name__ == '__main__':
s = socketserver.ThreadingTCPServer(('192.168.1.2',8081),MyServer)
s.serve_forever()

 客户端:

from socket import *

ip_port = ('192.168.1.2', 8081)
buffer_size = 1024

tcp_client = socket(AF_INET, SOCK_STREAM)
tcp_client.connect(ip_port)

while True:
msg = input('>>: ').strip()
if not msg: continue
tcp_client.send(msg.encode('utf-8'))
print('客户端已经发送消息')

data = tcp_client.recv(buffer_size)
print('收到服务端发来的消息', data.decode('utf-8'))

tcp_client.close()

socketserver-udp套接字 并发

 

对于tcp来说,self.request = conn

但是对于udp来说,self.request =(data,udp的套接字对象

 

服务端:

import socketserver

class MyServer(socketserver.BaseRequestHandler):
def handle(self):
#self.request[0] 是data
#self.request[1]是udp的socket套接字
#self.client_address 是conn
#收消息
print('收到客户端的消息是',self.request[0],self.client_address)
#发消息
self.request[1].sendto(self.request[0].upper(),self.client_address)


if __name__ == '__main__':
s=socketserver.ThreadingUDPServer(('192.168.1.2',8082),MyServer) #多线程
s.serve_forever()

客户端:

from socket import *
ip_port=('192.168.1.2',8082)
buffer_size=1024

udp_client=socket(AF_INET,SOCK_DGRAM) #数据报

while True:
msg=input('>>: ').strip()
udp_client.sendto(msg.encode('utf-8'),ip_port)

data,addr=udp_client.recvfrom(buffer_size)
# print(data.decode('utf-8'))
print(data)

 认证客户端合法性

服务端

from socket import *
import os
import hmac
secret_key = b'ni hao ma'

def conn_auth(conn):
'''
认证客户端连接
:param conn:
:return:
'''
print('开始验证连接的合法性')
msg = os.urandom(32)
conn.sendall(msg)
h=hmac.new(secret_key,msg)#加严
digest=h.digest()
respone=conn.recv(len(digest))
return hmac.compare_digest(respone,digest)#对比respone 与digest 是否一致



def data_handler(conn, bufize=1024):
if not conn_auth(conn):
print('该连接不合法,关闭')
conn.close()
return
print('连接合法,开始通信')

while True:
data = conn.recv(bufize)
if not data: break
conn.send(data.upper())



def server_handle(ip_port,bufize,backlog=5):
'''
只处理连接
:param ip_port:
:param bufize:
:param backlog:
:return:
'''
tcp_socket_server = socket(AF_INET, SOCK_STREAM)
tcp_socket_server.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
# 在bind 之前进行socket 设置,使其不卡在time_wait占用地址
tcp_socket_server.bind(ip_port)
tcp_socket_server.listen(backlog)
while True:
conn, addr = tcp_socket_server.accept()
print('conn是%s,addr是%s' % (conn, addr))
data_handler(conn, bufize)
tcp_socket_server.close()

if __name__ == '__main__':
ip_port =('192.168.1.2',8080)
bufize = 1024
server_handle(ip_port,bufize)

 客户端:

from socket import *
import os,hmac

secret_key = b'ni hao ma'

def conn_auth(conn):

'''
验证客户端到服务器的连接合法性
:param conn:
:return:
'''
msg = conn.recv(32)
h = hmac.new(secret_key, msg)
digest = h.digest()
conn.sendall(digest)


def client_handler(ip_port,bufsize=1024):
tcp_socket_client = socket(AF_INET, SOCK_STREAM)
tcp_socket_client.connect(ip_port)
conn_auth(tcp_socket_client)

while True:
#发消息
cmd = input('>>:').strip()
if not cmd: continue
if cmd == 'quit': break
tcp_socket_client.send(cmd.encode('utf-8'))
#收消息
data=tcp_socket_client.recv(bufsize)
print(data.decode('utf-8'))
tcp_socket_client.close()

if __name__ == '__main__':
ip_port = ('192.168.1.2', 8080)
bufize = 1024
client_handler(ip_port,bufize)

 

 作业

 <span role="heading" aria-level="2">网络编程:socket 编程

 

 

 

 

 

 

 

 

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/154421.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • django request.get_RequestParam

    django request.get_RequestParamDjango在接收到http请求之后,会根据http请求携带的参数以及报文信息创建一个WSGIRequest对象,并且作为视图函数第一个参数传给视图函数。也就是我们经常看到的request参数。在这个

  • 安装pycharm的步骤_pycharm安装教程2020

    安装pycharm的步骤_pycharm安装教程2020简介Jetbrains家族和Pycharm版本划分:pycharm是Jetbrains家族中的一个明星产品,Jetbrains开发了许多好用的编辑器,包括Java编辑器(IntelliJIDEA)、JavaScript编辑器(WebStorm)、PHP编辑器(PHPStorm)、Ruby编辑器(RubyMine)、C和C++编辑器(CLion)、.Net编辑器(Rider)、iOS/ma…

  • 【elasticsearch系列】windows安装IK分词器插件[通俗易懂]

    【elasticsearch系列】windows安装IK分词器插件[通俗易懂]环境github下载:https://github.com/medcl/elasticsearch-analysis-ik/releases注意,IK分词器插件要与ES版本保持一致;有的小伙伴在GitHub上下载插件时,没有发现与ES相对应的版本,可以切换到Tags中选择分支版本;例如Branchs列表中仅可能存在主版本号;切换到右侧Tags中查找对应的版本即可;小编这里选择的7.8.0的版本;安装IK解压缩后拷贝到ElasticSearch安装目录的plugins文件夹下,默认情况该

  • STM32驱动ST7701S芯片(vⅰV0手机换屏价)

    1、配置GPIO口voidLCD_GPIO_Config(void){/*定义一个GPIO_InitTypeDef类型的结构体*/GPIO_InitTypeDefGPIO_InitStructure;/*????IO??*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE…

  • 网络推广100种方法有哪些_100种宣传方式

    网络推广100种方法有哪些_100种宣传方式一米软件发现网上很多人都在找网络推广100种方法,但 其实网站推广并不是方法越多越好,而是找到适合自己的方法为宜,下面一米软件就来给大家介绍下一些常用网站推广的方法。1、搜索引擎营销搜索引擎营销,这种方法一般是通过自建官网然后针对官网进行优化与更新,使得网站在搜索引擎中有一个好的排名。也有通过做付费推广,使得网站的某个关键词在搜索引擎中有个好的排名。2、自媒…

  • Electron那些事10:本地数据库sqlite

    Electron那些事10:本地数据库sqlite【前言】上一节讲了本地日志,本地数据(文件)的部分,详见:Electron那些事09:本地数据_uikoo9的博客-CSDN博客虽然本地日志可以记录日志信息,本地数据可以记录简单的配置文件,但是像一些复杂的业务,需要维护一个本地数据库进行查询,本节讲一下本地数据库sqlite【sqlite】sqlite是有名的本地数据库,在很多系统中都有应用,SQLiteHomePage当然也有nodejs的版本,一般配套和electron使用,sqlite3-np…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号