Python_socket初识

TCP

Server:

import socket
s=socket.socket()                       #创建套接字
s.bind(('127.0.0.1',6000))              #为套接字绑定IP和port

s.listen()                              #监听连接
msg,addr=s.accept()                     #接受客户端发来的连接

ret = msg.recv(1024)                    #接受客户端发来的消息
print(ret.decode('utf-8'))
msg.send('hello'.encode('utf-8'))         #发送消息给客户端

msg.close()                             #关闭连接
s.close()                               #关闭套接字

Client:

import socket
s =socket.socket()                              #建立socket套接字

s.connect(('127.0.0.1',6000))                   #连接到远程

s.send('word'.encode('utf-8'))                  #发送消息给远程
ret = s.recv(1024)                              #接受远端发来的消息
print(ret.decode('utf-8'))

s.close()                                       #关闭连接

UDP

Server:

import socket

s = socket.socket(type=socket.SOCK_DGRAM)
s.bind(('127.0.0.1',6000))

msg,addr = s.recvfrom(1024)
print(msg.decode('utf-8'))
s.sendto('word!'.encode('utf-8'),addr)

s.close()

Client:

import socket
s = socket.socket(type=socket.SOCK_DGRAM)

s.sendto('hello'.encode('utf-8'),('127.0.0.1',6000))
msg,addr = s.recvfrom(1024)
print(msg.decode('utf-8'))

黏包

黏包发生在tcp协议:是属于面向流的传输,为了优化传输的效率,使用了Nagle算法,如果在很短很短的时间内发送了两次很小的数据,那么就会把这两个数据合为一个,进行传输。

优点:减少了一次传输的消耗,丢包率也减少了,增加了网络传输的效率

缺点:客户端没有办法分辨从哪里断句,不好拆包。因为面向流的通信是无消息保护边界的,很容易形成黏包。

注意:

UTP是没有黏包的,因为UTP没有经过三次握手,建立ip隧道,而且是面向数据的,只管把数据发出,并不负责数据是否到达或者丢失等元素,直接send就发送,哪怕是空也会直接被封装直接发走(tcp消息为空不会发送),没有使用Nagle算法,所以不会黏包

解决方法

建立两次传输,第一次传输要发送内容的长度,然后将长度作为第二次接受的值

基础:

(能用,有bug,方便理解)

client

import socket

ip_port=('127.0.0.1',6000)

s=socket.socket()
res = s.connect(ip_port)
inp = input('>>>')
lenth = str(len(inp)).encode('utf-8')
s.send(lenth)
s.send(inp.encode('utf-8'))


s.close()

Server

import socket
ip_peort = ('127.0.0.1',6000)

s =socket.socket()
s.bind(ip_peort)
s.listen(5)

conn,addr = s.accept()

lenth = conn.recv(1).decode('utf-8')
print(lenth)
data2 = conn.recv(int(lenth))

print('第一次接受的>',lenth)
print('第二次接受的>',data2.decode('utf-8'))

conn.close()
s.close()

进阶:

使用struct模块,该模块可以把一个类型,如数字,转成固定4位长度的bytes

Client:

import socket
import struct

ip_port=('127.0.0.1',6000)

s=socket.socket()
res = s.connect(ip_port)
inp = input('>>>')
lenth = struct.pack('i',len(inp))           #i  是int的意思
s.send(lenth)
s.send(inp.encode('utf-8'))

s.close()

Server:

import struct
import socket
ip_peort = ('127.0.0.1',6000)

s =socket.socket()
s.bind(ip_peort)
s.listen(5)

conn,addr = s.accept()

ret = conn.recv(4)
lenth = struct.unpack('i',ret)[0]
print(lenth)
data2 = conn.recv(lenth)
print('第一次接受的>',lenth)
print('第二次接受的>',data2.decode('utf-8'))

conn.close()
s.close()

传输不可读取的二进制文件

发送端:

import socket
import struct
import os
import json


s = socket.socket()
s.bind(('127.0.0.1',6000))
s.listen()
msg,addr = s.accept()

size = os.path.getsize('test.gif')
dic = {'filename':'test.jpg',
       'filesize':size}

str_len = json.dumps(dic).encode('utf-8')
#必须转换成json去判断长度,否则字典无法判断长度,而且网络上只能用json传输
dic_len = struct.pack('i',len(str_len))
msg.send(dic_len)
msg.send(str_len)

with open('test.gif','rb') as f:
    while True:
        Read = f.read(1024)
        if Read:
            msg.send(Read)
        else:
            break

msg.close()
s.close()

接收端:

import socket
import struct

import json


s = socket.socket()
s.connect(('127.0.0.1',6000))

dic_len = s.recv(4)         #字典的长度
print(dic_len)
dic_len = struct.unpack('i',dic_len)[0]
print(dic_len)
str_dic = s.recv(dic_len).decode('utf-8')
print(str_dic)
str_dic = json.loads(str_dic)
print(str_dic)

with open('test2.gif','ab') as f:
    data=s.recv(str_dic['filesize'])
    f.write(data)
s.close()

或者:

with open(dic['filename'],'ab') as f:
    while dic['filesize']:
        content = sk.recv(4096)
        dic['filesize'] -= len(content)
        f.write(content)

推荐第二个

  

Socketserver

import socketserver

class Mysocket(socketserver.BaseRequestHandler):                #继承BaseRequestHandler类
    def handle(self):                                           #必须定义一个handel方法
        self.request.send(b'Server.......')                     #相当于msg.send
        msg = self.request.recv(1024)                           #相当于msg.recv
        print(msg)                                              

if __name__ == '__main__':                                      #当目前模块为可执行状态时
    socketserver.TCPServer.allow_reuse_address = True           #允许地址复用
    server = socketserver.ThreadingTCPServer(('127.0.0.1',9000),Mysocket)           #类似于s.bind
    server.serve_forever()                                      #让server端一直运行

参考链接:http://www.cnblogs.com/Eva-J/articles/8244551.html

发表评论