2018-07-21tcp

2018-08-16  本文已影响0人  楚糖的糖

udp通信模型中,在通信开始之前,不需要建立相关的链接,只需要发送数据即可

tcp通信模型中,在通信开始之前,一定要先建立相关的链接,才能发送数据,类似于生活中,"打电话""


总结:

tcp传输控制协议:稳定、相对于udp而言,要慢一些、web服务器都是使用的tcp

udp用户数据包协议:不稳定、适当比tcp要快一些。

想要完成一个tcp服务器的功能,需要的流程如下:

socket:创建一个套接字,默认是主动的套接字(相当于买个手机)

bind:绑定ip和port  (插上手机卡)

listen:使套接字变为可以被动链接  (设计手机为正常接听状态(即能够响铃))                  listen(5)代表:最多可以接受5个客户端的连接

accept:等待客户端的链接  (静静的等着别人拨打)

recv/send:接收发送数据

NAT模式下收发数据:

#coding =utf-8

from socket import *

tcpSerSocket = socket(AF_INET,SOCK_STREAM)

tcpSerSocket.bind(("",7788))

tcpSerSocket.listen(5)

newSocket,clientAddr=tcpSerSocket.accept()

recvData=newSocket.recv(1024)

print("接收到的数据是:%s"%recvData)

newSocket.send(b"xiexie!")

newSocket.close()

tcpSerSocket.close()

实现客户端收发功能:

#coding=utf-8

from socket import *

tcpCliSocket =socket(AF_INET,SOCK_STREAM)

serAddr=('192.168.20.125',7788)

tcpCliSocket.connect(serAddr)

sendData = raw_input("请输入要发送的数据")

tcoCliSocket.send(sendData)

recvData = tcpCliSocket.recv(1024)

print("接收到的数据是%s"%recvData)

tcpCliSocket.close()

注意:对于套接字,服务器端有两个,客户端有一个。

注意:参数类型是一个元组的。

运行的时候先启动服务器端,再运行脚本文件:

应用:模拟QQ聊天

客户端参考代码:

from socket import *

tcpCliSocket=socket(AF_INET,SOCK_STREAM)

serAddr = ("192.168.242.133",7788)

tcpCliSocket.connect(serAddr)

while True:

        sendData = input("send")

        if len(sendData)>0:

                tcpCliSocket.send(sendData.encode("gb2312"))

        else:

                break

        recvData = tcpCliSocket.recv(1024)

        print('recv:',recvData.decode("gb2312"))

tcpCliSocket.close()

服务端参考代码

from socket import *

tcpSerSocket=socket(AF_INET,SOCK_STREAM)

tcpSerSocket.bind("192.168.242.133",7788)

tcpSerSocket.listen(5)

while True:

        newSocket,seraddr=tcpSerSocket.accept()

        while True:

                recvdata =newSocket.recv(1024)

                if len(recvData)>0:

                        print("recv",recvData.decode("gb2312"))

                else:

                        break

            sendData = input("send:")

            newSocket.send(sendData.encode("gb2312"))

    newSocket.close()

tcpSerSocket.close()

listen参数问题

from socket import *

tcpSerSocket=socket(AF_INET,SOCK_STREAM)

tcpSerSocket.bind("192.168.242.133",7788)

tcpSerSocket.listen(int(input("请输入最大链接数')))

while True:

        newSocket,cliAddr=tcpSerSocket.accept()

        print(cliAddr)

        sleep(1)

客户端的代码:

from socket import *

for i in range(int(input("请输入最大的链接数")))

        s=socket(AF_INET,SOCK_STREAM)

        s.connect(("192.168.2420133",7788))

        print(i)

mac系统就是写几就是几,而linux系统它自己有最大值,你写的不管用,这就是为什么linux系统合适做web服务器开发。

在此期间,如果20个客户端调用了connect链接服务器,那么这个服务器的linux底层会自动维护2个队列(半链接和已链接)其中半链接和已链接的总数为listen中的值,如果这个值为5,那么意味着此时最多有5个客户端能够链接成功,而剩余的15个客户端阻塞在connect函数。

如果服务器调用了accept,那么linux底层中的那个半链接和已连接中的客户端的个数就少了一个,因此此时那15个因为connect堵塞的客户端又会在进行链接来争抢这一个空出来的位置。


多进程服务器

from socket import *

from multiprocessing import Process

from time import sleep

def dd(newSocket,destAddr):

        while True:

                recvData = newSocket.recv(1024)

                if len(recvData)>0:

                        print("recv[%s]:%s"%(str(destAddr),recvData))

                else:

                        print("[%s]客户端已经关闭"%str(destAddr))

                        newSocker.close()

def main():

serSocket=socket(AF_INET,SOCK_STREAM)

serSocket.setSockopt(SOL_SOCKET,SO_REUSEADDR,1)

serSocket.bind(('192.168.242.133',7788))

serSocket.listen(5)

try:

        while True:

                print("---主进程,等待")

                newSocket,destAddr = serSocket.accept()

                print("主进程,创建进程")

                client = Process(target=dd,args=(newSocket,destAddr))

                client.start()

                newSocket.close()

finally:

        serSocket.close()

if __name__='__main__':

        main()

       默认情况下,两个独立的套接字不可与同一本地接口(在TCP/IP情况下,则是端口)绑定在一起。但是少数情况下,还是需要使用这种方式,来实现对一个地址的重复使用。设置了这个套接字,服务器便可在重新启动之后,在相同的本地接口以端口上进行监听。

        一般来说,一个端口释放后会等待两分钟之后才能再被使用,SO_REUSEADDR是让端口释放后立即就可以被再次使用。     SO_REUSEADDR用于对TCP套接字处于TIME_WAIT状态下的socket,允许重复绑定使用。server程序总是应该在调用bind()之前设置SO_REUSEADDR套接字选项。

通过为每个客户端创建一个进程的方式,能够同时为多个客户端进行服务,当客户端不是特别多的时候,这种方式还行,如果有几百上千个,就不可取了,因为每次创建进程等过程需要好较大的资源。


多线程服务器

from socket import *

from threading import Thread

from time import sleep

def dd(newSocket,destAddr):

        while True:

                recvData = newSocket.recv(1024)

                if len(recvData)>0:

                        print("recv[%s]:%s"%(str(destAddr),recvData))

                else:

                        print("[%s]客户端已经关闭"%str(destAddr))

                        break

      newSocker.close()

def main():

serSocket=socket(AF_INET,SOCK_STREAM)

serSocket.setSockopt(SOL_SOCKET,SO_REUSEADDR,1)

serSocket.bind(('192.168.242.133',7788))

serSocket.listen(5)

try:

        while True:

                print("---主进程,等待")

                newSocket,destAddr = serSocket.accept()

                print("主进程,创建进程")

                client = Thread(target=dd,args=(newSocket,destAddr))

                client.start()

                newSocket.close()

finally:

        serSocket.close()

if __name__='__main__':

        main()

对于效率这一块,差不多的,但是进程耗费的资源要大,线程耗费的资源要小。不管是多进程还是多线程代码几乎一样,

区别在于:在多线程中newSocket.close()不能关闭,因为线程是共享同一份资源的,也就是共享全局变量的,传递东西也就是传递的引用,一个进程里面所有的线程都有共用同一份东西的。

如果serSocket不小心被close了,那么意味着:不能再接收新的客户端的链接了。

如果newSocket被close了,那么意味着:这个套接字就不能再使用recv和send来收发数据了。

上一篇下一篇

猜你喜欢

热点阅读