8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

从客户端向客户端发送 TCP/fork/socket

Melloware 2月前

55 0

我正在尝试编写一个聊天程序,协议是 TCP,两个文件是 server.c 和 client.c,如果客户端和客户端是通过 fork 创建的并且具有相同的套接字描述符,那么如何从客户端向客户端发送消息。没有...

我正在尝试编写一个聊天程序,协议是 TCP,两个文件是 server.c 和 client.c如果它们是通过 fork 创建的并且具有相同的套接字描述符,则如何从客户端向客户端发送消息。

无论我如何尝试发送消息,它都会出现在发送消息的同一控制台窗口中

帖子版权声明 1、本帖标题:从客户端向客户端发送 TCP/fork/socket
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由Melloware在本站《sockets》版块原创发布, 转载请注明出处!
最新回复 (0)
  • Rock 2月前 0 只看Ta
    引用 1

    我目前正在开展一个项目,试图将视频从 Tello 无人机流式传输到使用 React Native 和 Expo 构建的移动应用程序,我已经在我的应用程序上完成了 EAS Build ...

    我目前正在开展一个项目,试图将视频从 Tello 无人机传输到使用 React Native 和 Expo 构建的移动应用程序,我已经在手机上为应用程序进行了 EAS Build。我正在使用 react-native-vlc-media-player 库来处理视频流。

    以下是我的 DroneScreen.js 文件中的相关代码:

    import React, { useEffect, useState, useRef } from "react";
    import {
      Button,
      StyleSheet,
      View,
      Dimensions,
      Image,
      TouchableOpacity,
      ScrollView,
      Animated,
      ActivityIndicator,
    } from "react-native";
    import { useFocusEffect } from "@react-navigation/native";
    import dgram from "react-native-udp";
    import KText from "../components/KText";
    import { VLCPlayer } from "react-native-vlc-media-player";
    import _ from "lodash";
    import Icon from "react-native-vector-icons/FontAwesome5";
    
    const screenWidth = Dimensions.get("window").width;
    
    export default function DroneScreen() {
      const [isConnected, setIsConnected] = useState(false);
      const [isStreaming, setIsStreaming] = useState(false);
      const [isLoading, setIsLoading] = useState(false);
      const [isInitialScreen, setIsInitialScreen] = useState(true);
      const [fadeAnim] = useState(new Animated.Value(0));
    
      const clientRef = useRef(null);
      const serverRef = useRef(null);
      const videoServerRef = useRef(null);
      const playerRef = useRef(null);
    
      useFocusEffect(
        React.useCallback(() => {
          fadeAnim.setValue(0);
    
          Animated.timing(fadeAnim, {
            toValue: 1,
            duration: 300,
            useNativeDriver: true,
          }).start();
        }, [])
      );
    
      useEffect(() => {
        clientRef.current = dgram.createSocket("udp4");
        serverRef.current = dgram.createSocket("udp4");
        videoServerRef.current = dgram.createSocket("udp4");
    
        try {
          clientRef.current.bind(8889);
          serverRef.current.bind(8890);
          videoServerRef.current.bind(11111);
        } catch (err) {
          console.error("Failed to bind socket", err);
          return;
        }
    
        let timeoutId = null;
        let intervalId = null;
    
        let state = null;
        let rinfo = null;
    
        serverRef.current.on("message", (msg, rinfo) => {
          state = parseState(msg.toString());
          rinfo = rinfo;
          setIsConnected(true);
        });
    
        intervalId = setInterval(() => {
          if (state && rinfo) {
            console.log(`server got: ${state} from ${rinfo.address}:${rinfo.port}`);
          }
        }, 15000);
    
        videoServerRef.current.on("message", (msg, rinfo) => {
          setIsStreaming(true);
        });
    
        timeoutId = setTimeout(() => {
          setIsConnected(false);
        }, 5000);
    
        return () => {
          clientRef.current.close();
          serverRef.current.close();
          videoServerRef.current.close();
    
          clearTimeout(timeoutId);
          clearInterval(intervalId);
        };
      }, []);
    
      function parseState(state) {
        return state
          .split(";")
          .map((x) => x.split(":"))
          .reduce((data, [key, value]) => {
            data[key] = value;
            return data;
          }, {});
      }
    
      const sendCommand = (command) => {
        clientRef.current.send(
          command,
          0,
          command.length,
          8889,
          "192.168.10.1",
          (err) => {
            if (err) {
              console.error("Failed to send command", err);
              setIsConnected(false);
            } else {
              console.log("Command sent: " + command);
            }
          }
        );
      };
    
      const connectAndStartStreaming = () => {
        setIsLoading(true);
        sendCommand("command");
        setTimeout(() => {
          sendCommand("streamon");
          setIsInitialScreen(false);
          setIsLoading(false);
        }, 5000);
      };
    
      return (
        <ScrollView contentContainerStyle={styles.container}>
          {isInitialScreen || !isConnected ? (
            <View style={styles.innerContainer}>
              <Animated.Image
                source={require("../assets/tello-img.png")}
                style={{
                  width: screenWidth * 1.3,
                  resizeMode: "contain",
                  position: "absolute",
                  opacity: fadeAnim,
                }}
              />
              <View style={styles.greyContainer}>
                <TouchableOpacity
                  style={[
                    styles.connectButton,
                    { bottom: 120, flexDirection: "row" },
                  ]}
                  onPress={connectAndStartStreaming}
                  disabled={isLoading}
                >
                  {isLoading ? (
                    <ActivityIndicator size="small" color="#fff" />
                  ) : (
                    <>
                      <Icon name="link" size={20} color="#fff" />
                      <KText style={styles.connectButtonText}>Connect</KText>
                    </>
                  )}
                </TouchableOpacity>
              </View>
            </View>
          ) : (
            <>
              {isStreaming &&
                (console.log("isStreaming:", isStreaming),
                (
                  <VLCPlayer
                    style={{ width: "100%", height: "100%" }}
                    source={{ uri: "udp://@0.0.0.0:11111" }}
                    autoplay={false}
                    initOptions={["--network-caching=150", "--rtsp-tcp"]}
                    onError={(e) => {
                      console.log("onError", e);
                      setIsConnected(false);
                      setIsStreaming(false);
                    }}
                    onOpen={(e) => {
                      console.log("onOpen", e);
                      if (
                        playerRef.current &&
                        typeof playerRef.current.play === "function"
                      ) {
                        playerRef.current.play();
                      }
                    }}
                    onBuffering={(e) => {
                      console.log("onBuffering", e);
                      if (
                        e.bufferRate > 0 &&
                        playerRef.current &&
                        typeof playerRef.current.play === "function"
                      ) {
                        playerRef.current.play();
                      }
                    }}
                    onPlaying={(e) => console.log("onPlaying", e)}
                    ref={playerRef}
                  />
                ))}
              <View style={styles.innerContainer}>
                <TouchableOpacity
                  style={styles.buttonContainer}
                  onPress={() => sendCommand("takeoff")}
                >
                  <KText style={styles.buttonText}>Take Off</KText>
                </TouchableOpacity>
                <TouchableOpacity
                  style={styles.buttonContainer}
                  onPress={() => sendCommand("land")}
                >
                  <KText style={styles.buttonText}>Land</KText>
                </TouchableOpacity>
                <TouchableOpacity
                  style={styles.buttonContainer}
                  onPress={() => sendCommand("streamoff")}
                >
                  <KText style={styles.buttonText}>Off Stream</KText>
                </TouchableOpacity>
              </View>
            </>
          )}
        </ScrollView>
      );
    }
    
    ....
    

    我面临的问题是,我无法在移动应用程序上接收来自无人机的视频流。我已确认我的手机已通过 WiFi 连接到无人机,并且无人机已配置为将视频流发送到端口 11111。我能够连接并发送有效的命令(如“takeoff”)。isStreaming 状态变量已设置为 true,这表明“streamon”命令正在运行。

    我尝试了各种解决方案,包括将套接字绑定到端口 11111,但这似乎不起作用,网上大多数解决方案都是针对 Python 定制的。我还检查了控制台日志中是否有任何错误或警告,但似乎没有与此问题相关的。我手机上的官方 Tello 应用程序能够显示视频。

    我将非常感激任何关于如何解决此问题的帮助或建议。谢谢!

  • ekse 2月前 0 只看Ta
    引用 2

    我有这段代码:import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.InetAddress;import java.net.ServerSocket;import java.net.Socket;import java...

    我有这个代码:

    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.net.InetAddress;
    import java.net.ServerSocket;
    import java.net.Socket;
    import java.nio.ByteBuffer;
    
    public class Node {
        private String serverIpAddress;
        private String nextHopIpAddress;
        private int nextHopPort;
    
        private ServerSocket nodeServerSocket;
        private Socket nextNodeSocket;
    
        public Node(String serverIpAddress, int serverPort, String nextHopIpAddress, int nextHopPort) {
            this.serverIpAddress = serverIpAddress;
            this.nextHopIpAddress = nextHopIpAddress;
            this.nextHopPort = nextHopPort;
    
            try {
                nodeServerSocket = new ServerSocket(serverPort, 10, InetAddress.getByName(serverIpAddress));
                System.out.println("Server is listening on " + serverIpAddress + ":" + serverPort);
    
                new Thread(() -> {
                   while (true) {
                       try {
                           Socket incomingConnection = nodeServerSocket.accept();
                           handleIncomingConnections(incomingConnection);
                       } catch (IOException exception) {
                           exception.printStackTrace();
                       }
                   }
                }).start();
    
            } catch (IOException exception) {
                exception.printStackTrace();
            }
        }
    
        private void handleIncomingConnections(Socket incomingConnection) {
            try {
                byte[] buffer = new byte[4];
                InputStream inputStream = incomingConnection.getInputStream();
    
                int bytesRead = inputStream.readNBytes(buffer, 0, buffer.length);
    
                int receivedValue = ByteBuffer.wrap(buffer).getInt();
    
                System.out.println(serverIpAddress + " received " + receivedValue + " from " + incomingConnection.getInetAddress());
    
                if (receivedValue == 100) {
                    closeConnections();
                    return;
                }
    
                sendData(++receivedValue);
            } catch (IOException exception) {
                exception.printStackTrace();
            }
        }
    
        public void sendData(int value) {
            try {
                if (nextNodeSocket == null || nextNodeSocket.isClosed() || !nextNodeSocket.isConnected()) {
                    nextNodeSocket = new Socket(nextHopIpAddress, nextHopPort);
                    System.out.println(serverIpAddress + " connected to " + nextHopIpAddress);
                }
    
                OutputStream outputStream = nextNodeSocket.getOutputStream();
                System.out.println(serverIpAddress + " sent " + value + " to " + nextHopIpAddress);
    
                outputStream.write(ByteBuffer.allocate(4).putInt(value).array());
    
            } catch (IOException exception) {
                exception.printStackTrace();
            }
        }
    
        private void closeConnections() {
            try {
                if (nextNodeSocket != null && !nextNodeSocket.isClosed()) {
                    nextNodeSocket.close();
                }
    
                if (nodeServerSocket != null && !nodeServerSocket.isClosed()) {
                    nodeServerSocket.close();
                }
            } catch (IOException exception) {
                exception.printStackTrace();
            }
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            Node node1 = new Node("127.0.0.1", 2345, "127.0.0.2", 3456);
            new Node("127.0.0.2", 3456, "127.0.0.3", 4567);
            new Node("127.0.0.3", 4567, "127.0.0.1", 2345);
    
            node1.sendData(1);
        }
    }
    

    此代码的目的是类似于这样的环形通信:

    127.0.0.1 通过向下一个节点发送值 1 来启动通信,每个节点都会增加该值,直到达到 100,然后通信应停止并关闭连接。建立连接后,它应保持打开状态,直到通信停止。这是我运行此程序时的输出:

    Server is listening on 127.0.0.1:2345
    Server is listening on 127.0.0.2:3456
    Server is listening on 127.0.0.3:4567
    127.0.0.1 connected to 127.0.0.2
    127.0.0.1 sent 1 to 127.0.0.2
    127.0.0.2 received 1 from /127.0.0.1
    127.0.0.2 connected to 127.0.0.3
    127.0.0.2 sent 2 to 127.0.0.3
    127.0.0.3 received 2 from /127.0.0.1
    127.0.0.3 connected to 127.0.0.1
    127.0.0.3 sent 3 to 127.0.0.1
    127.0.0.1 received 3 from /127.0.0.1
    127.0.0.1 sent 4 to 127.0.0.2
    

    一切看起来都很好,直到 \'127.0.0.3 向 127.0.0.1 发送了 3 个消息\' 和 \'127.0.0.1 从 /127.0.0.1 接收到了 3 个消息\'。127.0.0.1 怎么能从自身接收消息呢?最后一行之后,程序似乎卡住了。我在这里做错了什么?

  • 我正在尝试使用 Wifi Aware 技术在发布者和订阅者之间建立套接字连接。我正在关注来自 android 开发者网站的以下链接:https://developer.android.com/

    我正在尝试使用 Wifi Aware 技术在发布者和订阅者之间创建套接字连接。我正在关注来自 Android 开发者网站的以下链接:

    https://developer.android.com/develop/connectivity/wifi/wifi-aware#create_a_connection

    如上所述,我从订阅者发送一条消息,该消息在服务器端成功接收,但是当我在发布者上创建服务器套接字后请求网络时,既没有收到错误,也没有接收/创建任何网络:

    // 发布时调用的发布者回调方法。当消息发送成功时,它将调用 server.accept() 进行监听

    public void onMessageReceived(PeerHandle peer, byte[] message, Context mContext) {
        
       ......
       try{
          publisherSocket = new ServerSocket(0);
          publisherPort = publisherSocket.getLocalPort(0);
       }
        catch(...) {   .... }
    
       NetworkSpecifier networkSpecifier = new WifiAwareNetworkSpecifier.Builder(currentDiscoverySession,currentPublisherConnectedPeer).setPskPassphrase("some8Letter@").setPort(publisherPort).build;
    
       NetworkRequest publisherNetworkRequest = new NetworkRequest.Builder().addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE).setNetworkSpecifier(networkSpecifier).build();
    
        ConnectivityManager.NetworkCallback callback = new ConnectivityManager.NetworkCallback(){
           public void onAvailable(Network network) {
              // Problem is it was never called which means network not there
              currentDiscoverySession.sendMessage(currentPublisherConnectedPeer, 0, someDummyMessage);
           }
           
          .....};
    
       // Here ConnectivityManager is obatined using this.getSystemService(this.CONNECTIVITY_SERVICE) on onCreate of activity
       connectivityManager.requestNetwork(publisherNetworkRequest, callback);
    

    // 订阅调用时的回调方法

    public void onMessageReceived(PeerHandle peer, byte[] message, Context mContext) {
        
       NetworkSpecifier networkSpecifier = new WifiAwareNetworkSpecifier.Builder(currentDiscoverySession,peer).setPskPassphrase("some8Letter@").build();
       
       NetworkRequest subscriberNetworkRequest = new NetworkRequest.Builder().addTransportType(NetworkCapabilities.TRANSPORT_WIFI_AWARE).setNetworkSpecifier(networkSpecifier).build();
    
       ConnectivityManager.NetworkCallback callback = new ConnectivityManager.NetworkCallback(){
           public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities){
              .....
             WifiAwareNetworkInfo subscriberNetworkInfo = (WifiAwareNetworkInfo) networkCapabilities.getTransportInfo();
             
             subscriberSocket = network.getSocketFactory().createSocket(subscriberNetworkInfo.getPeerIpv6Addr(), subscriberNetworkInfo.getPort());
          // WRite some message using outputStream
          .....};
    
    
    

    我的清单文件:

    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="34"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    

    最小开发工具包:34 目标开发工具包:34

    我不知道为什么当我调用 requestNetworkMethod 时它没有通过 Wifi-Aware 返回任何网络。

    我尝试以毫秒为单位传递时间,发现调用了 onUnavailable() 方法,这基本上意味着没有这样的网络存在/创建。

    我想知道是否有可能通过 Wifi-Aware 创建这样的套接字连接,如果是的话,那么我的代码是否有问题,或者是否由于硬件限制,如果是,我该如何知道?

    我的最终目标是为 2 个应用创建一个视频通话应用,以便它们通过 WIFI-Aware 进行通信,我的目标是通过此网络发送和接收原始视频字节,而不使用任何预先存在的协议(如 WebRTC)。如果您发现此方法存在任何问题,也请告诉我。

  • 我正在开发一个简单的客户端/服务器应用程序,它需要一个入口点(从外部进程调用);为了简单起见,当前的架构有点像这样:Client.py ...

    我正在开发一个简单的客户端/服务器应用程序,它需要一个入口点(从外部进程调用);为了简单起见,当前的架构有点像这样:

    • Client.py <-- 这是入口点
    • Server.py <-- 这需要在后台运行
    • Shared.py <-- 共享/公共变量/对象
    • ...

    里面 Shared.py 有一些共享变量/信息(例如主机名、端口、密钥等),还有程序中使用的各种记录器的设置。客户端和服务器通过 multiprocessing.connection 套接字句柄进行通信。

    我无法改变这个应用程序只有一个入口点的事实;因此,在里面 Client.py 有一行这样的代码: Popen([sys.executable, 'Server.py') 这应该在单独的进程中启动另一个解释器。

    这是因为在最终版本中,这将需要作为(实际上是两个)可执行文件独立发布,并通过对 getattr(sys, 'frozen', False) 客户端/服务器的运行时检查来确定它是从实时解释器运行还是捆绑应用程序运行。

    事实是,如果我关闭(终止)第一个解释器,则“服务器”也会停止运行。这是因为文件 Shared.py 是从客户端和服务器导入的吗?此文件包含记录器对象,并且在客户端和服务器中,该文件都作为星型导入导入。

    我实际上需要让 Server.py 即使在 执行完毕后仍继续运行 Client.py (这样即使服务器仍在运行,它也会向调用进程返回一个值),但我搞不懂。解决脚本对 中包含的对象相互依赖的明显解决方案 Shared.py 是在两个完全不同的位置有两个单独的项目(或者只是拆分文件 Shared.py 并将其重新分发到客户端和服务器),但这仍然不能解决第二个解释器在第一个解释器关闭后终止的问题。

    我是否遗漏了某些显而易见的东西?提前感谢您的帮助。

    编辑:这是一个最小的可重现示例:

    共享.py

    import logging
    
    hostname = 'localhost'
    port = 6000
    
    ADDRESS = (hostname, port)
    
    # Log to file
    logger = logging.getLogger()
    
    # Set the log level
    logger.setLevel(logging.DEBUG)
    
    handler = logging.FileHandler('server.log')
    
    handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
    
    logger.addHandler(handler)
    

    客户端.py

    from multiprocessing.connection import Client, Connection
    from subprocess import Popen
    import time
    import sys
    
    from Shared import ADDRESS
    
    # Start process for the Server
    process = Popen([sys.executable, 'Server.py'])
    
    time.sleep(0.005)
    
    conn: Connection = Client(ADDRESS)
    # Send some data
    conn.send('Hello World')
    
    while True:
        _in = input('Enter a message: ')
        conn.send(_in)
        if _in == 'exit':
            break
    print('Exiting client!')
    

    服务器.py

    from multiprocessing.connection import Listener, Connection
    from Shared import ADDRESS, logger
    import time
    
    listener = Listener(ADDRESS)
    
    conn: Connection = listener.accept()
    
    logger.info('Server started!')
    
    while True:
        try:
            msg = conn.recv()
            logger.info(msg)
        except EOFError:
            pass
    
        time.sleep(1)
    

    通过 启动程序 python Client.py ;它会要求输入,服务器会将输入记录到文件中。但是,如果您停止客户端(例如使用 Ctrl+C),服务器也会停止运行;您可以通过注释此行来检查这一点: process = Popen([sys.executable, 'Server.py']) on Client.py 。服务器不应该在单独的进程上运行吗?为什么当客户端的解释器终止时它会停止?

  • Jar 2月前 0 只看Ta
    引用 5

    我正在开发一款 Wear OS 应用,尝试通过套接字连接访问 IP 地址列表。这些 IP 地址毫无意义(例如 1.1.1.1),但套接字仍可连接。您可以重现...

    我正在开发一款 Wear OS 应用,尝试通过套接字连接访问 IP 地址列表。这些 IP 地址毫无意义(例如 1.1.1.1),但套接字仍可连接。您可以通过停用 Wifi 和移动数据并通过蓝牙连接到手机来重现此问题。

    编辑:我发现这是因为使用蓝牙时连接由 Google 代理 --> firebaselogging.googleapis.com/1.1.1.1 。当然,这个 URL 可以通过套接字访问。你知道我如何使用套接字测试连接吗?

    val connectionTestState = try {
        socket.connect(InetSocketAddress(connection.ip, connection.port), 1000)
        Log.d("CONNECTION_TEST", "connection available for ${connection.ip}")
        ConnectionTestState.Reachable
    } catch (e: Exception) {
        Log.d("CONNECTION_TEST", "connection error for ${connection.ip} ${e.message}")
        ConnectionTestState.NotReachable
    } finally {
        socket.close()
    }
    

    我正在使用 Pixel Watch 2。

  • 由于默认的内部路由(同一子网),127.0.0.1 可以连接到 127.0.0.2..255。因此,127.0.0.1 很可能被选为出站套接字的本地 IP 地址。在您的例子中,您希望它与节点正在监听的 IP 地址相同。即使不设置出站 IP 地址,代码可能仍能工作,但输出会令人困惑。

  • mikl 2月前 0 只看Ta
    引用 8

    你的建议奏效了!但我不明白,为什么套接字可以有本地 IP 地址?在什么情况下会发生这种情况?

  • 不幸的是,这是我的学习作业。我需要专门通过 fork 编写 TCP 聊天。所以我不能使用 pthread 和 select。我将不得不忍受并做一些类似第一个解决方案的事情。谢谢你的回答。

  • 问题是,无论何时 fork() ,创建的子进程都是一个独立的进程,具有自己的私有内存空间和单独的文件描述符表。它将继承其父进程的文件描述符,但此后,父进程中稍后创建的任何文件描述符都将在(已经存在的)子进程中不可用。

    这意味着,例如,如果您的服务器接收到第一个 TCP 连接并创建文件描述符 123 来表示它,并分叉一个子进程来处理它,然后接收到第二个 TCP 连接,创建文件描述符 124 来表示它,并分叉第二个子进程来处理它,则第一个子进程将根本无法使用文件描述符 124,因为该文件描述符从未添加到其文件描述符表中。

    如果您想让子进程向其他子进程发送数据,最好的办法是让子进程将数据发送给父进程,然后让父进程将该数据转发给所有其他子进程。但请注意,这并非易事,因为您需要通过每个子进程的标准输入/标准输出文件描述符传递这些数据,或者使用或类似方法设置与每个子进程的连接 socketpair()

    如果您想避免所有这些复杂情况,最好生成线程而不是分叉子进程,因为这样所有线程至少都可以访问所有文件描述符(因为它们都在同一内存空间中一起执行)。但是,您需要处理锁定/同步/所有权问题,以确保所有这些线程都能安全地访问文件描述符。

    第三种选择是使用非阻塞 I/O 和 select() /或 poll() 多路复用 I/O 操作在单个线程中执行所有服务器操作。这是我通常默认的做法,因为尽管实现起来有点复杂,但根据我的经验,它仍然是三种方法中最容易 100% 正确的方法。

返回
作者最近主题: