首先我们看一段python client/server代码。
server端:
>>> import sys,socket >>> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) >>> MAX = 65535 >>> PORT = 1060 >>> s.bind(('127.0.0.1', PORT)) >>> print 'Listening at', s.getsockname() Listening at ('127.0.0.1', 1060) >>> while True: ... data, address = s.recvfrom(MAX) ... print 'the client at', address, 'says', repr(data) ... s.sendto('Your data was %d bytes' % len(data), address)
可以知道server监听在127.0.0.1的1060端口上。当收到 client发来的一段信息之后就会返回给 client一段话.
再看client端:
import socket, sys s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) MAX = 65535 PORT = 1060 print 'Address before sending:', s.getsockname() s.sendto('This is my message', ('127.0.0.1', PORT)) print 'Address after sending', s.getsockname() data, address = s.recvfrom(MAX) # overly promiscuous - see text! print 'The server', address, 'says', repr(data)
客户端非常简单,创建一个socket,通过socket的sendto像server发送一段信息,然后把收到的回复打印出来。
正常情况下,这两段代码是没什么问题的。可是如果这时候server没有运行,我们的客户端运行之后会怎么样呢? 会 stuck在
data, address = s.recvfrom(MAX)
这时候如果你通过一个其它的机器伪造server像client发回复会怎么样呢?我们新开一个窗口输入下面代码来看看
>>> import sys,socket >>> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) >>> s.sendto('fake' , ('127.0.0.1',56557)) 4
注意这里的4是sendto的返回值,表示发送了多长的数据。
我们看client端
>>> data, address = s.recvfrom(MAX) # overly promiscuous - see text! >>> print 'The server', address, 'says', repr(data) The server ('127.0.0.1', 33670) says 'fake'
client依然把我们伪造的server发回的信息当做server回复处理。 很多时候这是我们不想看到的。 这时候 connect的作用就体现出来了。 通过connect,client将会识别出server的ip和port。如果有数据从其它的ip和port发回,client将不会把它当做server回复。 并且调用了connect之后我们使用的是send()和recv()而不是 sendto()和recvfrom(). 看如下例子:
>>> import sys , socket >>> s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) >>> s.connect(('127.0.0.1',1060)) >>> print 'Client socket name is', s.getsockname() Client socket name is ('127.0.0.1', 48824) >>> s.send('This is another message') 23 >>> data = s.recv(65535) >>> print 'The server says', repr(data) The server says 'Your data was 23 bytes'
这个例子中client调用了connect, 然后用了send 和recv去发收消息。 当发送往正确的server的时候,正常收到了回复。 但是我们看看如果用一个错误的server发送消息给client它还会不会收到呢?
>>> s.sendto('fake' , ('127.0.0.1',48824)) 4
在一个新窗口中用socket发送消息到我们 client socket的端口。 但是client端没有反应。
>>> data = s.recv(65535)
这是因为 connect 的起到了过滤的作用。 client只会识别connect中提到的server。
值得了解的是,connect存在的意义不是出于安全考虑。因为connect实际上做的事情是在 OS 的内存中记住了server的地址信息。这样当你调用send 和 recv的时候就不用指定地址了。 调用connect不会在网络上传输数据。 而如果有黑客真的想攻击你,他可以很容易的把自己的地址伪装成server的ip和端口。
所以如果你的client想要能够识别server和非server, 你可以有两种办法:
1. 是调用connect。 这种方法非常简单,因connect会在OS的内存中记住server的信息。以后调用send 和 recv实际上都是去查阅这个server信息。 但是这种方法有个不方便的地方就是你一次只能跟一个server对话。如果你想发送给其它的server,需要重新调用connect,但是这样会覆盖之前的connect记住的server信息。
2. 就是不调用connect,直接使用sendto和recvfrom。在recvfrom之后你可以获得给你发信息的机器的地址信息。你可以写一段代码手动的去比较看是不是真正的服务器。这种方式要手动的写新代码。但是比较灵活,如果你想与多个server通信就不必重复的connect。
很重要的一点补充。这里的connect是用在UDP上的。而不是TCP。TCP上的connect是真正的网络函数,它会发起TCP三步握手去连接服务器。