-
2021-07-27 05:41:11
实验三、WINSOCK套接字编程实验报告
序号: 10 姓名: 学号: 成绩 指导老师:
1.实验目的:
用C或JAVA语言编写客户端、服务器程序,实现基于TCP或UDP的网络通信数据传输服务,熟悉基于TCP或UDP的Socket编程原理。 2.实验环境:
建立在TCP/IP 网络体系结构之上计算机网络实验环境。各计算机除了安装TCP/IP 软件外,还安装了TCP/IP 开发系统。计算机具备Windows环境中套接字socket 的编程接口功能,可为用户提供全网范围的进程通信功能。 3.实验指导:
参见套接字编程实验指导 4.实验步骤
(1)运行指导书中给出的参考程序,分析实验结果,并回答问题(1)-(3)
(2)根据给定参考程序修改代码,完善修改服务器和客户端的功能。并回答问题(4)-(5)
5.实验结果分析
(1) 为什么在服务器和客户端要包含winsock2.h文件?
答:这是系统自定义的“宏”,其中还包含了许多自定义的类型,如果不包含该文件,程序中会有许多类型无法识别。
(2) 为什么在服务器和客户端程序中要加入#pragma comment(lib,"ws2_32.lib") 语
句,如果不加会出现什么问题?
答:这句话的意思是链接“ws2_32.lib”这个库,如果不加就链接不到这个库。
(3) 为什么在服务器和客户端程序中要使用WSAStartup函数,如果不用程序会有什么问
题?
答:为了在应用程序当中调用任何一个Winsock API函数,首先第一件事情就是必须通过WSAStartup函数完成对Winsock服务的初始化,因此需要使用WSAStartup函数。如果不用,程序将无法调用Socket库中的其他函数。 (4) 修改后的程序完成实现了什么功能,附上修改后的源代码。(修改或填加的代码用
波浪线标注,并填加注释),并附上实验截图
#include "stdafx.h" #include #include #include #include #include #include #include
#pragma comment(lib,"ws2_32.lib")
#define MYPORT 3490 /*定义用户连接端口*/ #define BACKLOG 10 /*多少等待连接控制*/
更多相关内容 -
Java套接字(Socket)网络编程入门
2020-09-03 06:55:53主要介绍了Java套接字(Socket)网络编程入门,Socket可以理解为是对TCP/IP协议的抽象,需要的朋友可以参考下 -
JAVA网络编程资料(1)-Socket套接字—Java套接字编程.chm
2007-09-06 23:08:04JAVA网络编程资料(1)-Socket套接字—Java套接字编程.chm -
java_关于网络编程、套接字编程
2018-09-02 14:46:20套接字编程 ServerSocket类的使用 构造器: 常用方法: Socket类的使用 构造器: 常用方法: 网络编程: java语言中,提供了一套统一的编程接口,很多细节已经底层化,所以可以无痛的网络通信编程 提供...网络编程:
java语言中,提供了一套统一的编程接口,很多细节已经底层化,所以可以无痛的网络通信编程
提供的是Socket套接字技术-
常用的通信协议
-
TCP(信息传递协议)/IP(地址信息协议):通信之前,需要建立连接,之后,要断开连接,一般有一个作为服务器端,一个作为客户端
主要将通讯模式分成4个层: 应用层 传输层 IP层 编程接口层
-
UDP:不需要建立连接,相对来说,开销比较小,效率高,不安全
类型:java.net.InetAddress
提供了用于获取描述主机和IP的信息功能常用方法:
static InetAddress getByName(String host) 返回一个指定字符串主机的地址对象 主机名:可以使用IP来描述,还可以使用域名来使用(通常域名会邦定一个IP地址) String getHostName() 返回描述主机的字符串名称 String getHostAddress() 返回描述主机的IP地址 static InetAddress getLocalHost() 返回本地主机地址对象
/** * InetAddress类型的学习 */ public class Test01 { public static void main(String[] args) { try { InetAddress addr = InetAddress.getByName("www.baidu.com"); System.out.println(addr.getHostAddress()); System.out.println(addr.getHostName()); InetAddress local = addr.getLocalHost(); /*本地IP联网状态下,是网络终端的服务器随机分配的IP*/ System.out.println(local.getHostAddress()); System.out.println(local.getHostName()); /* 不联网状态下,我们的本地IP和主机名 * 可以使用127.0.0.1和localHost来替代 */ InetAddress lo = InetAddress.getByName("127.0.0.1"); System.out.println(lo); lo = InetAddress.getByName("localHost"); System.out.println(lo); } catch (UnknownHostException e) { e.printStackTrace(); } } }
套接字编程
Socket 在客户端使用,连接成功后会获取一个Socket对象 ServerSocket 在服务器端使用创建服务器端套接字对象,被连接后,获取一个Socket对象
注意:通信是使用各自获取的Socket对象来通信
编写步骤: 服务器对象 服务器等待被连接 客户端连接服务器(成功与否) 客户端与服务端通信 断开连接
端口号:是一个16位,无符号二进制整数,0-65535
和IP一起用来指定一个运行中程序的位置
所以这么说:端口号是运行程序的唯一标识符,即程序运行时,端口号不能碰撞通常0-1023是操作系统预定义的端口,所以,我们在自定义时,应该尽可能使用1024-65535范围的数
ServerSocket类的使用
构造器:
ServerSocket(int port) 创建一个指定端口号的ServerSocket对象
常用方法:
Socket accept() 等待客户端连接,返回客户端对象 void close() 关闭套接字 InetAddress getInetAddress() 获取服务端的地址 int getLocalPort() 获取服务端口号 SocketAddress getLocalSocketAddress() 获取
Socket类的使用
构造器:
Socket(String host, int port) 向指定的主机名和指定端口号的服务发送请求连接,连接成功,获取Socket对象
常用方法:
void close() 关闭套接字 InetAddress getInetAddress() 返回套接字所连接服务器的地址。 int getPort() 返回套接字所连接远程机器的窗口号 int getLocalPort() 返回本地端口 InputStream getInputStream() 获取输入流对象,接收远程机器发送的信息 OutputStream getOutputStream() 获取输出流对象,向远程机器发送信息
/** * 服务器端编程:ServerSocket */ public class Server { private ServerSocket server; public Server() { /* * 构造器ServerSocket(int port) * 使用指定的端口号来创建一个服务套接字对象 */ try { server = new ServerSocket(8088); } catch (IOException e) { e.printStackTrace(); } } private void start() { try { System.out.println("-----等待被连接-----"); /* * Socket accept() * 有阻塞效果,等待被客户端连接 */ Socket socket = server.accept(); System.out.println("-----连接成功-----"); } catch (IOException e) { e.printStackTrace(); } } public static void main(String[] args) { Server server = new Server(); server.start(); } }
/** * 客户端的编写 */ public class Client { private Socket socket; public Client() { try { System.out.println("-----正在努力连接服务器-----"); socket = new Socket("localHost", 8088); System.out.println("-----连接服务器成功-----"); } catch (IOException e) { e.printStackTrace(); } } private void start() { } public static void main(String[] args) { Client client = new Client(); client.start(); } }
-
-
套接字编程原理
2021-02-26 18:14:546、多路复用——select() 功能:用来检测一个或多个套接字状态。 格式:int PASCAL FAR select(int nfds,fd_set FAR * readfds,fd_set FAR * writefds, fd_set FAR * exceptfds,const struct timeval FAR * timeout)...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 面向连接的套接字的系统调用时序图
2.2 无连接协议的套接字调用时序图
2.3 面向连接的应用程序流程图
三、 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
我们来分析一下图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
-
实验三、WINSOCK套接字编程实验报告
2021-04-17 09:48:54实验三、WINSOCK套接字编程实验报告实验三、WINSOCK套接字编程实验报告序号: 姓名: 学号: 成绩: 指导老师:1.实验目的:用C或JAVA语言编写客户端、服务器程序,实现基于TCP或UDP的网络通信数据传输服务,熟悉基于...实验三、WINSOCK套接字编程实验报告
实验三、WINSOCK套接字编程实验报告
序号: 姓名: 学号: 成绩: 指导老师:
1.实验目的:
用C或JAVA语言编写客户端、服务器程序,实现基于TCP或UDP的网络通信数据传输服务,熟悉基于TCP或UDP的Socket编程原理。
2.实验环境:
建立在TCP/IP 网络体系结构之上计算机网络实验环境。各计算机除了安装TCP/IP 软件外,还安装了TCP/IP 开发系统。计算机具备Windows环境中套接字socket 的编程接口功能,可为用户提供全网范围的进程通信功能。
3.实验指导:
参见套接字编程实验指导
4.实验步骤
(1)运行指导书中给出的参考程序,分析实验结果,并回答问题(1)-(3)
(2)根据给定参考程序修改代码,完善修改服务器和客户端的功能。并回答问题(4)-(5)
5.实验结果分析
(1) 为什么在服务器和客户端要包含winsock2.h文件? 答:
windows socket的头文件,不包含该头文件,关于socket编程的结构和函数等都不能被调用。
(2) 为什么在服务器和客户端程序中要加入#pragma comment(lib,"ws2_32.lib")
语句,如果不加会出现什么问题?
答:#pragma comment(lib, "Ws2_32.lib ")表示链接Ws2_32.lib这个库。 和在工程设置里写上链入Ws2_32.lib的效果一样,不过这种方法写的 程序别人在使用你的代码的时候就不用再设置工程settings了。
如果没有这个
(3) 为什么在服务器和客户端程序中要使用WSAStartup函数,如果不用,程序会有
什么问题?
答:
使用Socket的程序在使用Socket之前必须调用WSAStartup函数。该函数的第一个参数指明程序请求使用的Socket版本,其中高位字节指明副版本、低位字
节指明主版本;操作系统利用第二个参数返回请求的Socket的版本信息。当一个应用程序调用WSAStartup函数时,操作系统根据请求的Socket版本来搜索相应的Socket库,然后绑定找到的Socket库到该应用程序中。以后应用程序就
-
实验三WINSOCK套接字编程实验报告.docx
2020-09-28 09:21:55百度文库- 百度文库-让每个人平等地提升自我 PAGE PAGE # 实验三WINSOC套接字编程实验报告 一 实验目的 用C或JAVA语言编写客户端服务器程序实现基于 TCP或 UDP的网络通信数据传输服务熟悉基于 TCP或 UDP的Socket编程... -
实验三、WINSOCK套接字编程实验报告.pdf
2020-07-29 23:42:40实验三 WINSOCK套接字编程实验报告 一实验目的 用 C 或 JAVA 语言编写客户端服务器程序实现基于 TCP 或 UDP 的网络通信数据传输服务熟悉基于 TCP或 UDP的 Socket 编程 原理 二实验环境 建立在 TCP/IP 网络体系结构... -
C++套接字编程基础知识介绍
2022-03-31 10:29:38无论在Windows平台还是Linux平台,都对套接字实现了自己的一套编程接口。Windows下的Socket实现叫WindowsSocket。Linux下的实现有两套:一套是伯克利套接口,起源于BerkeleyUnix,这套接口很简单,得到了广泛应用,... -
Java 网络编程从菜鸟到叫兽 4:面向流的套接字 I/O
2021-03-11 16:09:02输入流使用套接字的输入流读取数据时,当前线程会进入阻塞状态,直到套接字收到一些数据为止(亦即套接字的接收缓冲区有可用数据)。该输入流的 available() 方法只是返回接收缓冲区的可用字节数量,不... -
【C语言核心系列】C语言中TCP的套接字的socket编程
2021-03-19 21:05:08C语言基于TCP面向连接的socket编程的服务器端程序流程 -
计算机网络实验:套接字及客户服务器应用程序基础(Java实现点对点聊天的实用程序)
2022-01-19 22:21:05实验名称: 套接字及客户服务器应用程序基础 实验介绍: ...参考实验《套接字及客户/服务器应用程序基础》,Winsock编程基础。 网络各层常见协议 应用层:TFTP,HTTP,SNMP,FTP,SMTP,DNS,Telnet 等 -
socketjava源码-demo-sockets-io-nio-nio2:“Java套接字I/O:阻塞,非阻塞和异步”文章和源代码
2021-05-25 21:28:36Winsock也是基于Berkeley套接字的,具有符合Windows编程模型的附加功能。 POSIX定义 本文使用了POSIX规范中的简化定义。 阻塞的线程-在可以继续执行之前正在等待某种条件的线程。 阻塞-套接字的属性,使套接字对其... -
java 异步非阻塞io_Java套接字IO阻塞非阻塞和异步
2020-09-02 15:58:06java 异步非阻塞io 介绍 (Introduction) When describing I/O, the terms non-blocking and asynchronous are often used interchangeably, but there is a significant difference between them. In this article ... -
计算机网络(套接字及TCP/UDP传输)学习心得
2019-08-12 15:29:50创建套接字(AF_LOCAL) 创建套接字(AF_LOCAL) 准备地址(sockaddr_un) 准备地址(sockaddr_un) 绑定(自己的socket/地址) 连接(connect,连接进程A的地址) 接收数据 发送数据 关闭套接字 ... -
java_socket套接字网络编程
2014-10-14 17:15:004 at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:195 ) 5 at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:182 ) 6 at java.net.SocksSocketImpl.connect(SocksSocketImpl.... -
计算机网络实验二:UDP套接字编程实现多人聊天
2018-05-13 09:51:114. 实现UDP套接字编程。 二、实验内容(一)实验思路1、学习理解UDP协议。2、实现UDP客户端与服务器之间的通信。3、实现UDP客户端之间多线程通信以及聊天页面的UI实现。 (二)实验步骤1、理解UDP协... -
32_网络编程(Socket套接字编程)_讲义
2019-01-12 14:16:05TCP/IP协议中的四层分别是应用层、传输层、网络层和链路层,每层分别负责不同的通信功能,接下来针对这四层进行详细地讲解。 链路层:链路层是用于定义物理传输通道,通常是对某些网络连接设备的驱动协议,例如... -
javasocket编程
2021-02-28 13:30:27Socket,又称为套接字,Socket是计算机网络通信的基本的技术之一。如今大多数基于网络的软件,如浏览器,即时通讯工具甚至是P2P下载都是基于Socket实现的。本文会介绍一下基于TCP/IP的Socket编程,并且如何写一个... -
Socket实现双机通信.doc
2021-05-25 04:31:14目前有两种套接字:流套接字(Stream Socket)和数据报套接字(Datagram Socket)。流套接字提供一个面向连接的、可靠的数据传输服务,保证数据无差错、无重复、按顺序发送,具有流量控制功能,数据被看成字节流,... -
套接字的(客户端与服务器端的连接)
2015-11-07 18:09:32Client.java文件: import java.io.*; import java.net.*; public class Client { public static void main(String args[]) { String [] mess = {"1+1在什么情况下不等于2", "狗为什么不生跳蚤","什么东西能看、... -
socket套接字
2018-07-16 15:59:43socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口;HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。... -
C#套接字学习总结
2016-12-16 22:18:27C# 实现基本的套接字TCP通信 在C#中,实现了很多对Windows API的封装,在套接字通信方面也是如此。下面是一个TCP网络通信的简单实例,因为自己初学C#,记下来 学习 使用名字空间: using System... -
Windows 网络通信套接字技术
2020-04-05 15:59:242、基本套接字 为了更好说明套接字编程原理,这里介绍几个基本的套接字。 创建套接字——socket() 功能:使用前创建一个新的套接字。 格式:SOCKET PASCAL FAR socket(int af,int type,int procotol); 参数:af:... -
利用原始套接字实现tracert路由追踪
2017-03-14 09:52:31这个功能使用原始套接字也可以实现。 我们通过不断地向目的主机发送ICMP-ECHORequest包,并且将包的TTL一开始设为1,这样一到达网关路由器后,路由器就检测到这个包超时了(TTL=0了),于是就会丢弃次包,并返回一... -
java一次编写处处运行_Java 7套接字直接协议–编写一次,到处运行…。 奔跑(在某些地方)
2020-07-08 02:22:55java一次编写处处运行 本文将研究新的Java套接字直接协议 (SDP)技术,这是Java 7 SDK中最近引入的非常令人兴奋的突破。 SDP使超高性能计算(UHPC)社区能够在非常罕见的用例中使用Java普遍存在的功能和优点:对... -
百度地图开发java源码-TCP-IP-NetworkNote:《TCP/IP网络编程》(韩-尹圣雨)学习笔记
2021-06-06 07:34:04理解网络编程和套接字 linux 头文件 #include windows 头文件 #include <winsock2> 基于linux平台的实现 网络编程结束连接请求的套接字创建过程为 1. 调用socket函数创建套接字 int socket(int domain,int type ,... -
Java网络编程--多线程的Socket
2022-04-05 14:33:39在Java中网络程序有2种协议:TCP和UDP。 TCP 是可靠的连接。这个可靠的意思就是得有明确的连接对象才行,就像是打电话,拨打的号码必须得在服务中有人接,这个网络会话才算是建立了。 UDP: 不可靠的连接。... -
java网络编程技术课程设计
2014-05-08 13:46:42利用TCP/IP协议提供的编程接口———Socket(套接字)可以开发出TCP/IP应用程序。正确的使用WindowsSocket,就可使得不同地址下的进程得以在Windows环境下顺利进行各种网络通信,因此研究WinSock更有意义。而基于TCP/IP...