socket相关

socket是操作系统中I/O系统的延伸部分,它可以使进程和机器之间的通信成为可能。建立socket,对于一个客户端程序来说,建立一个socket需要两个步骤。首先,您需要建立一个实际的socket对象。其次,您需要把它链接到远程服务器上。
在建立socket对象的时候,您需要告诉系统两件事:通信类型和协议家族。通信类型指明了用什么协议传输数据。协议的例子包括IPv4、IPv6、IPX/SPX和AFP。到目前为止最通用的是IPv4,洗衣家族则定义数据如何传输通信类型基本上都是AF_INET(和IPv4对应)。协议家族一般是表示TCP通信的SOCK_STREAM或表示UDP通信的SOCK_DGRAM。对于TCP通信,建立一个socket连接,一般用类似这样的代码:

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

连接socket,您一般需要提供一个tuple,它包含远程主机名或IP地址和远程端口。连接一个socket一般用类似这样的代码:

s.connect(("www.example.com", 80))

简单实例:

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import socket

host = ''
port = 51444

# 建立一个socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 把socket设置成可复用的(reusable)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# 绑定一个端口
s.bind((host, port))
# 调用listen()函数,这表明已经开始等候来自客户端的连接了,同时设定每次最多
# 只有一个等候处理的链接.真正的服务器会允许一个很高的数字
s.listen(1)

print "Server is running on port %d; press Ctrl-C to terminate." % port

while 1:
    """
    主循环从对accept()函数调用开始.程序会在连接一个客户端后马上停止.当某个客户端
    链接的时候,accept()返回两个信息:一个新的链接客户端的socket和客户端的IP地址,
    端口号.在这个例子中,使用了文件类对象,所以它的工作方式和前面的例子类似.服务器接
    着显示出一些介绍性信息,从客户端读一个字符串,显示一个应答,最后关闭客户端socket.
    这里,关闭socket时很重要的,否则客户端将不知道服务器已经结束通信,而在服务器上会
    堆积很多旧的链接.当使用文件类对象的时候,必须关闭文件对象和socket对象.

    尽管这个基本服务程序可以运行,但它没有什么用处.
    """
    clientsock, clientaddr = s.accept()
    clientfile = clientsock.makefile('rw', 0)
    clientfile.write("Welcome, " + str(clientaddr) + "\n")
    clientfile.write("Please enter a string:")
    line = clientfile.readline().strip()
    clientfile.write("You entered %d characters. \n" % len(line))
    clientfile.close()
    clientsock.close()

在终端中使用 $ telnet localhost 51444 就可以直接连接到51444端口

752372-20160908154900629-671850067.png

寻找端口号:大多说操作系统都会附带提供一份这样的列表,你可以查询一下。Python的socket库包含一个getservbyname()的函数,它可以自动地查询。在UNIX系统中,您总是可以在/etc/services目录下找到这个列表。

为了查询这个列表,您需要两个参数:协议名和端口名。端口名是一个字符串,例如:http可以被转换为一个端口号。下面是一个对前面程序的修改,它使用端口名而不是端口号。

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import socket

print "Creating socket ..."
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print "done"

print "Looking up port number ..."
# 根据端口名,直接获取端口号进行使用
port = socket.getservbyname('http', 'tcp')
print "done"

print "Connecting to romote host on port %d..." % port
s.connect(("www.google.com",port))
print "done"

从socket获取信息

一旦建立了一个socket链接,您就可以从它哪里得到一些有用的信息。

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import socket

print "Creating socket..."
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print "done"

print "Looking up port number..."
port = socket.getservbyname('http', 'tcp')
print "done"

print "Connecting to remote host on port %d..." % port
s.connect(("www.gonghuizhudi.com", port))
print "done"

print "Connected from", s.getsockname()
print "Connected to", s.getpeername()

当运行这个程序的时候,您将看奥两条新的信息。第一条将显示您本身的IP地址和端口;第二条将显示远程机器的IP地址和端口号。还记得在第一章中介绍的,对于客户端来说,端口号是由操作系统分配的(也许是随机的),所以,您也许会发现每次运行这个程序的时候,端口号都不一样。

752372-20160908160918535-629348896.png

利用socket通信

现在和socket的通信建立起来了,是利用它发送和接收数据的时候了。Python提供了两种方法:

socket对象和文件类对象
socket对象提供了操作系统的send()、sendto、recv()和recvfrom()调用的接口。文件类提供了read()、write()和readline()这些更典型的Pythone接口。当您有一些特殊需求的时候,socket对象特别有用。例如:读写数据时、您需要协议可以详细地控制是、使用二进制协议传送固定大小数据时、数据超时需要特殊处理时,socket对象同样是很好的选择。

socket异常
不同的网络调用会产生不同的异常。下面的例子演示了当处理socket对象时,如何捕获每一个普通的异常。这个例子需要3个命令行的参数:一个是想要链接的主机名,一个是服务器上的端口号或名字,一个是想从服务器请求的文件。程序将链接上服务器,针对所请求文件的名字发送一个简单的HTTP请求,显示结果。在整个过程中,它将尝试处理各种类型潜在的错误。

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import socket, sys

# 下面的参数都可从终端中导入参数获得
host = sys.argv[1] # 第一个参数服务器名
textport = sys.argv[2] # 第二个参数,文档内容?
filename = sys.argv[3] # 第三个参数,文件内容

try:
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
except socket.error, e:
    print "Strange error creating socket: %s" % e
    sys.exit(1)

# Try parsing it as a numeric prot number

'''
    4种常见的异常:
    * 与一般I/O和通信问题有关的socket.error
    * 与查询地址信息有关的socket.gaierror
    * 与其他地址错误有关的socket.herror(和C语言中的h_errno相关)
    * 与在一个socket上调用settimeout()后,处理超时有关的socket.timeout

    在例子中,您应该特别注意对connect()的调用.既然程序可以解决把主机名转换成
    IP地址的问题,您实际上会看到两种错误:如果主机名不对则会产生socket.gaierror,
    如果连接远程主机有问题则会产生socket.error.

'''

try:
    port = int(textport)
except ValueError:
    try:
        port = socket.getservbyname(textport, 'tcp')
    except socket.error, e:
        print "Couldn`t find your port: %s" % e
        sys.exit(1)

try:
    s.connect((host, port))
except socket.gaierror, e: # 与查询地址信息有关的socket.gaierror
    print "Address-related error connecting to server: %s" % e
    sys.exit(1)
except socket.error, e: # 与一般I/O和通信问题有关的socket.error
    print "Connection error: %s" % e
    sys.exit(1)

try:
    s.sendall("GET %s HTTP/1.0\r\n\r\n" % filename)
except socket.error, e:
    print "Error sending data: %s" % e
    sys.exit(1)

while 1:
    try:
        buf = s.recv(2048)
    except socket.error, e:
        print "Error receiving data: %s" % e
        sys.exit(1)
    if not len(buf):
        break
    sys.stdout.write(buf)

使用UDP

迄今为止,这一章的内容主要是几种在TCP通信上,除此之外,您还可以使用UDP。UDP通信几乎不使用文件类对象,因为它们往往不能为数据如何发送和接收提供足够的控制。让我们来先介绍一个基本的UDP客户端:

#!/usr/bin/env python
# -*- coding:utf-8 -*-

"""
    这个例子需要两个命令行参数:一个主机名和一个服务器的端口号.它将连接服务器,接着提示您
    输入一行要发送的文字.数据被发送后,它就进入了一个无线循环来等待回复,所以您获取需要用
    Ctrl-C或Ctrl-Break来终止程序.
"""

import socket, sys

host = sys.argv[1]
textport = sys.argv[2]

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
    port = int(textport)
except ValueError:
    # That didn`t work. Look it up instead.
    port = socket.getservbyname(textport, "udp")

s.connect((host, port))
print "Enter data to transmit: "
data = sys.stdin.readline().strip()
s.sendall(data)
print "Looking for replies; press Ctrl-C or Ctrl-Break to stop."
while 1:
    buf = s.recv(2048)
    if not len(buf):
        break
    sys.stdout.write(buf)

这段程序发送一个UDP信息包,接收一个UDP信息包,并继续等候其他的(其他的永远也不会到达)。最后,它被Ctrl-C终止,这导致了keyborardInterrupt。

让我们来看看它和TCP客户端的区别。
首先,请注意当socket被建立的时候,程序调用的是SOCK_DGRAM,而不是SOCK_STREAM;这就会向操作系统提示socket将使用UDP通信,而不是TCP。
其次,对socket.getservbyname()的调用寻找的是UDP端口号,而不是TCP的。一个端口号对协议来说是特殊的,所以即使TCP使用119端口,一个完全不同的UDP应用程序也可以使用同一个端口。
第三,程序没有办法探测到服务器什么时候发送完数据。这是因为其实这里没有实际的链接。对connect()的调用知识初始化了一些内在参数。同时,服务器也许不会返回任何数据,或者数据也许在传输过程中丢失,程序并没有智能地判断出这个问题。因此,当您结束等待传来的信息包时,您必须按下Ctrl-C.

您可以运行第三章中的udpechoserver.py来测试前面代码。就这执行./udp.py localhost 51423 来链接您自己机器上的UDP应答服务器。

有时,使用UDP可以根本就不调用connect()。这里有个示范:

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import socket, sys, struct, time

"""
        这个程序是一个RFC868上定义的简单时间协议的示范.通过调用sendto(),程序
    向tie.nist.gov上的服务器发送一个空字符串.注意这里并没有调用connect().
        尽管这里使用的是recvfrom()而不是recv(),收到的回复也会和平常一样.通常
    来说,当使用不是长链接的UDP通信时,recv()不能提供足够的信息用于通信.实际上,
    对recvfrom()的调用返回一个touple,其中包括两个数据:实际接收数据和发送数据
    的机器地址.在本例中,我们并不关心发送数据的机器地址,所以我们只保存了回复中的
    字符串.服务器则比较关心它,并可以利用这个机制同时处理多个客户端的请求.使用
    recvfrom(),程序收到的回复是从1900年1月1日开始到现在二进制编码的秒数(关于
    这类数据解码的详细介绍,请见地5章).接着程序解开它,通过减去从1900到1970年之
    间的秒数转换成UNIX的时间格式,最后在您的屏幕上打印出来.如果您运行这个程序,
    它将显示出正确的时间.因为UDP并不能保证一个信息包能够成功地传送,同时这个程序
    没有做任何检查的尝试,您或许需要运行好几次才能收到一个回答.此外,由于有些防火
    墙会阻止UDP通信,所以您也许根本收不到任何答复.
"""

hostname = 'time.nist.gov'
port = 37

host = socket.gethostbyname(hostname)

s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.sendto('', (host, port))

print "Looking for replies; press Ctrl-C to stop."
buf = s.recvfrom(2048)[0]
if len(buf) != 4:
    print "Wrong-sized reply %d: %s" % (len(buf), buf)
secs = struct.unpack("!I",buf)[0]
secs -= 2208988800
print time.ctime(int(secs))

总结

网络通信的基本接口是socket,它扩展了操作系统的I/O到网络通信。socket可以通过socket()函数来建立,通过connect()函数来连接。得到了socket,您可以确定本地和远程端点的IP地址和端口号。socket对不同的协议来说都是一种通用的接口,它可以处理TCP和UDP通信。
当和千里之外的机器通信的时候,很多不同的事情会发生错误,所以错误检查是很重要的。绝大多数与网络相关的调用都会产生异常,虽然有时候这些异常不是马上出现。使用shutdown()可以确保当有写错误放生是,您能获得提醒。

Python提供了两种和socket工作的接口:用于UDP和高级TCP目的的标准socket接口,以及用于简单TCP通信的文件类接口。

通过短时倒谱(Cepstrogram)计算进行时-倒频分析研究(Matlab代码实现)内容概要:本文主要介绍了一项关于短时倒谱(Cepstrogram)计算在时-倒频分析中的研究,并提供了相应的Matlab代码实现。通过短时倒谱分析方法,能够有效提取信号在时间与倒频率域的特征,适用于语音、机械振动、生物医学等领域的信号处理与故障诊断。文中阐述了倒谱分析的基本原理、短时倒谱的计算流程及其在实际工程中的应用价值,展示了如何利用Matlab进行时-倒频图的可视化与分析,帮助研究人员深入理解非平稳信号的周期性成分与谐波结构。; 适合人群:具备一定信号处理基础,熟悉Matlab编程,从事电子信息、机械工程、生物医学或通信等相关领域科研工作的研究生、工程师及科研人员。; 使用场景及目标:①掌握倒谱分析与短时倒谱的基本理论及其与傅里叶变换的关系;②学习如何用Matlab实现Cepstrogram并应用于实际信号的周期性特征提取与故障诊断;③为语音识别、机械设备状态监测、振动信号分析等研究提供技术支持与方法参考; 阅读建议:建议读者结合提供的Matlab代码进行实践操作,先理解倒谱的基本概念再逐步实现短时倒谱分析,注意参数设置如窗长、重叠率等对结果的影响,同时可将该方法与其他时频分析方法(如STFT、小波变换)进行对比,以提升对信号特征的理解能力。
<think>好的,我现在需要处理用户关于Java中Socket相关的面试题的查询。首先,用户提供了几个引用链接,我需要先理解这些引用的内容,看看哪些和Socket编程相关,然后根据这些内容来生成问题和答案。 引用[1]提到了线程与进程的区别,但用户的问题是关于Socket的,可能这部分不太相关,不过线程在Socket编程中可能有涉及,比如多线程处理连接,但暂时先标记一下。引用[2]是一个Java main方法的代码片段,没有直接提到Socket,可能相关性不大。引用[3]明确提到Socket编程是网络通信的基础,并简要说明了它的作用,这个需要重点参考。引用[4]详细说明了三次握手在TCP连接建立时的作用,以及在Socket编程中客户端执行connect()时会触发三次握手,这部分内容非常重要,尤其是三次握手的过程和Socket API的关系。 接下来,用户希望得到与Java Socket相关的面试题,我需要根据这些引用内容来生成相关问题。首先,基础概念方面,可能会问到Socket是什么,它在Java中的应用,TCP和UDP的区别等。然后三次握手和四次挥手是TCP的重要知识点,需要结合引用[4]中的内容来提问。Socket API的使用方法,如如何创建客户端和服务器端的Socket,以及多线程处理连接的问题,可能涉及引用[3]中的内容。异常处理和资源管理也是常见的面试题,比如端口占用或连接超时的处理。最后,实际应用场景和示例代码的编写可能会被问到,需要给出简单的代码示例。 现在需要确保每个问题都能覆盖到用户提供的引用内容,尤其是引用[3]和[4]。例如,三次握手的问题需要明确提到在Socket编程中哪个方法触发三次握手,即connect()方法。此外,代码示例部分需要符合用户提供的格式,使用正确的语法,并单独成段。 检查是否有遗漏的重要点,比如TCP与UDP的区别,Socket的生命周期,或者非阻塞IO相关的问题,但根据引用内容,可能主要集中在三次握手和基础API的使用上。需要确保生成的问题全面且符合常见的面试题类型,同时引用正确的来源,比如在三次握手的部分引用[4]。 最后,整理问题列表,确保问题结构清晰,覆盖基础、原理、API、异常处理、应用场景等方面,每个问题都能引导出关键知识点,并正确标注引用来源。</think>### Java Socket 编程面试题解答 #### 1. **什么是 Socket?它在 Java 网络编程中的作用是什么?** Socket 是网络通信的端点,用于实现不同设备间的数据传输。在 Java 中,通过 $java.net.Socket$ 和 $java.net.ServerSocket$ 类实现 TCP 协议通信,而 $java.net.DatagramSocket$ 用于 UDP 协议。Socket 编程使得应用程序能够通过 IP 地址和端口号与其他设备建立连接并交换数据[^3]。 #### 2. **TCP 三次握手在 Socket 编程中如何体现?** 三次握手是建立 TCP 连接的核心过程: 1. 客户端发送 SYN 包(同步序列号)到服务器。 2. 服务器返回 SYN-ACK 包(确认客户端的 SYN)。 3. 客户端发送 ACK 包确认服务器的 SYN。 在 Java Socket 编程中,客户端调用 $socket.connect()$ 时会触发三次握手,而服务器通过 $serverSocket.accept()$ 等待连接[^4]。 #### 3. **Java 中如何创建 TCP 客户端和服务端?** **服务端示例:** ```java try (ServerSocket serverSocket = new ServerSocket(8080)) { Socket clientSocket = serverSocket.accept(); // 等待客户端连接 // 处理输入输出流 } catch (IOException e) { e.printStackTrace(); } ``` **客户端示例:** ```java try (Socket socket = new Socket("localhost", 8080)) { // 处理输入输出流 } catch (IOException e) { e.printStackTrace(); } ``` #### 4. **TCP 和 UDP 的 Socket 编程有什么区别?** - **TCP**:面向连接,可靠传输,使用 $Socket$ 和 $ServerSocket$。 - **UDP**:无连接,不可靠但高效,使用 $DatagramSocket$ 和 $DatagramPacket$。 例如,UDP 发送数据的代码片段: ```java DatagramSocket socket = new DatagramSocket(); byte[] buffer = "数据".getBytes(); DatagramPacket packet = new DatagramPacket(buffer, buffer.length, InetAddress.getByName("localhost"), 8080); socket.send(packet); ``` #### 5. **如何处理 Socket 编程中的异常和资源泄漏?** - 使用 `try-with-resources` 自动关闭资源(Java 7+ 支持)。 - 捕获并处理 $IOException$,例如连接超时或端口占用: ```java try (Socket socket = new Socket()) { socket.connect(new InetSocketAddress("localhost", 8080), 5000); // 超时5秒 } catch (SocketTimeoutException e) { System.out.println("连接超时"); } catch (IOException e) { e.printStackTrace(); } ``` #### 6. **多线程在 Socket 服务端中的应用场景是什么?** 服务端通常使用多线程处理多个客户端请求,避免阻塞主线程: ```java while (true) { Socket clientSocket = serverSocket.accept(); new Thread(() -> { try (BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()))) { String request = in.readLine(); // 处理请求 } catch (IOException e) { e.printStackTrace(); } }).start(); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值