溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

44網(wǎng)絡(luò)編程_socket_TCP

發(fā)布時間:2020-06-29 00:31:23 來源:網(wǎng)絡(luò) 閱讀:254 作者:chaijowin 欄目:編程語言

?

?

?

目錄

socket,套接字:... 1

TCP編程:... 2

TCP服務(wù)器端編程步驟:... 3

群聊程序,TCP實現(xiàn):... 5

makefile... 7

TCP客戶端編程步驟:... 10

?

?

?

socket,套接字:

py中提供socket.py標(biāo)準(zhǔn)庫,非常底層的接口庫;

socket是一種通用的網(wǎng)絡(luò)編程接口;

44網(wǎng)絡(luò)編程_socket_TCP

?

協(xié)議族:

AF,address family,用于sock=socket.socket()第一個參數(shù);

AF_NET,ipv4;

AF_NET6,ipv6;

AF_UNIXunix domain socket,win沒有這個;

?

socket類型:

socket.SOCK_STREAM,面向連接的流套接字,默認(rèn)值TCP協(xié)議,如sock=socket.socket(type=socket.SOCK_STREAMsock=socket.socket())均取默認(rèn);

socket.SOCK_DGRAM,無連接的數(shù)據(jù)報文套接字,UDP協(xié)議,如sock=socket.socket(type=socket.SOCK_DGRAM);

?

?

?

TCP編程:

socket編程,需要兩端,一般需要一個服務(wù)端server,一個客戶端client;

?

C/S模型,C/S編程;

B/S,B/S編程,本質(zhì)上是C/S,是種特殊的C/S,要支持httphtmlh6)、cssjs、聲音、視頻等;只做Bweb前端開發(fā);

?

TCP服務(wù)器端編程步驟:

創(chuàng)建socket對象,socket(family=AF_INET, type=socket.SOCK_STREAM, proto=0, fileno=None)

綁定ipport,bind()方法;ipv4地址為一個二元組('ip',port);

開始監(jiān)聽,將在指定的ipport上監(jiān)聽,listen()方法;

獲取用于傳送數(shù)據(jù)的socket對象:

???????? accept()阻塞等待客戶端建立連接,返回一個新的socket對象和客戶端地址(ipv4為二元組地址,遠(yuǎn)程客戶端的地址),如accept() -> (socket object, address info)

???????? recv(bufsize[,flags]),接收數(shù)據(jù),使用緩沖區(qū)接收數(shù)據(jù);

???????? send(bytes),發(fā)送數(shù)據(jù);

?

生產(chǎn)中編程時不會用這么底層的模塊,當(dāng)前使用是用于理解socket;

?

sock=socket.socket()

ip='127.0.0.1'

port=9999

addr=(ip,port)

sock.bind(addr)

sock.lienten()

conn,addrinfo=sock.accept()

?

conn.recv(bufsize[,flag]),獲取數(shù)據(jù),默認(rèn)是阻塞方式,TCP接收數(shù)據(jù)

conn.recvfrom(bufsize[,flag]),獲取數(shù)據(jù),返回一個二元組(bytes,address)UDP接收數(shù)據(jù);

conn.recv_into(buffer[,nbytes][,flag]]),獲取到nbytes的數(shù)據(jù)后,存儲到buffer中;如果nbytes沒有指定或0,將buffer大小的數(shù)據(jù)存入buffer中;返回接收的字節(jié)數(shù);

conn.recvfrom_into(buffer[,nbytes[,flags]]),獲取數(shù)據(jù),返回一個二元組(bytes,address)buffer中;

conn.send(bytes[,flags]),TCP發(fā)送數(shù)據(jù);

conn.sendall(bytes[,flags])TCP發(fā)送全部數(shù)據(jù),成功返回None本質(zhì)上調(diào)的是send();

conn.sendto(string[,flag],address),UDP發(fā)送數(shù)據(jù)

conn.sendfile(file,offset=0,count=None),發(fā)送一個文件直至EOF,使用高性能的ossendfile機(jī)制,返回發(fā)送的字節(jié)數(shù);win不支持sendfile,不是普通文件時,用send()發(fā)送文件;offset指起始位置;3.5版開始;

?

conn.getpeername(),返回連接套接字的遠(yuǎn)程地址,返回值通常是元組(ipaddr,port);

conn.getsockname(),返回套接字自己的地址,通常是元組(ipaddr,port);

conn.setblocking(flag),默認(rèn)值True阻塞;False0非阻塞,通常將套接字設(shè)為非阻塞,非阻塞模式下,如果調(diào)用recv()沒有發(fā)現(xiàn)任何數(shù)據(jù),或調(diào)用send()無法立即發(fā)送數(shù)據(jù),將引起socket.error異常;Set the socket to blocking (flag is true) or non-blocking (false). setblocking(True) is equivalent to settimeout(None);setblocking(False) is equivalent to settimeout(0.0).;

conn.settimeout(value),設(shè)置套接字操作的超時期,timeout是一個浮點(diǎn)數(shù),單位s,值為None表示沒有超時期;一般,超時期應(yīng)在剛創(chuàng)建套接字時設(shè)置,因為它們可能用于連接的操作,如connect();

conn.setsockopt(level,optname,value),設(shè)置套接字選項的值,如緩沖區(qū)大小等;具體看help有很多選項,不同OS不同version都不盡相同;

?

例:

sock = socket.socket()?? #步驟1,均取默認(rèn),family=AF_NET,type=SOCK_STREAM

?

ip = '192.168.7.144'?? #點(diǎn)分四段表示

port = 9999

addr = (ip, port)

sock.bind(addr)?? #步驟2

?

sock.listen()?? #步驟3

?

# s1 = socket.socket()

# s1.bind(addr)?? #OSError: [WinError 10048] 通常每個套接字地址(協(xié)議/網(wǎng)絡(luò)地址/端口)只允許使用一次

# s1.listen()

?

time.sleep(3)

logging.info(sock)

?

conn, addrinfo = sock.accept()?? #步驟4

# while True:?? #不能這樣寫,接收進(jìn)1client請求后,之后的連接請求接收不到,解決:多線程

#???? conn, addrinfo = sock.accept()

?

logging.info('{} {}'.format(conn, addrinfo))

?

for i in range(3):

??? data = conn.recv(1024)

??? logging.info(data)?? #字節(jié)碼

??? logging.info(data.decode())?? #字符串

??? msg = 'ask {}'.format(data.decode())?? #要發(fā)送字節(jié)碼,若用data則拋TypeError異常

??? conn.send(msg.encode())

?

conn.close()

sock.close()

輸出:

2018-08-08-11:28:03?????? Thread info: 13528 MainThread <socket.socket fd=236, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('192.168.7.144', 9999)>

2018-08-08-11:28:04?????? Thread info: 13528 MainThread <socket.socket fd=240, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('192.168.7.144', 9999), raddr=('192.168.7.144', 7536)> ('192.168.7.144', 7536)

2018-08-08-11:28:47?????? Thread info: 13528 MainThread b'fuck'

2018-08-08-11:28:47?????? Thread info: 13528 MainThread fuck

2018-08-08-11:28:52?????? Thread info: 13528 MainThread b'fuck again'

2018-08-08-11:28:52?????? Thread info: 13528 MainThread fuck again

2018-08-08-11:28:58?????? Thread info: 13528 MainThread b'fuck again again'

2018-08-08-11:28:58?????? Thread info: 13528 MainThread fuck again again

44網(wǎng)絡(luò)編程_socket_TCP

44網(wǎng)絡(luò)編程_socket_TCP

?

?

群聊程序,TCP實現(xiàn):

例:

群聊程序:

需求分析:聊天工具CS程序;

服務(wù)器應(yīng)具有的功能:

啟動服務(wù),包括綁定地址端口、監(jiān)聽;

建立連接,能和多個client建立連接;

接收不同用戶信息;

分發(fā),將接收的某個用戶的信息轉(zhuǎn)發(fā)到已連接的所有client;

停止服務(wù);

記錄連接的client

?

class ChatServer:

??? def __init__(self, ip='127.0.0.1', port=9999):?? #生產(chǎn)中用ini文件配置

??????? self.sock = socket.socket()

??????? self.addr = (ip, port)

??????? self.clients = {}

??????? self.event = threading.Event()

?

??? def start(self):

??????? self.sock.bind(self.addr)

??????? self.sock.listen()

??????? threading.Thread(target=self._accept, name='accept').start()

?

??? def stop(self):

??????? for conn in self.clients.values():

??????????? conn.close()

??????? self.sock.close()

??????? self.event.wait(3)?? #TCP資源回收要些時間;UDP很快

??????? self.event.set()

?

??? def _accept(self):

??????? # while True:

??????? while not self.event.is_set():

??? ????????conn, client = self.sock.accept()

??????????? self.clients[client] = conn

??????????? threading.Thread(target=self._recv, args=(conn, client), name='recv').start()

?

??? def _recv(self, conn, client):

??????? # while True:

??????? while not self.event.is_set():

??????????? data = conn.recv(1024)?? #databytecode,此句可能有異常,建議放在try...except

??????????? data = data.strip().decode()?? #datastring

??????????? logging.info(data)

??????????? if data == 'quit':

??????????????? logging.info('...quit')

??????????????? self.clients.pop(client)

??????????????? conn.close()

??????????????? break

??????????? msg = 'ack {}'.format(data)?? #msgstring

??????????? # conn.send(msg.encode())

??????????? for c in self.clients.values():?? #注意此處c不能寫為conn,否則會將上面的conn覆蓋;一般循環(huán)次數(shù)用一個字符i,有意義的變量用多個字符

??????????????? c.send(msg.encode())?? #send的數(shù)據(jù)要為bytecode

?

cs = ChatServer()

cs.start()

?

e = threading.Event()

def showthread():

??? while not e.wait(5):

??????? logging.info(threading.enumerate())

# showthread()

# cs.stop()

threading.Thread(target=showthread, daemon=True).start()?? #用于自己監(jiān)控看,可隨時關(guān)閉

?

while True:

??? cmd = input('>>> ').strip()

??? if cmd == 'quit':

??????? cs.stop()

??????? break

?

注:

仍有問題:各種異常處理;client主動退出后server不知道;

?

?

makefile

conn,clientinfo=sock.accept(1024)

f=conn.makefile(mode='r',buffering=None,*,encoding=None,errors=None,newline=None)?? #創(chuàng)建一個與該套接字相關(guān)聯(lián)的文件對象;

?

高級接口用的是makefile;

?

例:

sock = socket.socket()

ip = '127.0.0.1'

port = 9999

addr = (ip,port)

sock.bind(addr)

sock.listen()

?

conn, addrinfo = sock.accept()

f = conn.makefile(mode='rw')

line = f.read(10)?? #等價conn.recv(1024),兩者差不多,makefile在處理字符串上容易些(不用decode()encode()

print(line)

f.write('return your msg:{}'.format(line))? ?#conn.send(msg)

f.flush()

?

?

例,使用makefile循環(huán)接收消息:

sock = socket.socket()

ip = '127.0.0.1'

port = 9999

addr = (ip,port)

sock.bind(addr)

sock.listen()

?

event = threading.Event()

?

def accept(sock, event:threading.Event):

??? conn, addrinfo = sock.accept()

??? f = conn.makefile(mode='rw')

??? while True:

??????? line = f.readline()?? #注意此處按行讀取,遇到\n才接收;類似conn.recv(1024)

??????? print(line)

??????? if line.strip() == 'quit':

??????????? break

??????? f.write('return your msg:{}'.format(line))?? #conn.send(msg)

??????? f.flush()

??? f.close()

??? sock.close()

??? event.wait(3)

??? print('end')

??? event.set()

?

threading.Thread(target=accept, args=(sock, event)).start()

while not event.wait(5):

??? print(sock)

?

例,將ChatServer改為makefile;

class ChatServer:

??? def __init__(self, ip='127.0.0.1', port=9999):

??????? self.sock = socket.socket()

??????? self.addr = (ip, port)

??????? self.clients = {}

??????? self.event = threading.Event()

?

??? def start(self):

??????? self.sock.bind(self.addr)

??????? self.sock.listen()

??????? threading.Thread(target=self._accept, name='accept').start()

?

??? def stop(self):

??????? for f in self.clients.values():

??????????? f.close()

??????? self.sock.close()

??????? self.event.wait(3)

??????? self.event.set()

?

??? def _accept(self):

? ??????while not self.event.is_set():

??????????? conn, client = self.sock.accept()

??????????? f = conn.makefile(mode='rw')

??????????? self.clients[client] = f

??????????? threading.Thread(target=self._recv, args=(f, client), name='recv').start()

?

??? def _recv(self, f, client):

??????? while not self.event.is_set():

??????????? try:

??????????????? data = f.readline()

??????????? except Exception as e:

??????????????? logging.info(e)

??????????????? data = 'quit'

??????????? data = data.strip()

??????????? logging.info(data)

?

??????????? if data == 'quit':

??????????????? logging.info('{}: ...quit'.format(client))

??????????????? self.clients.pop(client)

??????????????? f.close()

??????????????? break

?

??????????? msg = 'ack: {}'.format(data)

?? ?????????for f in self.clients.values():

??????????????? f.writelines(msg)

??????????????? f.flush()

?

cs = ChatServer()

cs.start()

?

myutils.show_threads()

?

e = threading.Event()

while not e.wait(5):

??? cmd = input('>>> ').strip()

??? if cmd == 'quit':

?? ?????cs.stop()

??????? break

?

?

TCP客戶端編程步驟:

創(chuàng)建socket對象;

連接到遠(yuǎn)端服務(wù)器的ipport,connect()方法;

傳輸數(shù)據(jù),使用send()recv()方法;

關(guān)閉連接、釋放資源;

?

TCP、UDP的客戶端是隨機(jī)的port,而服務(wù)器端是綁定死的(提供服務(wù)的場所要固定,如銀行);

?

?

例:

sock = socket.socket()?? #step 1

?

ip = '127.0.0.1'

port = 9999

addr = (ip, port)

sock.connect(addr)?? #step 2

?

sock.send(b'hello\n')?? #step3

data = sock.recv(1024)

print(data)

?

sock.close()?? #step4,好習(xí)慣,fd資源有限

?

例,ChatClient

class ChatClient():

??? def __init__(self, ip='127.0.0.1', port=9999):

??????? self.sock = socket.socket()

??? ????self.addr = (ip, port)

??????? self.event = threading.Event()

?

??? def start(self):

??????? self.sock.connect(self.addr)

??????? threading.Thread(target=self._recv, name='recv').start()

?

??? def stop(self):

??????? self.sock.close()

??????? self.event.wait(3)

??????? self.event.set()

?

??? def _recv(self):

??????? while not self.event.is_set():

??????????? try:

??????????????? data = self.sock.recv(1024)

??????????? except Exception as e:

??????????????? logging.info(e)

?????????????? ?break

??????????? logging.info(data.decode())

?

??? def send(self):

??????? self.sock.send(data.encode())

?

def main():

??? cc = ChatClient()

??? cc.start()

?

??? myutils.show_threads()

?

??? while True:

??????? cmd = input('>>> ').strip()

??????? if cmd == 'quit':

??????????? cc.send(cmd)

??????????? cc.stop()

??????????? break

??????? cc.send(cmd)

?

if __name__ == '__main__':

??? main()

?


向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI