基本套接字TCP和UDP

TCP的Java支持

协议相当于相互通信的程序间达成的一种约定,它规定了分组报文的结构、交换方式、包含的意义以及怎样对报文所包含的信息进行解析,TCP/IP协议族有IP协议、TCP协议和UDP协议。现在TCP/IP协议族中的主要socket类型为流套接字(使用TCP协议)和数据报套接字(使用UDP协议)。

TCP协议提供面向连接的服务,通过它建立的是可靠地连接。Java为TCP协议提供了两个类:Socket类和ServerSocket类。一个Socket实例代表了TCP连接的一个客户端,而一个ServerSocket实例代表了TCP连接的一个服务器端,一般在TCP Socket编程中,客户端有多个,而服务器端只有一个,客户端TCP向服务器端TCP发送连接请求,服务器端的ServerSocket实例则监听来自客户端的TCP连接请求,并为每个请求创建新的Socket实例,由于服务端在调用accept()等待客户端的连接请求时会阻塞,直到收到客户端发送的连接请求才会继续往下执行代码,因此要为每个Socket连接开启一个线程。服务器端要同时处理ServerSocket实例和Socket实例,而客户端只需要使用Socket实例。另外,每个Socket实例会关联一个InputStream和OutputStream对象,我们通过将字节写入套接字的OutputStream来发送数据,并通过从InputStream来接收数据。

TCP连接的建立步骤

客户端向服务器端发送连接请求后,就被动地等待服务器的响应。典型的TCP客户端要经过下面三步操作:

1、创建一个Socket实例:构造函数向指定的远程主机和端口建立一个TCP连接;

2.通过套接字的I/O流与服务端通信;

3、使用Socket类的close方法关闭连接。

服务端的工作是建立一个通信终端,并被动地等待客户端的连接。典型的TCP服务端执行如下两步操作:

1、创建一个ServerSocket实例并指定本地端口,用来监听客户端在该端口发送的TCP连接请求;

2、重复执行:

1)调用ServerSocket的accept()方法以获取客户端连接,并通过其返回值创建一个Socket实例;

2)为返回的Socket实例开启新的线程,并使用返回的Socket实例的I/O流与客户端通信;

3)通信完成后,使用Socket类的close()方法关闭该客户端的套接字连接。

DEMO

客户端

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;

public class Exercise {
	public void tcpExample(){
		try {
			Socket socket = new Socket("192.168.138.46",8344);
			System.out.println("Connection ....to server sending echo string");
			OutputStream out = socket.getOutputStream();
			System.out.println("发送数据……");
			out.write("测试连接并发送数据到服务器成功……".getBytes());
			socket.close();
			System.out.println("客户端已经关闭");

		} catch (UnknownHostException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
	public static void main(String[] args) {
		Exercise e = new Exercise();
		//e.getInterfaceExample();
		e.tcpExample();

	}
}

服务端

package NET;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketAddress;

public class TCPServer {
	//设置缓冲区的大小
	private static final int BUFSIZE = 50;

	public static void tcpsever(){
		try {
			ServerSocket serSocket = new ServerSocket(8344);
			int recvMsgSize = 0;
			int total=0;
			byte[] receive =  new byte[BUFSIZE];
			while(true){
				//此方法返回一个服务器的关联socket,用此进行数据交换
				Socket clntSock = serSocket.accept();
				SocketAddress socadd = clntSock.getRemoteSocketAddress();
				System.out.println("clint address:"+socadd);
				InputStream in = clntSock.getInputStream();
				while((total=in.read(receive, recvMsgSize, BUFSIZE-recvMsgSize))!=-1){
					recvMsgSize += total;
				}
				System.out.println("从客户端收到的数据:"+new String(receive));
				clntSock.close();
				System.out.println("关闭和此客户端的连接……");
			}

		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}
	public static void main(String args[]){
		tcpsever();
	}

}

UDP的Java支持

UDP协议提供的服务不同于TCP协议的端到端服务,它是面向非连接的,属不可靠协议,UDP套接字在使用前不需要进行连接。实际上,UDP协议只实现了两个功能:

1)在IP协议的基础上添加了端口;

2)对传输过程中可能产生的数据错误进行了检测,并抛弃已经损坏的数据。

Java通过DatagramPacket类和DatagramSocket类来使用UDP套接字,客户端和服务器端都通过DatagramSocket的send()方法和receive()方法来发送和接收数据,用DatagramPacket来包装需要发送或者接收到的数据。发送信息时,Java创建一个包含待发送信息的DatagramPacket实例,并将其作为参数传递给DatagramSocket实例的send()方法;接收信息时,Java程序首先创建一个DatagramPacket实例,该实例预先分配了一些空间,并将接收到的信息存放在该空间中,然后把该实例作为参数传递给DatagramSocket实例的receive()方法。在创建DatagramPacket实例时,要注意:如果该实例用来包装待接收的数据,则不指定数据来源的远程主机和端口,只需指定一个缓存数据的byte数组即可(在调用receive()方法接收到数据后,源地址和端口等信息会自动包含在DatagramPacket实例中),而如果该实例用来包装待发送的数据,则要指定要发送到的目的主机和端口。

UDP的通信建立的步骤

UDP客户端首先向被动等待联系的服务器发送一个数据报文。一个典型的UDP客户端要经过下面三步操作:

1、创建一个DatagramSocket实例,可以有选择地对本地地址和端口号进行设置,如果设置了端口号,则客户端会在该端口号上监听从服务器端发送来的数据;

2、使用DatagramSocket实例的send()和receive()方法来发送和接收DatagramPacket实例,进行通信;

3、通信完成后,调用DatagramSocket实例的close()方法来关闭该套接字。

由于UDP是无连接的,因此UDP服务端不需要等待客户端的请求以建立连接。另外,UDP服务器为所有通信使用同一套接字,这点与TCP服务器不同,TCP服务器则为每个成功返回的accept()方法创建一个新的套接字。一个典型的UDP服务端要经过下面三步操作:

1、创建一个DatagramSocket实例,指定本地端口号,并可以有选择地指定本地地址,此时,服务器已经准备好从任何客户端接收数据报文;

2、使用DatagramSocket实例的receive()方法接收一个DatagramPacket实例,当receive()方法返回时,数据报文就包含了客户端的地址,这样就知道了回复信息应该发送到什么地方;

3、使用DatagramSocket实例的send()方法向服务器端返回DatagramPacket实例。

UDP Socket Demo

这里有一点需要注意:

UDP程序在receive()方法处阻塞,直到收到一个数据报文或等待超时。由于UDP协议是不可靠协议,如果数据报在传输过程中发生丢失,那么程序将会一直阻塞在receive()方法处,这样客户端将永远都接收不到服务器端发送回来的数据,但是又没有任何提示。为了避免这个问题,我们在客户端使用DatagramSocket类的setSoTimeout()方法来制定receive()方法的最长阻塞时间,并指定重发数据报的次数,如果每次阻塞都超时,并且重发次数达到了设置的上限,则关闭客户端。

DEMO‘

package NET;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;

import com.sun.jmx.snmp.InetAddressAcl;

public class UDP {
	private final static int  TIMEOUT = 3000;
	private final static int BUFSIZE = 50;
	private final static int tries = 5;
	/**
	 * udp连接的客户端
	 * @throws SocketException
	 * @throws UnknownHostException
	 */
	public void udpclnt() throws SocketException, UnknownHostException{
		int tril = 0;
		DatagramSocket socket = new DatagramSocket();
		String art = "UDP连接测试成功……";
		byte[] by = art.getBytes();
		InetAddress address = null;
		address = InetAddress.getByName("lenovo-PC");
		socket.setSoTimeout(0);
		DatagramPacket send = new DatagramPacket(by,by.length,address,30000 );
		  try {
			  socket.send(send); 

			  } catch (IOException e) {
				  tril++;
				  System.out.println("进行重发:剩余重发次数"+ (tries-tril));
			  }
		  socket.close();
	}
    /**
     * udp连接的服务器
     */
    public void udpServer(){
    	try {
			DatagramSocket socket = new DatagramSocket(30000);
			socket.setSoTimeout(TIMEOUT);
			DatagramPacket receive = new DatagramPacket(new byte[50], BUFSIZE);
			socket.receive(receive);
			System.out.println("连接的客户端IP:"+receive.getAddress().getHostAddress()+"端口:"+receive.getPort());
		   byte[]re= receive.getData();
		    System.out.println("收到的数据:"+new String(re));
    	} catch (SocketException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    }
    public static void main(String []args){
    	final UDP udp = new UDP();
    	Thread thread = new Thread(){
    			   public void run(){
    			     try {
						udp.udpclnt();
					} catch (SocketException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					} catch (UnknownHostException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
    			   }
    			};
    	thread.start();
    	udp.udpServer();

    }
}

总结:

1、编写TCP客户端程序,在实例化Socket类时,要注意,底层的TCP协议只能处理IP协议,如果传递的第一个参数是主机名字而不是你IP地址,Socket类具体实现的时候会将其解析成相应的地址,若因为某些原因连接失败,构造函数会抛出一个IOException异常。

2、TCP协议读写数据时,read()方法在没有可读数据时会阻塞等待,直到有新的数据可读。另外,TCP协议并不能确定在read()和write()方法中所发送信息的界限,接收或发送的数据可能被TCP协议分割成了多个部分。说到这里,其实是与内部结构相关的

TCP在发送之后会把所有的数据复制到缓冲区中,而UDP则send之后所有的消息都在发送途中。

3、编写TCP服务器端的程序将在accept()方法处阻塞,以等待客户端的连接请求,一旦取得连接,便要为每个客户端的连接建立一个Socket实例来进行数据通信。

4、在UDP程序中,创建DatagramPacket实例时,如果没有指定远程主机地址和端口,则该实例用来接收数据(尽管可以调用setXXX()等方法指定),如果指定了远程主机地址和端口,则该实例用来发送数据。

5、UDP程序在receive()方法处阻塞,直到收到一个数据报文或等待超时。由于UDP协议是不可靠协议,如果数据报在传输过程中发生丢失,那么程序将会一直阻塞在receive()方法处,这对客户端来说是肯定不行的,为了避免这个问题,我们在客户端使用DatagramSocket类的setSoTimeout()方法来制定receive()方法的最长阻塞时间,并指定重发数据报的次数,如果每次阻塞都超时,并且重发次数达到了设置的上限,则关闭客户端。

6、UDP服务器为所有通信使用同一套接字,这点与TCP服务器不同,TCP服务器则为每个成功返回的accept()方法创建一个新的套接字。

7、在UDP程序中,DatagramSocket的每一次receive()调用最多只能接收调用一次send()方法所发送的数据,而且,不同的receive()方法调用绝对不会返回同一个send()方法所发送的额数据。

8、在UDP套接字编程中,如果receive()方法在一个缓冲区大小为n的DatagramPscket实例中调用,而接受队列中的第一个消息长度大于n,则receive()方法只返回这条消息的前n个字节,超出的其他字节部分将自动被丢弃,而且也没有任何消息丢失的提示。因此,接受者应该提供一个足够大的缓存空间的DatagramPacket实例,以完整地存放调用receive()方法时应用程序协议所允许的最大长度的消息。一个DatagramPacket实例中所运行传输的最大数据量为65507个字节,即UDP数据报文所能负载的最多数据,因此,使用一个有65600字节左右缓存数组的数据总是安全的。

9、在UDP套接字编程中,每一个DatagramPacket实例都包含一个内部消息长度值,而该实例一接收到新消息,这个长度值便可能改变(以反映实际接收的消息的字节数)。如果一个应用程序使用同一个DatagramPacket实例多次调用receive()方法,每次调用前就必须显式地将消息的内部长度重置为缓冲区的实际长度。

10、另一个潜在问题的根源是DatagramPacket类的getData()方法,该方法总是返回缓冲区的原始大小,忽略了实际数据的内部偏移量和长度信息。

11.TCP是需要连接的,UDP不用连接,是不可靠的传输,所以使用UDP要重排乱序消息或者处理丢失消息,因此这就加大了服务器的负担。但是之所以使用UDP是因为在传输信息很少时,TCP握手花费的时间基本是传输的2倍,还有在没有可靠性要求时,UDP很灵活。

时间: 2024-08-05 11:14:01

基本套接字TCP和UDP的相关文章

socket套接字TCP API

socket套接字TCP API socket概念 socket又称"套接字",是计算机网络中进程间通信数据通道的一个端点,或称之为句柄.IP地址+端口号就可以唯一确定一个socket. TCP/IP协议族包括传输层(TCP/UDP),网络层(ICMP/IP/IGMP),链路层(ARP/RARP).应用层通常使用socket地址,即IP地址+端口号来确定通信的对端.而socket正是TCP/IP协议族与应用层之间的接口层,可以说对上层提供了TCP/IP协议族的一种封装,无需关心更底层的

linux网络环境下socket套接字编程(UDP文件传输)

今天我们来介绍一下在linux网络环境下使用socket套接字实现两个进程下文件的上传,下载,和退出操作! 在socket套接字编程中,我们当然可以基于TCP的传输协议来进行传输,但是在文件的传输中,如果我们使用TCP传输,会造成传输速度较慢的情况,所以我们在进行文件传输的过程中,最好要使用UDP传输. 在其中,我们需要写两个程序,一个客户端,一个服务端,在一个终端中,先运行服务端,在运行客户端,在服务端和客户端都输入IP地址和端口号,注意服务端和客户端的端口号要相同,然后选择功能,在linux

原始套接字-TCP/IP下三层数据显示

1 #include <stdio.h> 2 #include <errno.h> 3 #include <unistd.h> 4 #include <sys/socket.h> 5 #include <sys/types.h> 6 #include <linux/in.h> 7 #include <linux/if_ether.h> 8 9 int main(int argc, char **argv) 10 { 11

Python网络编程—socket套接字编程(UDP)

套接字介绍 1.套接字 : 实现网络编程进行数据传输的一种技术手段 2.Python实现套接字编程:import socket 3.套接字分类 流式套接字(SOCK_STREAM): 以字节流方式传输数据,实现tcp网络传输方案.(面向连接--tcp协议--可靠的--流式套接字) 数据报套接字(SOCK_DGRAM):以数据报形式传输数据,实现udp网络传输方案.(无连接--udp协议--不可靠--数据报套接字) UDP套接字编程 服务端流程 1.创建数据报套接字 sockfd = socket

套接字,TCP,UDP

一.Socket 1.socket由来 #Socket是应用层与TCP/IP协议族通信的中间软件抽象层,它是一组接口. #我们无需深入理解tcp/udp协议,socket已经为我们封装好了,我们只需要遵循socket的规定去编程,写出的程序自然就是遵循tcp/udp标准的. 2.socket分类 #1.基于文件类型的套接字家族 #套接字家族的名字:AF_UNIX unix一切皆文件,基于文件的套接字调用的就是底层的文件系统来取数据,两个套接字进程运行在同一机器,可以通过访问同一个文件系统间接完成

探索UDP套接字编程

UDP和TCP处于同一层网络模型中,也就是运输层,基于二者之上的应用有很多,常见的基于TCP的有HTTP.Telnet等,基于UDP有DNS.NFS.SNMP等.UDP是无连接,不可靠的数据协议服务,而TCP提供面向流.提供可靠数据服务.注意,UDP和TCP没有好坏之分,只是二者的适用场景不同罢了. 典型的UDP套接字编程模型是客户端不予服务端建立连接,而只是调用sendto函数来向服务端发送数据,其中必须要指定服务端的信息,包括IP和端口等:服务端不接收来自客户端的连接,而只是调用recvfr

【转】 探索UDP套接字编程

UDP和TCP处于同一层网络模型中,也就是运输层,基于二者之上的应用有很多,常见的基于TCP的有HTTP.Telnet等,基于UDP有DNS.NFS.SNMP等.UDP是无连接,不可靠的数据协议服务,而TCP提供面向流.提供可靠数据服务.注意,UDP和TCP没有好坏之分,只是二者的适用场景不同罢了. 典型的UDP套接字编程模型是客户端不予服务端建立连接,而只是调用sendto函数来向服务端发送数据,其中必须要指定服务端的信息,包括IP和端口等:服务端不接收来自客户端的连接,而只是调用recvfr

【UNIX网络编程(二)】基本TCP套接字编程函数

基于TCP客户/服务器程序的套接字函数图如下: 执行网络I/O,一个进程必须做的第一件事就是调用socket函数,指定期望的通信协议类型. #include <sys/socket.h> int socket(int family, int type, int protocol);/*返回值:若成功则为非负描述符,若出错则为-1*/ socket函数成功时返回一个小的非负整数值,它与文件描述符类似,把它称为套接字描述符,简称sockfd.family参数指明协议族,被称为协议域.type参数指

《网络编程》基本 UDP 套接字编程

在前面文章中介绍了<UDP 协议>和<套接字数据传输>.UDP 协议和 TCP 协议不同,它是一种面向无连接.不可靠的传输层协议.在基于 UDP 套接字编程中,数据传输可用函数 sendto 和 recvfrom.以下是基本 UDP 套接字编程过程: sendto 与 recvfrom 函数 这两个函数的功能类似于 write 和 read 函数,可用无连接的套接字编程.其定义如下: /* 函数功能:发送数据: * 返回值:若成功则返回已发送的字节数,若出错则返回-1: * 函数原