工程建立:
先建Project然后添加module和package,如图所示:
代码编写:
首先代码中会出现中文字符,所以要在代码中添加#coding utf-8,避免出现乱码。
然后明确TCP/IP协议需要哪些类与方法:
baseserver--->tcpserver-->threadingmixserver
按照顺序进行编写:
TCPServer 继承于 TheadingTCPServer类,按住ctrl阅读TheadingTPServer这个类 :
发现TheadingTCPServer继承于ThreadingMixIn和TCPServer,同样按住ctrl点击TCPServer进行阅读,里面有具体的实现,初始化的方法。
然后,定义一个类,继承于socketserver里的一个类StreamRequestHandler点鼠标右键里面有一个重载方法Override实现setup就可以了。
最后,写测试代码判断是否能够流畅运行:
使用元祖来规定输出的格式,address是一个元祖用括弧括起来的。实现Tserver,接收address,使用我们定义的ClientRequestHandler进行处理,修改间隔时间,点击运行。
运行TCP/IP代码:
在eclipse里点击Run as,按win+R进入命令行模式,输入telnet 192.168.1.2,远程登录,完成实验
学习反思:
学习中遇到的问题:
1.输入端口号错误,导致套接字重复报错,经过查询,23号端口为Telnet端口,修改代码。
2.重载是什么?
答:重载就是方法名相同,参数(个数或类型)不同(称之为签名不同)
比如:
string test()
{
Console.WriteLine("Hello world");
}
string test(string msg)
{
Console.WriteLine(msg);
}
这样就达到了,用同样的方法名,通过传递不同的参数,实现不同的目的 。
3.继承怎么使用?
答:继承是指一个对象直接使用另一对象的属性和方法。事实上,我们遇到的很多实体都有继承的含义。例如,若把汽车看成一个实体,它可以分成多个子实体,如:卡车、公共汽车等。这些子实体都具有汽车的特性,因此,汽车是它们的"父亲",而这些子实体则是汽车的"孩子"。如果一个类A继承自另一个类B,就把这个A称为"B的子类",而把B称为"A的父类"。继承可以使得子类具有父类的各种属性和方法,而不需要再次编写相同的代码。在令子类继承父类的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类的原有属性和方法,使其获得与父类不同的功能。
关于服务器端的完善和客户端的编写:
在之前的服务器端里只是重载了setup方法并没有对数据的处理,为了完善服务器端的功能将重载方法改为handle进行处理。然后定义一个清单,便于用户连接,当用户连接,进入循环获取他们的套接字,也就是他们的IP地址和端口号。并且通过self.wfile.write给客户端返回一个字符串,如果客户端向服务器端发送121的请求,通过self.rfile.read来获取并显示已获取。
'''
Created on 2018年9月25日
@author: 15006
'''
from socketserver import ThreadingTCPServer, StreamRequestHandler
from multiprocessing.connection import Client
class TServer(ThreadingTCPServer):
def __init__(self,server_add,ClientRequestHandler):
ThreadingTCPServer.__init__(self, server_add, ClientRequestHandler)
class ClientRequestHandler(StreamRequestHandler):
def handle(self):
Clientlist= []
flag = True
while flag:
clientadd =self.client_address
if clientadd in Clientlist:
pass
else:
Clientlist.append(clientadd)
self.wfile.write(b"hello")
print(str(clientadd)+"已连接")
if self.rfile.read(121):
print(str(clientadd)+"已获取")
pass
if __name__ == '__main__':
address = ('',20025)
server = TServer(address,ClientRequestHandler)
server.serve_forever()
pass
客户端的编写:
通过ADDR来规定套接字的元祖格式,通过connect的方法进行客户端的连接。在<<<<<<<<<<输入发送给服务器端的请求,最后使用close来关闭通信。
#coding:utf-8
'''
@author: 15006
'''
from _socket import socket
from socket import *
from test.test_gzip import data1
from time import sleep
HOST='localhost'
PORT = 20025
BUFSIZ = 1024
ADDR = (HOST,PORT)
tcpCliSock = socket(AF_INET,SOCK_STREAM)
tcpCliSock.connect(ADDR)
while True:
data = input('>>>>>>>>> ')
if not data:
break
tcpCliSock.sendall(data.encode())
data = tcpCliSock.recv(BUFSIZ)
if not data:
break
print(data)
sleep(20)
tcpCliSock.close()
if __name__ == '__main__':
pass
代码效果:
依次运行服务器端文件和客户端文件:
显示IP与登录端口
在客户端输入121,收到回复hello,但是由于使用的是date.encode(),返回的是二进制流,所以显示的为b’hello’
最后服务器端读到121,显示已获得,完成一次完整的通信。
存在的问题:
1.客户端与服务器端只能建立一次通信
2.想要在代码中输入自定义的端口号,可是参考网上教程,需要编写解码器完成,多次尝试,没有成功
3.对于多线程与单线程的不太理解
4.不清楚这个代码中每次运行它显示的端口号都在改变,明明已经被定义在全局变量里了,为什么还会改变
编写过程中已经解决的问题:
1.服务器积极拒绝
原因是没有先开启服务器端,直接运行了客户端。
2.二进制流问题
由于使用的是date.encode(),返回的是二进制流,显示的为b’hello’,将date.encode()替换为data.encode(encoding='utf_8', errors='strict')即可解决
3.为什么服务器端重载方法为handle而不是setup
因为setup只能初始化,并不能对数据进行处理,handle中可以使用wfile和rfile来处理数据。
4.客户端一直尝试与服务器进行连接,可能导致服务器端崩溃
方法一:用sleep()设置间隔时间,简单但是面对大量数据请求会造成严重卡顿现象
方法二:每次请求过后close通信通道
基本流程梳理: