TCP編程

Socket是網(wǎng)絡(luò)編程的一個(gè)抽象概念。通常我們用一個(gè)Socket表示“打開了一個(gè)網(wǎng)絡(luò)鏈接”,而打開一個(gè)Socket需要知道目標(biāo)計(jì)算機(jī)的IP地址和端口號,再指定協(xié)議類型即可。

TCP和UDP的區(qū)別:

  • (TCP)傳輸控制協(xié)議,是一種提供可靠數(shù)據(jù)傳輸?shù)耐ㄓ脜f(xié)議。
  • (UDP)用戶數(shù)據(jù)報(bào)協(xié)議,是一個(gè)面向無連接的協(xié)議。采用該協(xié)議不需要兩個(gè)應(yīng)用程序先建立連接。UDP協(xié)議不提供差錯恢復(fù),不能提供數(shù)據(jù)重傳,因此該協(xié)議傳輸數(shù)據(jù)安全性差。
    以上是書面內(nèi)容

1、雙方都是一種[網(wǎng)絡(luò)傳輸協(xié)議]
2、TCP需要建立連接,而UDP不需要建立連接(無連接傳輸)  
3、是否建立真實(shí)連接的特性,造成了雙方可靠性的差距。

  • TCP屬于可靠的傳輸協(xié)議:因?yàn)閭鬏斍半p方建立好了連接,相當(dāng)于買賣雙方建立好了交易合同,傳輸中一般不會出現(xiàn)意外,直到連接終止;
  • UDP屬于不可靠的傳輸協(xié)議:UDP的所謂連接相當(dāng)于一種映射,UDP單方面的認(rèn)為目標(biāo)地址(端口)是可用的,從而進(jìn)行收發(fā)數(shù)據(jù),而實(shí)際上目標(biāo)地址(端口)未必可用,所以傳輸數(shù)據(jù)不可靠

4、由于TCP需要建立真實(shí)的連接,所以需要消耗服務(wù)器的負(fù)載要大于UDP

TCP通信模型

tcp服務(wù)器

完成一個(gè)tcp服務(wù)器的功能,需要的流程如下:

  1. socket創(chuàng)建一個(gè)套接字
  2. bind綁定ip和port
  3. listen使套接字變?yōu)榭梢员粍渔溄?/li>
  4. accept等待客戶端的鏈接
  5. recv/send接收發(fā)送數(shù)據(jù)

客戶端

大多數(shù)連接都是可靠的TCP連接。創(chuàng)建TCP連接時(shí),主動發(fā)起連接的叫客戶端,被動響應(yīng)連接的叫服務(wù)器。

import socket
import time

# socket對象
clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

print('1......')

'''
連接服務(wù)器,
    如果連接上,繼續(xù)運(yùn)行
    連接不上,報(bào)錯
'''
clientSocket.connect(('192.168.11.74',8888))

print('2......')

#關(guān)閉
clientSocket.close()

服務(wù)端

import socket
import time

# 買個(gè)手機(jī)
serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 插卡
serverSocket.bind(('', 8888))
# 由飛行模式到接聽模式
serverSocket.listen(10)

print('1......')

'''
    clientAddr:連接的客戶端的信息(ip,port)
'''
# 等待電話打入
newSocket, clientAddr = serverSocket.accept()
print('2......')
print(newSocket)
print(clientAddr)

# time.sleep(100)
# 關(guān)
newSocket.close()  # 關(guān)閉之后,客戶端也會被關(guān)閉

serverSocket.close()  # 項(xiàng)目運(yùn)行中服務(wù)器一直運(yùn)行,不會關(guān)閉

tcp服務(wù)端發(fā)送和接收消息

import socket
import time

'''
serverSocket是用來接收新的客戶端的
以后與這個(gè)連接的客戶端的收發(fā)消息就不能用serverSocket了,
而是用返回來的新的newSocket
'''

serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)


serverSocket.bind(('', 8888))
serverSocket.listen(10)

newSocket, clientAddr = serverSocket.accept()


#發(fā)
sendData = input('>>')
newSocket.send(sendData.encode('gbk'))
#收
'''
    此時(shí)的recv會導(dǎo)致阻塞。
    一旦對應(yīng)客戶端斷開了,不阻塞,并返回''的字符串
'''
recvData = newSocket.recv(1024)
print(recvData.decode('gbk'))




newSocket.close()
serverSocket.close()

客戶端接受和發(fā)送消息

import socket
import time

clientSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
clientSocket.connect(('192.168.11.66',8888))


#發(fā)
sendData = input('>>')
clientSocket.send(sendData.encode('gbk'))
#收
recvData = clientSocket.recv(1024)
print(recvData.decode('gbk'))

clientSocket.close()

tcp服務(wù)器使用多線程接受多個(gè)客戶端

import socket
import time
import threading


def socketState(newSocket,clientAddr):

    while True:
        recvData = newSocket.recv(1024)
        recvData = recvData.decode('gbk')
        if recvData == '':
            print('客戶端%s退出了...'%clientAddr[0])
            newSocket.close()
            break
        else:
            print('來自于%s:%s的消息(%s):%s'%(clientAddr[0],clientAddr[1],time.strftime('%Y-%m-%d %H:%M:%S'),recvData))
            sendData = 'echo:%s'%recvData
            newSocket.send(sendData.encode('gbk'))


def main():
    #創(chuàng)建服務(wù)端socket對象
    serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    serverSocket.bind(('', 8888))
    serverSocket.listen(10)
    #循環(huán),等待多個(gè)客戶端連接
    while True:
        #等待客戶端的連接,阻塞。連接后,繼續(xù)運(yùn)行
        newSocket, clientAddr = serverSocket.accept()


        #創(chuàng)建新的線程,執(zhí)行與新客戶端的交互
        serverThread = threading.Thread(target=socketState, args=(newSocket,clientAddr))
        serverThread.start()
        # 這里不能關(guān)閉,多線程共享數(shù)據(jù)
        #newSocket.close()


if __name__ == '__main__':
    main()

服務(wù)端使用多進(jìn)程接收多個(gè)客戶端

import socket
import time
import multiprocessing

def socketState(newSocket,clientAddr):

    while True:
        recvData = newSocket.recv(1024)
        recvData = recvData.decode('gbk')
        if recvData == '':
            print('客戶端%s退出了...'%clientAddr[0])
            newSocket.close()
            break
        else:
            print('來自于%s:%s的消息(%s):%s'%(clientAddr[0],clientAddr[1],time.strftime('%Y-%m-%d %H:%M:%S'),recvData))
            sendData = 'echo:%s'%recvData
            newSocket.send(sendData.encode('gbk'))

def main():
    #創(chuàng)建服務(wù)端socket對象
    serverSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    serverSocket.bind(('', 8888))
    serverSocket.listen(10)
    #循環(huán),等待多個(gè)客戶端連接
    while True:
        #等待客戶端的連接,阻塞。連接后,繼續(xù)運(yùn)行
        newSocket, clientAddr = serverSocket.accept()

        #創(chuàng)建新的進(jìn)程,執(zhí)行與新客戶端的交互
        serverProcess = multiprocessing.Process(target=socketState, args=(newSocket,clientAddr))
        serverProcess.start()
        '''
            這里要關(guān)閉。
            子進(jìn)程會單獨(dú)分配與父進(jìn)程相同的內(nèi)容,地址不同(深拷貝)
       '''
        newSocket.close()


if __name__ == '__main__':
    main()

總結(jié):

  • 用TCP協(xié)議進(jìn)行Socket編程在Python中十分簡單,對于客戶端,要主動連接服務(wù)器的IP和指定端口,對于服務(wù)器,要首先監(jiān)聽指定端口,然后,對每一個(gè)新的連接,創(chuàng)建一個(gè)線程或進(jìn)程來處理。通常,服務(wù)器程序會無限運(yùn)行下去。

  • 同一個(gè)端口,被一個(gè)Socket綁定了以后,就不能被別的Socket綁定了。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 個(gè)人認(rèn)為,Goodboy1881先生的TCP /IP 協(xié)議詳解學(xué)習(xí)博客系列博客是一部非常精彩的學(xué)習(xí)筆記,這雖然只是...
    貳零壹柒_fc10閱讀 5,196評論 0 8
  • 1.這篇文章不是本人原創(chuàng)的,只是個(gè)人為了對這部分知識做一個(gè)整理和系統(tǒng)的輸出而編輯成的,在此鄭重地向本文所引用文章的...
    SOMCENT閱讀 13,377評論 6 174
  • 參考:http://www.2cto.com/net/201611/569006.html TCP HTTP UD...
    F麥子閱讀 3,069評論 0 14
  • 我有一個(gè)朋友,很好的很好的朋友,兩年前鬧掰的,因?yàn)橐患苄〉氖虑?,雙方都過于自我,以為對方離了自己就沒法過,然而事...
    大炮筒閱讀 331評論 0 0
  • 故鄉(xiāng)的小溪嘻嘻嘩嘩 故鄉(xiāng)的小溪泛起水花 可我的故鄉(xiāng)在哪?
    東野顧城閱讀 222評論 0 1

友情鏈接更多精彩內(nèi)容