UDP通信を使うチャットプログラムを改変して、
小さいサイズの画像も送信できないかと、
画像を分割して送信するようにプログラムをつくったつもりですが、
画像データを受信できませんでした。
片方のパソコンからもう片方のパソコンに同じこのプログラムのtest.pyファイルを入れて、お互いIPアドレスを入力して通信する環境です。
テキストの通信はできています。
次のプログラムをどう修正すれば画像が遅れて受信できるようになりますでしょうか?
よろしくお願いします。
import tkinter as tk
from PIL import Image, ImageTk
import socket
import threading
from tkinter import filedialog
import base64
import io
# Port番号
PORTNUM = 8000
BUFFER_SIZE = 1024 # 画像データのバッファサイズ
class Application(tk.Frame):
def __init__(self, master=None):
super().__init__(master)
self.master.title("画像の表示") # ウィンドウタイトル
self.master.geometry("600x400") # ウィンドウサイズ(幅x高さ)
# Label for displaying received image
self.image_label = tk.Label(self.master)
self.image_label.place(x=280, y=160, width=160, height=120)
# Label for displaying image preview
self.preview_label = tk.Label(self.master)
self.preview_label.place(x=10, y=190, width=160, height=120)
# Button for selecting an image
self.select_img_button = tk.Button(self.master, text='画像を選択', command=self.select_image)
self.select_img_button.place(x=280, y=290, width=120, height=30)
# Button for sending an image
self.send_img_button = tk.Button(self.master, text='画像を送信', command=self.send_image)
self.send_img_button.place(x=10, y=290, width=120, height=30)
# UDP socket setup
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.sock.bind(('', PORTNUM))
self.sock.settimeout(0.1)
# Entry widgets
self.ipaddr = tk.StringVar()
self.ipaddr.set('192.168.8.x')
ent1 = tk.Entry(self.master, textvariable=self.ipaddr)
ent1.place(x=120, y=20, width=120, height=20)
self.txdata = tk.StringVar()
self.txdata.set('send data')
ent2 = tk.Entry(self.master, textvariable=self.txdata)
ent2.place(x=10, y=100, width=260, height=20)
self.rxdata = tk.StringVar()
self.rxdata.set('receive data')
ent3 = tk.Entry(self.master, textvariable=self.rxdata)
ent3.place(x=10, y=160, width=260, height=20)
# Button for sending data
btn = tk.Button(self.master, text='送信', command=self.button_click)
btn.place(x=280, y=100, width=120, height=40)
# Start the timer thread
self.thread = threading.Thread(target=self.timerctrl)
self.thread.daemon = True
self.thread.start()
def resize_image(self, img_data, size=(160, 120)):
img = Image.open(io.BytesIO(img_data))
img = img.resize(size, Image.LANCZOS) # Use LANCZOS resampling filter
img = ImageTk.PhotoImage(img)
return img
# タイマー処理(約1秒周期)
def timerctrl(self):
try:
# UDP受信
recvdata, fromdata = self.sock.recvfrom(BUFFER_SIZE)
# 受信データを変換
recvtext = recvdata.decode('utf-8')
except socket.timeout: # 受信タイムアウト
recvtext = ''
# データを受信した場合
if recvtext.startswith("IMG:"):
# 受信データが画像データである場合
img_data = base64.b64decode(recvtext[4:])
img = self.resize_image(img_data)
self.image_label.config(image=img)
self.image_label.image = img
elif recvtext: # 画像データでなく、かつデータが存在する場合
self.rxdata.set(recvtext)
# タイマーを再設定
self.master.after(1000, self.timerctrl)
No.1ベストアンサー
- 回答日時:
Pythonはいまいち理解度がアレですが…
UDPパケットの場合、もろもろの送達保証はなかったはずです。
https://ja.wikipedia.org/wiki/User_Datagram_Prot …
送信側は複数回(?)で送信しているようですが、受信側はそれを正しく再構築できているんでしょうか?
複数回のどれか一つでも欠落した場合に再送処理なんてものはしてくれませんし、そもそも受信する順番自体が保証されません。
1/2/3/4/5/6…という順番で送信しても、受信側で2/6/4/2/1…のように受信される可能性もあるわけですが、そこらへんは大丈夫ですか?
(同じパケットが複数届く(通った経路が違う)ということもある…とされています)
# ローカルネットワークとか、LANケーブル直結とかならそうそうパケット欠落やら順番が入れ替わることはないかもしれませんけど。
チャット程度であればたいしたサイズのパケットにはならないでしょうから順番が入れ替わるということは考慮しなくても大丈夫かもしれませんが、
バイナリデータで欠落があった場合にどうなるか…とかも考慮が必要でしょう。
UDPヘッダ的には1パケットで約64k程度は送れる…ようですが、下位レイヤーで分割されたパケットが正しく復元されるのかはちょっと不明ですね。
# 判定用のチェックサムがあるので、チェックサム異常となれば、UDPパケット自体が破棄されておしまいでしょう。(破棄されたこと自体の通知もない…んじゃないでしょうかねぇ)
ということで、UDPでやるならばそういうパケットロスやら順番やら再送やらのプロトコルは自前で処理する必要があるかと。
いやなら素直にTCPでやりとり…でしょうね。
多対多になるチャットの場合にTCPで…というのは厳しいかもしれませんげと。
あと…本筋ではありませんが、バイナリデータをBASE64で送る場合は元データより最終的なサイズが増えますので注意しましょう。
https://ja.wikipedia.org/wiki/Base64#%E5%95%8F%E …
UDPでやるならばパケットロスやらデータ送信順番やら再送やらのプロトコルは自前で処理する必要があるんですね。もう一度一から考え直します。ありがとうございました。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
関連するカテゴリからQ&Aを探す
おすすめ情報
- ・漫画をレンタルでお得に読める!
- ・あなたの人生で一番ピンチに陥った瞬間は?
- ・初めて見た映画を教えてください!
- ・今の日本に期待することはなんですか?
- ・【大喜利】【投稿~1/31】『寿司』がテーマの本のタイトル
- ・集中するためにやっていること
- ・テレビやラジオに出たことがある人、いますか?
- ・【お題】斜め上を行くスキー場にありがちなこと
- ・人生でいちばんスベッた瞬間
- ・コーピングについて教えてください
- ・あなたの「プチ贅沢」はなんですか?
- ・コンビニでおにぎりを買うときのスタメンはどの具?
- ・おすすめの美術館・博物館、教えてください!
- ・【お題】大変な警告
- ・【大喜利】【投稿~1/20】 追い込まれた犯人が咄嗟に言った一言とは?
- ・洋服何着持ってますか?
- ・みんなの【マイ・ベスト積読2024】を教えてください。
- ・「これいらなくない?」という慣習、教えてください
- ・今から楽しみな予定はありますか?
- ・AIツールの活用方法を教えて
- ・最強の防寒、あったか術を教えてください!
- ・【大喜利】【投稿~1/9】 忍者がやってるYouTubeが炎上してしまった理由
- ・歳とったな〜〜と思ったことは?
- ・モテ期を経験した方いらっしゃいますか?
- ・好きな人を振り向かせるためにしたこと
- ・スマホに会話を聞かれているな!?と思ったことありますか?
- ・それもChatGPT!?と驚いた使用方法を教えてください
- ・見学に行くとしたら【天国】と【地獄】どっち?
- ・これまでで一番「情けなかったとき」はいつですか?
- ・この人頭いいなと思ったエピソード
- ・あなたの「必」の書き順を教えてください
- ・14歳の自分に衝撃の事実を告げてください
- ・人生最悪の忘れ物
- ・あなたの習慣について教えてください!!
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
画像をクリックして元に戻すには
-
imgタグは何で囲むのが良いか
-
ホームページの一番下に配置し...
-
inputタグでサーバにデータを送...
-
リンクを知らせる手のマークが...
-
アイコンと文字を並べる時アイ...
-
cssで、チェックボックスの画像...
-
画像をクリックして同じページ...
-
3つの画像を中央に寄せて表示さ...
-
リンク画像のマウスオーバー時...
-
cssヘッダー画像の下に配置した...
-
ボタンをセル内一杯に表示させ...
-
htmlの文字が縦書きになる
-
htmlのolやulなどlistにtitleや...
-
html/cssの、navを2段にする...
-
質問1.
-
マージソートの計算量について-...
-
リストマーカーをボックス内に...
-
form input テキストを上下中央...
-
wordpressでキャプション内で改...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
ボタンをセル内一杯に表示させ...
-
リンクを知らせる手のマークが...
-
favicon.ico はもういらない?
-
機種依存文字、m2(平方メート...
-
inputタグでサーバにデータを送...
-
cssで、チェックボックスの画像...
-
【HTML/CSS】ボタンの枠が伸び...
-
UDP通信を使うチャットプログラ...
-
htmlで画像を2個ずつ並べていき...
-
プルダウンの選択リストの中に...
-
cssヘッダー画像の下に配置した...
-
画像をクリックして同じページ...
-
レスポンシブ対応のHTML・CSS(...
-
c言語を用いて画像の透明度をあ...
-
HTMLのIMAGEに。。
-
XML画像データををHTMLで簡単に...
-
画像の横に文字をうまく配置で...
-
footerの背景が切れて、背景画...
-
table で画像をピッタリとくっ...
-
ポップアップウィンドウのサイ...
おすすめ情報
# 画像を選択する関数
def select_image(self):
img_path = filedialog.askopenfilename()
with open(img_path, "rb") as img_file:
img_data = base64.b64encode(img_file.read()).decode('ascii')
selected_img = self.resize_image(base64.b64decode(img_data))
self.preview_label.config(image=selected_img)
self.preview_label.image = selected_img
# 画像データを送信
self.sock.sendto(bytes("IMG:" + img_data, 'utf-8'), (self.ipaddr.get(), PORTNUM))
# 画像を送信する関数
def send_image(self):
img_path = filedialog.askopenfilename()
with open(img_path, "rb") as img_file:
img_data = base64.b64encode(img_file.read()).decode('ascii')
# 画像データを分割して送信
chunk_size = 800 # データを分割するサイズ
img_data_bytes = bytes("IMG:" + img_data, 'utf-8')
for i in range(0, len(img_data_bytes), chunk_size):
chunk = img_data_bytes[i:i+chunk_size]
self.sock.sendto(chunk, (self.ipaddr.get(), PORTNUM))
# 画像データを分割して送信
chunk_size = 800 # データを分割するサイズ
img_data_bytes = bytes("IMG:" + img_data, 'utf-8')
for i in range(0, len(img_data_bytes), chunk_size):
chunk = img_data_bytes[i:i+chunk_size]
self.sock.sendto(chunk, (self.ipaddr.get(), PORTNUM))
# ボタンが押されたときに実行する関数
def button_click(self):
addr = self.ipaddr.get() # 送信先IPアドレスを格納する
data = self.txdata.get() # 送信データを格納する
# UDP送信
self.sock.sendto(bytes(data, 'utf-8'), (addr, PORTNUM))
if __name__ == "__main__":
root = tk.Tk()
app = Application(master=root)
app.mainloop()
(誤)どう修正すれば画像が遅れて受信できるようになります
→(正)どう修正すれば画像が送信できて受信できるようになります