简单的socket

最终目标

最终目标是实现一个能够双方互相通信的socket

socket原理

在这里插入图片描述
一个接收端和一个发送端,接收端和发送端分别有各自的应用,通信就是发送端想发送给指定的应用,想要找到对应的主机则需要IP,找到IP就能知道发送的地址,需要找到指定的应用则则需要端口,一个端口对应一个应用。

0.分析伪代码

接收端

1.首先导入socket ## import socket。
2.然后指定协议类型,指定为TCP/IP。
3.等待时间,等发送端发送数据。
4.接收到了数据。
5.再把需要发送的数据发送回去。
6.继续等待接收数据。

发送端

1.同样导入socket ## import socket。
2.指定网络协议类型,为TCP/IP。
3.进行连接接收端,输入ip和端口号,##connect(a.ip,a.porn)。
4.将数据发送过去。##connect.send(hello)
5.等待接收返回的数据。
6.关闭连接。

开始写代码

1.最简单的实现互相通信

客户端(发送端)
#客户端
import socket
client = socket.socket()	# 创建socket实例并且声明socket类型
client.connect(("localhost",9999))		#connect只接收一个参数,但是我们需要传入ip和端口,所以用元组将两个参数包裹起来
client.send('你好世界'.encode('utf-8'))	#传数据
data = client.recv(1024)	#接收服务端的返回
print(data.decode())			#打印服务端的返回
client.close()		#关闭连接
服务端(接收端)
import socket
server = socket.socket()
server.bind(('localhost',9999))	#绑定要监听端口
server.listen()		#监听

conn,addr = server.accept() #等客户端连进来
print(conn,addr)		#打印下conn和addr,可以看出conn为一个内存地址,addr为ip地址和一个随机端口
data = conn.recv(1024)
print('recv:',data)
conn.send(data.upper())
server.close()
笔记:

conn是客户端连接后再服务器端为其生成的一个连接实例。
addr是ip地址和一个随机端口,这个端口是服务端发到的客户端接收的端口。

9999是服务端的端口号,而addr中第二个参数是服务端返回数据时客户端的端口号。

2.实现双方多次通信

客户端(发送端)

客户端的变化:
添加了一个判断是否直接回车,如果回车那么就重新输入。

#客户端
import socket
client = socket.socket()
client.connect(("localhost",9999))	#绑定端口IP和接口
while True:	#实现多次通信
    msg = input('>>>:').strip()		#输入
    if len(msg) == 0:	#判断输入是否为空,如果直接输入回车那就重新输入
        continue
    client.send(msg.encode('utf-8'))#发送输入的数据
    data = client.recv(1024)	#等待服务端返回的数据。
    print("recv:",data.decode())	#输出服务端返回的数据


服务端(接收端)
# 服务端
import socket

server = socket.socket()		#声明socket类型
server.bind(('localhost', 9999))	#绑定主机ip和端口
server.listen(5)		#设定最大连接的客户端数量
print('dengdianhua')	
while True:			#这个作用是解决当客户端断开时会导致服务端异常断开的错误
    conn, addr = server.accept()
    print(conn, addr)
    while True:		#这个死循环作用是实现多次通信
        data = conn.recv(1024)	#接收数据
        print('recv:', data)	#打印传过来的数据
        if not data:		#判断data是否为空,如果为空,则代表客户端断开了
            print('client ha host')		#连接中断
            break

        conn.send(data.upper())	#返回个客户端数据并将字母大写

以上就实现了客户端和服务端的多次通信。

问题:

这个程序我发现一个我无法解决的bug,就是我判断了当客户端断开时服务器端会断开并等待下一次的请求,但是我运行程序时断开客户端,服务器端还是会出现错误。希望能有大神能帮我解决一哈。

3.能够使用简单的命令

客户端发送命令,服务端执行并将结果返回给客户端。

客户端(发送端)

客户端的变化:

#客户端
import socket

client = socket.socket()

client.connect(('localhost', 9999))

while True:
    cmd = input('>>:').strip()	
    if len(cmd) == 0:		#判断是否为直接敲了回车
        continue
    client.send(cmd.encode('utf-8'))	#发送数据
    cmd_res_size = client.recv(1024)	#接收服务器返回的数据
    print('命令结果大小:', cmd_res_size)	******防粘包
    client.send("准备接收数据".encode('utf-8'))	
    received_size = 0
    received_data = b""
    while received_size < int(cmd_res_size.decode()):
        data = client.recv(1024)
        received_size += len(data)
        received_data += data
    else:
        print('cmd res recvive done...', received_size)
        print(received_data.decode())
client.close()

服务端(接收端)
# 服务端
# Author:fang
import socket, os

server = socket.socket()
server.bind(("localhost", 9999))
server.listen(5)

while True:
    conn, addr = server.accept()
    print('new conn', addr)
    while True:
        print("等待新指令")
        data = conn.recv(1024)
        if not data:
            print('客户端已断开')
            break
        print('执行指令:', data)
        cmd_res = os.popen(data.decode()).read()
        print('before send', len(cmd_res))
        if len(cmd_res) == 0:
            cmd_res = 'cmd has no output'

        conn.send(str(len(cmd_res.encode())).encode())
        client_ack = conn.recv(1024)
        conn.send(cmd_res.encode('utf-8'))
        print('send done')
server.close()

结果

遇到的问题:
粘包:

连续发送数据的时候,服务端的缓存机制将缓存中的数据发送给服务端,数据可能会粘到一起发送到客户端。

方法一

sleep一小段时间。

import time

time.sleep(0.5)

缺点:会影响服务器运行速度。

方法二

在连续的两次send间隙向服务端或客户端(客户端向服务端发,服务端向客户端发)接收客户端发过来的一次请求或者消息,会将缓存的数据直接发出去,这样就会不会将两份前后发的数据粘到一起。

代码
服务端
		conn.send(str(len(cmd_res.encode())).encode())
        client_ack = conn.recv(1024)		#接收客户端器发送数据
        conn.send(cmd_res.encode('utf-8'))
客户端
	cmd_res_size = client.recv(1024)		#第一次等待服务端发送数据
    print('命令结果大小:', cmd_res_size)	#打印要传输过来的数据大小
    client.send("准备接收数据".encode('utf-8'))	#告诉服务端自己已经准备好接收数据了

总结

  1. 熟悉了socket的基本用法,对于TCP/IP协议也有了一定的了解。
  2. 实现了服务端和客户端双方通信,并能多次通信。
  3. 实现了发送命令给服务端,服务端执行命令并将得到的结果返回给客户端
  4. 解决了编程中遇到的各种问题,比如编码问题,粘包问题,经过努力最终解决了问题。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值