Socket 小知识

之前想要写一个简洁的小型服务器,包括 ServerClient,所以用到了 Pythonsocket 模块。
这里先附上 两段代码

  1. Server
#!/usr/bin/python
# -*- coding: utf-8 -*-
import socket  

print 'Start listening...'

HOST='129.173.67.53'
# PORT=52631
# HOST='localhost'
PORT=3306
print("IP Addr : " + HOST , " , PORT : " + str(PORT))

s= socket.socket(socket.AF_INET,socket.SOCK_STREAM)   
// 绑定 IP 和 端口
s.bind((HOST,PORT))   
s.listen(1)         
while 1:
    conn,addr=s.accept()   
    print 'Connected by ', addr   
    
    text1 = conn.recv(1024)
    print "Text 1 :" + text1
    text2 = conn.recv(1024)

    # print "Text 1 : " + text1
    print "Text 2 : " + text2


    result = str(gtmapi.textSimilarity(text1 , text2))
    conn.sendall(result)
    print "Sim : " + result

    conn.close()
jpype.shutdownJVM()

  1. Client
#!/usr/bin/python
# -*- coding: utf-8 -*-
import socket  

# Create a TCP/IP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Connect the socket to the port where the server is listening
server_address = ('localhost', 8001)
#server_address = ('129.173.67.53', 3306)
print 'connecting to'
print server_address
sock.connect(server_address)
while 1:

    text1 = "Im sitong"
    text2 = "I am no sitong"

    sock.sendall(text1)
    sock.sendall(text2)

    result = sock.recv(1024)
    print "Sim : " + result

    sock.close()
    break

讨论

运行了上面的代码之后,你会发现个是有问题的,问题就是 你 recv 获取的两个text 的内容可能是错误的。例如

// sent texts
text1 = "I am apple."
text2 = "I like apple."
// recv texts
text1 = "I am"
text2 = apple. I like apple.

就是因为 sendall 发送的信息给 client之后, 并不能保证 client 一次性全部都收到。而且使用的 sendall 的次数和 recv 的次数是没有关系的。也就是说, 并不是你连续使用两次sendall, 就可以使用 两个 recv 来分别接收两次发送的数据。
解决的办法有三个:

  • 基础的接收方法
    因为当与服务器的socket 断开之后,client 会接受到空字符串,所以可以直接加上循环接收数据,当收到空字符串的时候就是结束了。代码如下:
import socket,struct,sys,time

Port=22220
#assume a socket disconnect (data returned is empty string) means  all data was #done being sent.
def recv_basic(the_socket):
    total_data=[]
    while True:
        data = the_socket.recv(20480)    
        if not data: break
        total_data.append(data)
    return ''.join(total_data)
  • 尾部标记法
    就是设置好一个约定,当接收到的数据结尾包含某个字符串的时候,就代表数据传输结束了。
    代码如下:
End='something useable as an end marker'
def recv_end(the_socket):
    total_data=[];data=''
    while True:
            data=the_socket.recv(8192)
            if End in data:
                total_data.append(data[:data.find(End)])
                break
            total_data.append(data)
            if len(total_data)>1:
                #check if end_of_data was split
                last_pair=total_data[-2]+total_data[-1]
                if End in last_pair:
                    total_data[-2]=last_pair[:last_pair.find(End)]
                    total_data.pop()
                    break
    return ''.join(total_data)
  • 负载长度方法
    就是在数据传输一开始 写明要传输的数据的长度。这要求接收方一边接收数据,一遍解析数据。
    代码如下:
def recv_size(the_socket):
    #data length is packed into 4 bytes
    total_len=0;total_data=[];size=sys.maxint
    size_data=sock_data='';recv_size=8192
    while total_len4:
                size_data+=sock_data
                size=struct.unpack('>i', size_data[:4])[0]
                recv_size=size
                if recv_size>524288:recv_size=524288
                total_data.append(size_data[4:])
            else:
                size_data+=sock_data
        else:
            total_data.append(sock_data)
        total_len=sum([len(i) for i in total_data ])
    return ''.join(total_data)

你可能感兴趣的:(Socket 小知识)