java 原始套接字编程_套接字编程原理

2023-10-30

6、多路复用——select() 功能:用来检测一个或多个套接字状态。 格式:int PASCAL FAR select(int nfds,fd_set FAR * readfds,fd_set FAR * writefds, fd_set FAR * exceptfds,const struct timeval FAR * timeout); 参数:readfds:指向要做读检测的指针 writefds:指向要做写检测的指针 exceptfds:指向要检测是否出错的指针 timeout:最大等待时间

7、关闭套接字——closesocket() 功能:关闭套接字s 格式:BOOL PASCAL FAR closesocket(SOCKET s);

Windows Socket套接字原理

一、客户机/服务器模式

在TCP/IP网络中两个进程间的相互作用的主机模式是客户机/服务器模式(Client/Server model)。该模式的建立基于以下两点:1、非对等作用;2、通信完全是异步的。客户机/服务器模式在操作过程中采取的是主动请示方式:

首先服务器方要先启动,并根据请示提供相应服务:(过程如下)

1、打开一通信通道并告知本地主机,它愿意在某一个公认地址上接收客户请求。

2、等待客户请求到达该端口。

3、接收到重复服务请求,处理该请求并发送应答信号。

4、返回第二步,等待另一客户请求

5、关闭服务器。

客户方:

1、打开一通信通道,并连接到服务器所在主机的特定端口。

2、向服务器发送服务请求报文,等待并接收应答;继续提出请求……

3、请求结束后关闭通信通道并终止。

二、典型过程图

2.1 面向连接的套接字的系统调用时序图

5014be9470533532620bb3c5f9156802.png

2.2 无连接协议的套接字调用时序图

4461a1f56a33cfcd7c1cb5168d996e92.png

2.3 面向连接的应用程序流程图

e66d34445f55bd0cbdfd80b5f6d6faaf.png

三、  Socket有五种不同的类型:

1、流式套接字(stream socket)

定义:#define SOCK_STREAM 1

流式套接字提供了双向、有序的、无重复的以及无记录边界的数据流服务,适合处理大量数据。它是面向联结的,必须建立数据传输链路,同时还必须对传输的数据进行验证,确保数据的准确性。因此,系统开销较大。

2、 数据报套接字(datagram socket)

定义:#define SOCK_DGRAM 2

数据报套接字也支持双向的数据流,但不保证传输数据的准确性,但保留了记录边界。由于数据报套接字是无联接的,例如广播时的联接,所以并不保证接收端是否正在侦听。数据报套接字传输效率比较高。

3、原始套接字(raw-protocol interface)

定义:#define SOCK_RAW 3

原始套接字保存了数据包中的完整IP头,前面两种套接字只能收到用户数据。因此可以通过原始套接字对数据进行分析。

◆Socket开发所必须需要的文件(以WinSock V2.0为例):

头文件:Winsock2.h

库文件:WS2_32.LIB

动态库:W32_32.DLL

四、基本套接字

1、创建套接字——socket()

功能:使用前创建一个新的套接字

格式:SOCKET PASCAL FAR socket(int af,int type,int procotol);

参数:af: 通信发生的区域

type: 要建立的套接字类型

procotol: 使用的特定协议

2、指定本地地址——bind()

功能:将套接字地址与所创建的套接字号联系起来。

格式:int PASCAL FAR bind(SOCKET s,const struct sockaddr FAR * name,int namelen);

参数:s: 是由socket()调用返回的并且未作连接的套接字描述符(套接字号)。

其它:没有错误,bind()返回0,否则SOCKET_ERROR

地址结构说明:

struct sockaddr_in

{

short sin_family;//AF_INET

u_short sin_port;//16位端口号,网络字节顺序

struct in_addr sin_addr;//32位IP地址,网络字节顺序

char sin_zero[8];//保留

}

3、建立套接字连接——connect()和accept()

功能:共同完成连接工作

格式:int PASCAL FAR connect(SOCKET s,const struct sockaddr FAR * name,int namelen);

SOCKET PASCAL FAR accept(SOCKET s,struct sockaddr FAR * name,int FAR * addrlen);

参数:同上

4、监听连接——listen()

功能:用于面向连接服务器,表明它愿意接收连接。

格式:int PASCAL FAR listen(SOCKET s, int backlog);

5、数据传输——send()与recv()

功能:数据的发送与接收

格式:int PASCAL FAR send(SOCKET s,const char FAR * buf,int len,int flags);

int PASCAL FAR recv(SOCKET s,const char FAR * buf,int len,int flags);

参数:buf:指向存有传输数据的缓冲区的指针。

6、多路复用——select()

功能:用来检测一个或多个套接字状态。

格式:int PASCAL FAR select(int nfds,fd_set FAR * readfds,fd_set FAR * writefds,

fd_set FAR * exceptfds,const struct timeval FAR * timeout);

参数:readfds:指向要做读检测的指针

writefds:指向要做写检测的指针

exceptfds:指向要检测是否出错的指针

timeout:最大等待时间

7、关闭套接字——closesocket()

功能:关闭套接字s

格式:BOOL PASCAL FAR closesocket(SOCKET s);

JAVA Socket 套接字原理

用Java开发网络软件非常方便和强大,Java的这种力量来源于他独有的一套强大的用于网络的 API,这些API是一系列的类和接口,均位于包java.net和javax.net中。在这篇文章中我们将介绍套接字(Socket)慨念,同时以实例说明如何使用Network API操纵套接字,在完成本文后,你就可以编写网络低端通讯软件。

什么是套接字(Socket)?

Network API是典型的用于基于TCP/IP网络Java程序与其他程序通讯,Network API依靠Socket进行通讯。Socket可以看成在两个程序进行通讯连接中的一个端点,一个程序将一段信息写入Socket中,该Socket将这段信息发送给另外一个Socket中,使这段信息能传送到其他程序中。如图1

a017c1afa6bdddb71c9d18b33ad5d90a.png

我们来分析一下图1,Host A上的程序A将一段信息写入Socket中,Socket的内容被Host A的网络管理软件访问,并将这段信息通过Host A的网络接口卡发送到Host B,Host B的网络接口卡接收到这段信息后,传送给Host B的网络管理软件,网络管理软件将这段信息保存在Host B的Socket中,然后程序B才能在Socket中阅读这段信息。

假设在图1的网络中添加第三个主机Host C,那么Host A怎么知道信息被正确传送到Host B而不是被传送到Host C中了呢?基于TCP/IP网络中的每一个主机均被赋予了一个唯一的IP地址,IP地址是一个32位的无符号整数,由于没有转变成二进制,因此通常以小数点分隔,如:198.163.227.6,正如所见IP地址均由四个部分组成,每个部分的范围都是0-255,以表示8位地址。

值得注意的是IP地址都是32位地址,这是IP协议版本4(简称Ipv4)规定的,目前由于IPv4地址已近耗尽,所以IPv6地址正逐渐代替Ipv4地址,Ipv6地址则是128位无符号整数。

假设第二个程序被加入图1的网络的Host B中,那么由Host A传来的信息如何能被正确的传给程序B而不是传给新加入的程序呢?这是因为每一个基于TCP/IP网络通讯的程序都被赋予了唯一的端口和端口号,端口是一个信息缓冲区,用于保留Socket中的输入/输出信息,端口号是一个16位无符号整数,范围是0-65535,以区别主机上的每一个程序(端口号就像房屋中的房间号),低于256的短口号保留给标准应用程序,比如pop3的端口号就是110,每一个套接字都组合进了IP地址、端口、端口号,这样形成的整体就可以区别每一个套接字t,下面我们就来谈谈两种套接字:流套接字和自寻址数据套接字。

流套接字(Stream Socket)

无论何时,在两个网络应用程序之间发送和接收信息时都需要建立一个可靠的连接,流套接字依靠TCP协议来保证信息正确到达目的地,实际上,IP包有可能在网络中丢失或者在传送过程中发生错误,任何一种情况发生,作为接受方的 TCP将联系发送方TCP重新发送这个IP包。这就是所谓的在两个流套接字之间建立可靠的连接。

流套接字在C/S程序中扮演一个必需的角色,客户机程序(需要访问某些服务的网络应用程序)创建一个扮演服务器程序的主机的IP地址和服务器程序(为客户端应用程序提供服务的网络应用程序)的端口号的流套接字对象。

客户端流套接字的初始化代码将IP地址和端口号传递给客户端主机的网络管理软件,管理软件将IP地址和端口号通过NIC传递给服务器端主机;服务器端主机读到经过NIC传递来的数据,然后查看服务器程序是否处于监听状态,这种监听依然是通过套接字和端口来进行的;如果服务器程序处于监听状态,那么服务器端网络管理软件就向客户机网络管理软件发出一个积极的响应信号,接收到响应信号后,客户端流套接字初始化代码就给客户程序建立一个端口号,并将这个端口号传递给服务器程序的套接字(服务器程序将使用这个端口号识别传来的信息是否是属于客户程序)同时完成流套接字的初始化。

如果服务器程序没有处于监听状态,那么服务器端网络管理软件将给客户端传递一个消极信号,收到这个消极信号后,客户程序的流套接字初始化代码将抛出一个异常对象并且不建立通讯连接,也不创建流套接字对象。这种情形就像打电话一样,当有人的时候通讯建立,否则电话将被挂起。

这部分的工作包括了相关联的三个类:InetAddress, Socket, 和 ServerSocket。 InetAddress对象描绘了32位或128位IP地址,Socket对象代表了客户程序流套接字,ServerSocket代表了服务程序流套接字,所有这三个类均位于包java.net中。

InetAddress类

InetAddress类在网络API套接字编程中扮演了一个重要角色。参数传递给流套接字类和自寻址套接字类构造器或非构造器方法。InetAddress描述了32位或64位IP地址,要完成这个功能,InetAddress类主要依靠两个支持类Inet4Address 和 Inet6Address,这三个类是继承关系,InetAddrress是父类,Inet4Address 和 Inet6Address是子类。

由于InetAddress类只有一个构造函数,而且不能传递参数,所以不能直接创建InetAddress对象,比如下面的做法就是错误的:

InetAddress ia = new InetAddress ();

但我们可以通过下面的5个工厂方法创建来创建一个InetAddress对象或InetAddress数组:

. getAllByName(String host)方法返回一个InetAddress对象的引用,每个对象包含一个表示相应主机名的单独的IP地址,这个IP地址是通过host参数传递的,对于指定的主机如果没有IP地址存在那么这个方法将抛出一个UnknownHostException 异常对象。

. getByAddress(byte [] addr)方法返回一个InetAddress对象的引用,这个对象包含了一个Ipv4地址或Ipv6地址,Ipv4地址是一个4字节数组,Ipv6地址是一个16字节地址数组,如果返回的数组既不是4字节的也不是16字节的,那么方法将会抛出一个UnknownHostException异常对象。

. getByAddress(String host, byte [] addr)方法返回一个InetAddress对象的引用,这个InetAddress对象包含了一个由host和4字节的addr数组指定的IP地址,或者是host和16字节的addr数组指定的IP地址,如果这个数组既不是4字节的也不是16位字节的,那么该方法将抛出一个UnknownHostException异常对象。

. getByName(String host)方法返回一个InetAddress对象,该对象包含了一个与host参数指定的主机相对应的IP地址,对于指定的主机如果没有IP地址存在,那么方法将抛出一个UnknownHostException异常对象。

. getLocalHost()方法返回一个InetAddress对象,这个对象包含了本地机的IP地址,考虑到本地主机既是客户程序主机又是服务器程序主机,为避免混乱,我们将客户程序主机称为客户主机,将服务器程序主机称为服务器主机。

上面讲到的方法均提到返回一个或多个InetAddress对象的引用,实际上每一个方法都要返回一个或多个Inet4Address/Inet6Address对象的引用,调用者不需要知道引用的子类型,相反调用者可以使用返回的引用调用InetAddress对象的非静态方法,包括子类型的多态以确保重载方法被调用。

InetAddress和它的子类型对象处理主机名到主机IPv4或IPv6地址的转换,要完成这个转换需要使用域名系统,下面的代码示范了如何通过调用getByName(String host)方法获得InetAddress子类对象的方法,这个对象包含了与host参数相对应的IP地址:

InetAddress ia = InetAddress.getByName ("www.javajeff.com"));

一但获得了InetAddress子类对象的引用就可以调用InetAddress的各种方法来获得InetAddress子类对象中的IP地址信息,比如,可以通过调用getCanonicalHostName()从域名服务中获得标准的主机名;getHostAddress()获得IP地址,getHostName()获得主机名,isLoopbackAddress()判断IP地址是否是一个loopback地址。

List1 是一段示范代码:InetAddressDemo

// InetAddressDemo.java

import java.net.*;

class InetAddressDemo {  public static void main (String [] args) throws UnknownHostException  {   String host = "localhost";

if (args.length == 1)    host = args [0];

InetAddress ia = InetAddress.getByName (host);

System.out.println ("Canonical Host Name = " +         ia.getCanonicalHostName ());   System.out.println ("Host Address = " +         ia.getHostAddress ());   System.out.println ("Host Name = " +         ia.getHostName ());   System.out.println ("Is Loopback Address = " +         ia.isLoopbackAddress ());  } }

当无命令行参数时,代码输出类似下面的结果:

Canonical Host Name = localhost Host Address = 127.0.0.1 Host Name = localhost Is Loopback Address = true

InetAddressDemo给了你一个指定主机名作为命令行参数的选择,如果没有主机名被指定,那么将使用localhost(客户机的),InetAddressDemo通过调用getByName(String host)方法获得一个InetAddress子类对象的引用,通过这个引用获得了标准主机名,主机地址,主机名以及IP地址是否是loopback地址的输出。

Socket类

当客户程序需要与服务器程序通讯的时候,客户程序在客户机创建一个socket对象,Socket类有几个构造函数。两个常用的构造函数是 Socket(InetAddress addr, int port) 和 Socket(String host, int port),两个构造函数都创建了一个基于Socket的连接服务器端流套接字的流套接字。对于第一个InetAddress子类对象通过addr参数获得服务器主机的IP地址,对于第二个函数host参数包被分配到InetAddress对象中,如果没有IP地址与host参数相一致,那么将抛出UnknownHostException异常对象。两个函数都通过参数port获得服务器的端口号。假设已经建立连接了,网络API将在客户端基于Socket的流套接字中捆绑客户程序的IP地址和任意一个端口号,否则两个函数都会抛出一个IOException对象。

如果创建了一个Socket对象,那么它可能通过调用Socket的 getInputStream()方法从服务程序获得输入流读传送来的信息,也可能通过调用Socket的 getOutputStream()方法获得输出流来发送消息。在读写活动完成之后,客户程序调用close()方法关闭流和流套接字,下面的代码创建了一个服务程序主机地址为198.163.227.6,端口号为13的Socket对象,然后从这个新创建的Socket对象中读取输入流,然后再关闭流和Socket对象。

Socket s = new Socket ("198.163.227.6", 13); InputStream is = s.getInputStream (); // Read from the stream. is.close (); s.close ();

接下面我们将示范一个流套接字的客户程序,这个程序将创建一个Socket对象,Socket将访问运行在指定主机端口10000上的服务程序,如果访问成功客户程序将给服务程序发送一系列命令并打印服务程序的响应。List2使我们创建的程序SSClient的源代码:

Listing 2: SSClient.java

// SSClient.java

import java.io.*; import java.net.*;

class SSClient {  public static void main (String [] args)  {   String host = "localhost";

// If user specifies a command-line argument, that argument   // represents the host name.

if (args.length == 1)    host = args [0];

BufferedReader br = null;   PrintWriter pw = null;   Socket s = null;

try   {    // Create a socket that attempts to connect to the server    // program on the host at port 10000.

s = new Socket (host, 10000);

// Create an input stream reader that chains to the socket's    // byte-oriented input stream. The input stream reader    // converts bytes read from the socket to characters. The    // conversion is based on the platform's default character    // set.

InputStreamReader isr;    isr = new InputStreamReader (s.getInputStream ());

// Create a buffered reader that chains to the input stream    // reader. The buffered reader supplies a convenient method    // for reading entire lines of text.

br = new BufferedReader (isr);

// Create a print writer that chains to the socket's byte-    // oriented output stream. The print writer creates an    // intermediate output stream writer that converts    // characters sent to the socket to bytes. The conversion    // is based on the platform's default character set.

pw = new PrintWriter (s.getOutputStream (), true);

// Send the DATE command to the server.

pw.println ("DATE");

// Obtain and print the current date/time.

System.out.println (br.readLine ());    // Send the PAUSE command to the server. This allows several    // clients to start and verifies that the server is spawning    // multiple threads.

pw.println ("PAUSE");    // Send the DOW command to the server.

pw.println ("DOW");

// Obtain and print the current day of week.

System.out.println (br.readLine ());

// Send the DOM command to the server.      pw.println ("DOM");

// Obtain and print the current day of month.

System.out.println (br.readLine ());

// Send the DOY command to the server.

pw.println ("DOY");

// Obtain and print the current day of year.

System.out.println (br.readLine ());   }   catch (IOException e)   {    System.out.println (e.toString ());   }   finally   {    try    {     if (br != null)      br.close ();

if (pw != null)      pw.close ();

if (s != null)      s.close ();    }    catch (IOException e)    {     }   }  } }

运行这段程序将会得到下面的结果:

Tue Jan 29 18:11:51 CST 2002 TUESDAY 29 29

SSClient创建了一个Socket对象与运行在主机端口10000的服务程序联系,主机的IP地址由host变量确定。SSClient将获得Socket的输入输出流,围绕BufferedReader的输入流和PrintWriter的输出流对字符串进行读写操作就变得非常容易,SSClient个服务程序发出各种date/time命令并得到响应,每个响应均被打印,一旦最后一个响应被打印,将执行Try/Catch/Finally结构的Finally子串,Finally子串将在关闭Socket之前关闭BufferedReader 和 PrintWriter。

在SSClient源代码编译完成后,可以输入java SSClient 来执行这段程序,如果有合适的程序运行在不同的主机上,采用主机名/IP地址为参数的输入方式,比如www.sina.com.cn是运行服务器程序的主机,那么输入方式就是java SSClient www.sina.com.cn。

技巧

Socket类包含了许多有用的方法。比如getLocalAddress()将返回一个包含客户程序IP地址的InetAddress子类对象的引用;getLocalPort()将返回客户程序的端口号;getInetAddress()将返回一个包含服务器IP地址的InetAddress子类对象的引用;getPort()将返回服务程序的端口号。

ServerSocket类

由于SSClient使用了流套接字,所以服务程序也要使用流套接字。这就要创建一个ServerSocket对象,ServerSocket有几个构造函数,最简单的是ServerSocket(int port),当使用ServerSocket(int port)创建一个ServerSocket对象,port参数传递端口号,这个端口就是服务器监听连接请求的端口,如果在这时出现错误将抛出IOException异常对象,否则将创建ServerSocket对象并开始准备接收连接请求。

接下来服务程序进入无限循环之中,无限循环从调用ServerSocket的accept()方法开始,在调用开始后accept()方法将导致调用线程阻塞直到连接建立。在建立连接后accept()返回一个最近创建的Socket对象,该Socket对象绑定了客户程序的IP地址或端口号。

由于存在单个服务程序与多个客户程序通讯的可能,所以服务程序响应客户程序不应该花很多时间,否则客户程序在得到服务前有可能花很多时间来等待通讯的建立,然而服务程序和客户程序的会话有可能是很长的(这与电话类似),因此为加快对客户程序连接请求的响应,典型的方法是服务器主机运行一个后台线程,这个后台线程处理服务程序和客户程序的通讯。

为了示范我们在上面谈到的慨念并完成SSClient程序,下面我们创建一个SSServer程序,程序将创建一个ServerSocket对象来监听端口10000的连接请求,如果成功服务程序将等待连接输入,开始一个线程处理连接,并响应来自客户程序的命令。下面就是这段程序的代码:

Listing 3: SSServer.java

// SSServer.java

import java.io.*; import java.net.*; import java.util.*;

class SSServer {  public static void main (String [] args) throws IOException  {    System.out.println ("Server starting...\n");

// Create a server socket that listens for incoming connection   // requests on port 10000.

ServerSocket server = new ServerSocket (10000);

while (true)   {    // Listen for incoming connection requests from client    // programs, establish a connection, and return a Socket    // object that represents this connection.

Socket s = server.accept ();

System.out.println ("Accepting Connection...\n");

// Start a thread to handle the connection.

new ServerThread (s).start ();   }  } }

class ServerThread extends Thread {  private Socket s;

ServerThread (Socket s)  {   this.s = s;  }

public void run ()  {   BufferedReader br = null;   PrintWriter pw = null;

try   {    // Create an input stream reader that chains to the socket's    // byte-oriented input stream. The input stream reader    // converts bytes read from the socket to characters. The    // conversion is based on the platform's default character    // set.

InputStreamReader isr;    isr = new InputStreamReader (s.getInputStream ());

// Create a buffered reader that chains to the input stream    // reader. The buffered reader supplies a convenient method    // for reading entire lines of text.

br = new BufferedReader (isr);

// Create a print writer that chains to the socket's byte-    // oriented output stream. The print writer creates an    // intermediate output stream writer that converts    // characters sent to the socket to bytes. The conversion    // is based on the platform's default character set.

pw = new PrintWriter (s.getOutputStream (), true);

// Create a calendar that makes it possible to obtain date    // and time information.

Calendar c = Calendar.getInstance ();

// Because the client program may send multiple commands, a    // loop is required. Keep looping until the client either    // explicitly requests termination by sending a command    // beginning with letters BYE or implicitly requests    // termination by closing its output stream.

do    {     // Obtain the client program's next command.

String cmd = br.readLine ();

// Exit if client program has closed its output stream.

if (cmd == null)      break;        // Convert command to uppercase, for ease of comparison.

cmd = cmd.toUpperCase ();

// If client program sends BYE command, terminate.

if (cmd.startsWith ("BYE"))      break;

// If client program sends DATE or TIME command, return     // current date/time to the client program.

if (cmd.startsWith ("DATE") || cmd.startsWith ("TIME"))      pw.println (c.getTime ().toString ());

// If client program sends DOM (Day Of Month) command,     // return current day of month to the client program.

if (cmd.startsWith ("DOM"))      pw.println ("" + c.get (Calendar.DAY_OF_MONTH));

// If client program sends DOW (Day Of Week) command,     // return current weekday (as a string) to the client     // program.

if (cmd.startsWith ("DOW"))      switch (c.get (Calendar.DAY_OF_WEEK))     {      case Calendar.SUNDAY : pw.println ("SUNDAY");       break;

case Calendar.MONDAY : pw.println ("MONDAY");       break;

case Calendar.TUESDAY : pw.println ("TUESDAY");       break;

case Calendar.WEDNESDAY: pw.println ("WEDNESDAY");       break;

case Calendar.THURSDAY : pw.println ("THURSDAY");       break;

case Calendar.FRIDAY : pw.println ("FRIDAY");       break;

case Calendar.SATURDAY : pw.println ("SATURDAY");     }

// If client program sends DOY (Day of Year) command,     // return current day of year to the client program.

if (cmd.startsWith ("DOY"))      pw.println ("" + c.get (Calendar.DAY_OF_YEAR));

// If client program sends PAUSE command, sleep for three      // seconds.       if (cmd.startsWith ("PAUSE"))     try     {      Thread.sleep (3000);     }     catch (InterruptedException e)     {     }    }    while (true);    {    catch (IOException e)    {     System.out.println (e.toString ());    }    finally    {     System.out.println ("Closing Connection...\n");

try     {      if (br != null)       br.close ();

if (pw != null)        pw.close ();

if (s != null)        s.close ();     }     catch (IOException e)     {     }    }   } }

运行这段程序将得到下面的输出:

Server starting... Accepting Connection... Closing Connection...

SSServer的源代码声明了一对类:SSServer 和ServerThread;SSServer的main()方法创建了一个ServerSocket对象来监听端口10000上的连接请求,如果成功, SSServer进入一个无限循环中,交替调用ServerSocket的 accept() 方法来等待连接请求,同时启动后台线程处理连接(accept()返回的请求)。线程由ServerThread继承的start()方法开始,并执行ServerThread的run()方法中的代码。

一旦run()方法运行,线程将创建BufferedReader, PrintWriter和 Calendar对象并进入一个循环,这个循环由读(通过BufferedReader的 readLine())来自客户程序的一行文本开始,文本(命令)存储在cmd引用的string对象中,如果客户程序过早的关闭输出流,会发生什么呢?答案是:cmd将得不到赋值。

注意必须考虑到这种情况:在服务程序正在读输入流时,客户程序关闭了输出流,如果没有对这种情况进行处理,那么程序将产生异常。

一旦编译了SSServer的源代码,通过输入Java SSServer来运行程序,在开始运行SSServer后,就可以运行一个或多个SSClient程序。

DatagramSocket类

DatagramSocket类在客户端创建自寻址套接字与服务器端进行通信连接,并发送和接受自寻址套接字。虽然有多个构造函数可供选择,但我发现创建客户端自寻址套接字最便利的选择是DatagramSocket()函数,而服务器端则是DatagramSocket(int port)函数,如果未能创建自寻址套接字或绑定自寻址套接字到本地端口,那么这两个函数都将抛出一个SocketException对象,一旦程序创建了DatagramSocket对象,那么程序分别调用send(DatagramPacket dgp)和 receive(DatagramPacket dgp)来发送和接收自寻址数据包,

List4显示的DGSClient源代码示范了如何创建自寻址套接字以及如何通过套接字处理发送和接收信息

Listing 4: DGSClient.java // DGSClient.java

import java.io.*; import java.net.*;

class DGSClient {  public static void main (String [] args)  {   String host = "localhost";

// If user specifies a command-line argument, that argument   // represents the host name.     if (args.length == 1)    host = args [0];

DatagramSocket s = null;

try   {    // Create a datagram socket bound to an arbitrary port.

s = new DatagramSocket ();

// Create a byte array that will hold the data portion of a    // datagram packet''s message. That message originates as a    // String object, which gets converted to a sequence of    // bytes when String''s getBytes() method is called. The    // conversion uses the platform''s default character set.

byte [] buffer;    buffer = new String ("Send me a datagram").getBytes ();

// Convert the name of the host to an InetAddress object.    // That object contains the IP address of the host and is    // used by DatagramPacket.

InetAddress ia = InetAddress.getByName (host);

// Create a DatagramPacket object that encapsulates a

// reference to the byte array and destination address    // information. The destination address consists of the    // host''s IP address (as stored in the InetAddress object)    // and port number 10000 -- the port on which the server    // program listens.

DatagramPacket dgp = new DatagramPacket (buffer,         buffer.length,         ia,         10000);

// Send the datagram packet over the socket.

s.send (dgp);

// Create a byte array to hold the response from the server.    // program.

byte [] buffer2 = new byte [100];

// Create a DatagramPacket object that specifies a buffer    // to hold the server program''s response, the IP address of    // the server program''s computer, and port number 10000.

dgp = new DatagramPacket (buffer2,       buffer.length,       ia,       10000);

// Receive a datagram packet over the socket.

s.receive (dgp);

// Print the data returned from the server program and stored    // in the datagram packet.

System.out.println (new String (dgp.getData ()));

}   catch (IOException e)   {    System.out.println (e.toString ());   }   finally   {    if (s != null)     s.close ();    }  } }

DGSClient由创建一个绑定任意本地(客户端)端口好的DatagramSocket对象开始,然后装入带有文本信息的数组buffer和描述服务器主机IP地址的InetAddress子类对象的引用,接下来,DGSClient创建了一个DatagramPacket对象,该对象加入了带文本信息的缓冲器的引用,InetAddress子类对象的引用,以及服务端口号10000, DatagramPacket的自寻址数据包通过方法sent()发送给服务器程序,于是一个包含服务程序响应的新的DatagramPacket对象被创建,receive()得到响应的自寻址数据包,然后自寻址数据包的getData()方法返回该自寻址数据包的一个引用,最后关闭DatagramSocket。

DGSServer服务程序补充了DGSClient的不足,List5是DGSServer的源代码:

Listing 5: DGSServer.java // DGSServer.java

import java.io.*; import java.net.*;

class DGSServer {  public static void main (String [] args) throws IOException  {   System.out.println ("Server starting ...\n");

// Create a datagram socket bound to port 10000. Datagram   // packets sent from client programs arrive at this port.

DatagramSocket s = new DatagramSocket (10000);

// Create a byte array to hold data contents of datagram   // packet.

byte [] data = new byte [100];

// Create a DatagramPacket object that encapsulates a reference   // to the byte array and destination address information. The   // DatagramPacket object is not initialized to an address   // because it obtains that address from the client program.

DatagramPacket dgp = new DatagramPacket (data, data.length);

// Enter an infinite loop. Press Ctrl+C to terminate program.

while (true)   {    // Receive a datagram packet from the client program.

s.receive (dgp);

// Display contents of datagram packet.

System.out.println (new String (data));

// Echo datagram packet back to client program.

s.send (dgp);  } } }

DGSServer创建了一个绑定端口10000的自寻址套接字,然后创建一个字节数组容纳自寻址信息,并创建自寻址包,下一步,DGSServer进入一个无限循环中以接收自寻址数据包、显示内容并将响应返回客户端,自寻址套接没有关闭,因为循环是无限的。

在编译DGSServer 和DGSClient的源代码后,由输入java DGSServer开始运行DGSServer,然后在同一主机上输入Java DGSClient开始运行DGSClient,如果DGSServer与DGSClient运行于不同主机,在输入时注意要在命令行加上服务程序的主机名或IP地址,如:java DGSClient www.yesky.com

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

java 原始套接字编程_套接字编程原理 的相关文章

  • Acwing-42. 栈的压入、弹出序列

    每一步进行的操作有两种 将下一个数压入栈中 将当前栈顶元素弹出 判断当前栈顶元素是否和下一个要输出的数是一样的 一样 gt 必然会将当前栈顶元素弹出 不一样 gt 必然会将输入序列的下一个元素加入栈中 class Solution publ
  • 实战DeviceIoControl 之五:列举已安装的存储设备

    Q 前几次我们讨论的都是设备名比较清楚的情况 有了设备名 路径 就可以直接调用CreateFile打开设备 进行它所支持的I O操作了 如果事先并不能确切知道设备名 如何去访问设备呢 A访问设备必须用设备句柄 而得到设备句柄必须知道设备路径
  • tf.add()不只是简单相加

    tf add 大多数用法都是 单个数字和单个数字的简单相加 例如 import tensorflow as tf x tf constant 2 y tf constant 1 sess tf Session print sess run
  • Python中的【if __name__=='__main__':】

    1 Java和C 的程序入口 Java的程序入口Main函数 public static void Main string args 方法体 C 的程序入口Main函数 public static void Main string args
  • 业务数据分析——同环比(待补全)

    1 解决的问题 企业对公司人员贡献 离职 入职 招聘 人力成本等的统计和发现 与之前特定时期的数据对比 直观的感受数值变化 各方面的发展情况 从而对企业结构和投入等作出调整 同比说明本期发展水平与去年同期发展水平对比的相对发展速度 环比说明
  • echarts 修改图片(画布)大小

    一 问题 echarts 官网有很多 examples 可以直接在上面修改成自己想要的样子 链接 https echarts apache org examples 但是我发现这个不能改变整个图片的大小 也就是下载之后的大小 有时候布局不是
  • 【python学习笔记】Python对经纬度处理

    说明 因为地球是球面 所以地球平面间的距离也得根据球面来计算 连个问题 两经纬度点之间的距离 根据一个给定经纬度的点 进行附近若干距离地点查询 两点之间的距离 根据经纬度计算距离 def distance lon1 lat1 lon2 la
  • FEC原理及其实现

    感谢原作者 http blog csdn net rootusers article details 49097257 视频会议中通常使用的FEC QOS技术 这方面的资料比较复杂和稀少 根据这么多年的工作经验 做一下分享 在IP视频通话中
  • 编码方式

    NRZ编码 NRZ 是最简单的串行编码技术 用两个电压来代表两个二进制数 如高电平表示 1 低电平表示 0 NRZI编码 NRZI 则是用电平的一次翻转来表示 1 与前一个 NRZI 电平相同的电平表示 0 曼彻斯特编码 曼侧斯特编码将一个
  • (10)QJ_黑电平&AWB&CCM校正步骤&raw数据抓取

    1 设备准备 待校正主控 SENSOR 镜头 镜头盖 可抓raw版本 2 环境准备 图像室灯箱环境 24色卡 照度计 3 说明书准备 HiISP 颜色调优说明 图像质量调试工具使用指南 4 工具准备 PQTOOL 版本对应 插件齐全 Ima
  • 多语言版本 OPENFILENAME过滤器设置问题

    正常的时候 打开并选择一个文件这样写代码 char szFileName MAX PATH 0 OPENFILENAME ofn memset szFileName 0 MAX PATH memset ofn 0 sizeof ofn of
  • Mol Cell Proteomics.

    大家好 本周分享的是发表在Molecular Cellular Proteomics 上的一篇关于蛋白质组学样本质谱分析前处理方法改进的文章 题目是Protein aggregation capture on microparticles
  • Android Studio代码没有颜色区分的两个原因

    使用一段 Android Studio Electric Eel 2022 1 1 Patch 1 版本一段时间后 发现里面的代码没有了颜色提示 经查找 有两个原因 1 勾选了power save mode 这个时候只要点击File找到Po
  • CDN内容分发网络架构与四大关键技术

    转自 http lylhelin iteye com blog 811523 随着宽带网络 和宽带流媒体应用 的兴起 CDN 通常被称为内容分发网络Content distribution network 有时也被称作内容传递网络Conte
  • 实用的vue插件大汇总

    Vue是一个构建数据驱动的 web 界面的渐进式框架 Vue js 的目标是通过尽可能简单的 API 实现响应的数据绑定和组合的视图组件特别整理了常用的vue插件 来了个大汇总 方便查找使用 便于工作和学习 很全的vue插件汇总 赶紧收藏下
  • 判断点是否在多边形内部

    文章目录 1 使用matplotlib path库 2 使用shapely库 本文参考 文档1 文档2 有两种方法 将分别做出说明 1 使用matplotlib path库 步骤 创建多边形点 matplotlib path生成多边形路径
  • 内存优化-野指针优化

    空指针 有指向任何东 的指针 即 nil NULL 0 给空指针发送消息不会报错 野指针 C 语 声明 个指针变量 但是没有赋初始值 此时指针 指向 个垃圾值 即指向 块随机的内 存空间 OC语 指针所指的对象已经被释放 回收了 但是指针没
  • C++类和对象(三)之拷贝构造函数

    1 概念 构造函数 只有单个形参 该形参是对本类类型对象的引用 一般常用const修饰 在用已存在的类类型对象创建新对象时由编译器自动调用 2 特征 拷贝构造函数是构造函数的一个重载形式 拷贝构造函数的参数只有一个且必须使用引用传参 使用传
  • android多个跑马灯6,【Android】实现走马灯并可设置速度

    一 前言 使用TextView实现走马灯效果非常的简单 只需要在布局里添加一个如下的TextView android id id marquee android layout width match parent android layou

随机推荐

  • 三维模型3DTile格式轻量化压缩处理的数据质量提升方法分析

    三维模型3DTile格式轻量化压缩处理的数据质量提升方法分析 在处理三维模型3DTile格式的轻量化压缩时 如何在减少数据量的同时 保证或提升数据质量是一大挑战 以下为一些提升数据质量的方法分析 改进几何简化算法 在进行几何简化时 除了考虑
  • os.environ[‘CUDA_VISIBLE_DEVICES‘]指定GPU后,还是用的“0“卡

    背景 实验室服务器有多张显卡 但今天 0 卡显存已被占满 因此我在代码中添加os environ CUDA VISIBLE DEVICES 2 指定使用编号为 2 的显卡 显存够的前提下 但跑VGG16的时候却报错 经过调试发现os env
  • 【tvm官网教程】张量表达与调度

    tvm官网教程 张量表达与调度 目的 1 调度原语 1 1 te常用接口 1 2 tvm常用接口 1 3 stage常用成员函数 2 内置函数与数学函数 2 1 直接声明外部数学调用 2 2 统一内置函数调用 2 3 内置函数下降规则 3
  • docker宿主机访问容器_干货来啦!带你初探Docker逃逸

    Docker是当今使用范围最广的开源容器技术之一 具有高效易用的优点 然而如果使用Docker时采取不当安全策略 则可能导致系统面临安全威胁 本期安仔课堂 ISEC实验室的张老师将为大家介绍不同环境下 Docker逃逸至外部宿主机的情况 一
  • 人工智能算法总结

    一 按照模型训练方式不同分类 可以分为监督学习 Supervised Learning 无监督学习 Unsupervised Learning 半监督学习 Semi supervised Learning 和强化学习 Reinforceme
  • case class

    case class scala里的case class和普通class有几点不同 1 初始化的时候可以不用new 当然你也可以加上 普通类一定需要加new 2 toString的实现更漂亮 3 默认实现了equals 和hashCode
  • BUUCTF-Crypto--一眼就解密 writeup分享

    题目描述 给定密文 ZmxhZ3tUSEVfRkxBR19PRl9USElTX1NUUklOR30 通过解密得出明文 并将结果包上flag 提交 题目求解 我们首先观察密文 类似于章节中的一道题 上面的都是小写的 我们用的是MD5的方法求解
  • 如何使用Eclipse

    1 安装好Eclipse双击进去使用 会先跳出来一个Workspace的工作环境路径选择 自己选好要保存的路径即可保存 如果不小心勾了下面的 设置为默认并不在询问 的窗口 进入Eclipse之后 点击最上方菜单栏 Window Prefer
  • Hyperledger Fabric 入门笔记(五)项目fabric-samples简介

    文章目录 前言 一 特定场景的链码 应用示例 1 1 资产转移asset transfer系列 1 2 拍卖auction系列 1 3 代币token系列 1 4 其它 二 测试网络 三 其它用途 四 过时的内容 五 未知用途 前言 本文对
  • wsl访问Win10中MySQL_在 Windows 10 上安装适用于 Linux 的 Windows 子系统 (WSL)

    适用于 Linux 的 Windows 子系统安装指南 Windows 10 Windows Subsystem for Linux Installation Guide for Windows 10 09 15 2020 本文内容 安装适
  • 腾讯云轻量应用服务器性能测评(全网超详细)

    腾讯云轻量应用服务器性能如何 CPU型号主频 内存 公网带宽和系统盘存储多维对比 轻量应用服务器会不会比云服务器CVM性能差 相对于CVM云服务器轻量服务器更适合轻量级的应用 轻量服务适合中小企或个人开发者用于搭建We网站b应用 小程序 A
  • Base64FileUtils工具类

    package com ruoyi common utils import org apache commons codec binary Base64 import java io public class Base64FileUtils
  • 报错注入的原理分析

    SQL报错注入就是利用数据库的某些机制 人为地制造错误条件 使得查询结果能够出现在错误信息中 这种手段在联合查询受限且能返回错误信息的情况下比较好用 01使用报错注入的前提 页面上没有显示位但是有sql语句执行错误信息输出位 使用mysql
  • android 标题栏,状态栏和导航栏的区别

    http blog csdn net baidu 26352053 article details 53025912 标题栏是手机左上最顶上 显示中国移动 安全卫士 或者当前运行软件的地方 手机的顶部 右边显示信号 电量 网速等等是状态栏
  • linux docker常用命令

    docker ps 显示当前正在运行的容器 docker ps a 显示所有状态的容器 docker images 列出本地镜像 docker port 容器Id 查看容器端口 lsof i 容器端口 检查容器端口是否可用 docker s
  • Prometheus 安装部署监控JMX

    系统环境 系统版本 centos7 Prometheus版本 2 20 1 服务器ip 192 168 0 226 客户端ip 192 168 10 62 一 服务器端系统安装 192 168 0 226 1 将下载好的包文件上传到服务器并
  • 前端学习之原生JS实现attr方法的封装

    HTML代码 img src images 1 jpg alt JS代码 功能 1 参数为2个 设置 2 参数为1个 设置批量属性 获取属性 function attr property value console log property
  • ubuntu18.04配置Swin Transformer环境

    1 安装pytorch pip install torch 1 8 1 i https pypi douban com simple pip install torchvision 0 9 1 i https pypi douban com
  • 电脑打开计算机显示远程过程调用失败,win7系统电脑弹出提示“远程过程调用失败且未执行”的解决方法...

    win7系统使用久了 好多网友反馈说win7系统电脑弹出提示 远程过程调用失败且未执行 的问题 非常不方便 有什么办法可以永久解决win7系统电脑弹出提示 远程过程调用失败且未执行 的问题 面对win7系统电脑弹出提示 远程过程调用失败且未
  • java 原始套接字编程_套接字编程原理

    6 多路复用 select 功能 用来检测一个或多个套接字状态 格式 int PASCAL FAR select int nfds fd set FAR readfds fd set FAR writefds fd set FAR exce