TCP套接字Socket
客户端:
客户端套接字Socket一般向服务器发送请求,所有在套接字中必须包含需要访问的服务器端IP地址和连接的端口号(端口号0-1023一般为系统默认端口,所以选择的端口号必须大于1024)。
eg:Socket s=new Socket("localhost",8888);
服务端:
服务端套接字ServerSocket接收来自客户端的消息并进行转发,在创建服务端套接字ServerSocket中需要包含与客户端对映的端口号,然后ServerSocket套接字创建对象以后,需要用该对象接收来自客户端的消息,该方法会返回一个套接字对象。
eg:ServerSocket server=new ServerSocket(8888);
Socket s=server.accept();
此时客户端和服务器端就可以建立连接了。
完整可运行的项目代码如下:
客户端代码:
package com.lib;
import java.awt.BorderLayout;
import java.awt.TextArea;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class MyClient extends JFrame{
//输入框
JTextField tf=new JTextField();
//接收显示框
TextArea ta=new TextArea();
//接收消息和发送消息
private InputStream in;
private OutputStream out;
private DataOutputStream Dout;
private DataInputStream Din;
public MyClient(){
//设置布局风格上下型
this.add(ta,BorderLayout.NORTH);
this.add(tf,BorderLayout.CENTER);
//setSize()设置宽和高
this.setSize(500,300);
//设置标题
this.setTitle("这是客户端");
//设置显示位置
this.setLocation(500, 200);
//设置文本区和文本行的宽高
this.setResizable(false);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
//先建立连接,并准备好发送和接收消息的操作
connection();
//发送消息
tf.addActionListener(new PostInformation());
//开启多线程接收消息
new Thread(new getReceive()).start();
}
public void connection(){
try {
Socket clientSocket=new Socket("localhost",8888);
System.out.println("客户端已经创建连接");
//准备接收消息
in=clientSocket.getInputStream();
Din=new DataInputStream(in);
//准备发送消息
out=clientSocket.getOutputStream();
Dout=new DataOutputStream(out);
} catch (Exception e) {
e.printStackTrace();
}
}
//通过键盘监听事件发送消息到服务器
public class PostInformation implements ActionListener{
@Override
public void actionPerformed(ActionEvent e) {
String content=tf.getText();
//清空输入框,即设置输入为空
tf.setText("");
try {
Dout.writeUTF(content);
} catch (IOException e1) {
try {
Dout.close();
out.close();
} catch (IOException e2) {
e2.printStackTrace();
}
}
}
}
//开启多线程接收消息,防止一个线程阻塞导致程序崩溃
public class getReceive implements Runnable{
@Override
public void run() {
while(true){
try {
String content;
content = Din.readUTF();
//将接收到的消息显示在文本区中,显示格式如下:
/*
* 第一行显示发送者ID或者ID
* 第二行显示时间:年月日--时分秒--星期
* 第三行显示接收到的内容
*
*/
//获取本地计算机名称
String serverName=InetAddress.getLocalHost().getHostName();
//获得发送者IP
InetAddress myServer=InetAddress.getByName(serverName);
String getIPs=myServer.getHostAddress();
//显示月-日-时-分-秒 -星期-上午或者下午
SimpleDateFormat sd=new SimpleDateFormat("MM-dd HH:mm:ss E a");
//在文本区显示接收的消息
//ta.getText()是上次发送的内容
ta.setText(ta.getText()+"\n"+getIPs+"\n"+sd.format(new Date())+"\n"+content+"\n");
}catch (Exception e) {
try {
Din.close();
in.close();
} catch (Exception b) {
}
}
}
}
}
public static void main(String[] args) {
new MyClient();
}
}
服务端代码:
package com.Socket;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import com.Socket.ChatServer.Client;
public class ManyClientServers {
private InputStream sin;
private OutputStream sout;
private DataInputStream Din;
private DataOutputStream Dout;
//接收多个客户端的内容
static List cs=new ArrayList();
public static void main(String[] args) {
ServerSocket server;
try {
server = new ServerSocket(8888);
System.out.println("服务器端准备开启");
while(true){
Socket s;
s = server.accept();
System.out.println("服务器端开启准备接收消息");
Client c=new Client(s);
new Thread(c).start();
cs.add(c);
}
} catch (IOException e) {
e.printStackTrace();
}
}
//开启多线程防止该线程阻塞,程序崩溃
public static class Client implements Runnable{
Socket s;
InputStream in;
OutputStream out;
DataOutputStream Dout;
DataInputStream Din;
public Client(Socket s){
this.s=s;
try {
in=s.getInputStream();
Din=new DataInputStream(in);
out=s.getOutputStream();
Dout=new DataOutputStream(out);
} catch (IOException e) {
e.printStackTrace();
}
}
public void send(String content){
try {
Dout.writeUTF(content);
} catch (IOException e) {
e.printStackTrace();
try {
Dout.close();
out.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
@Override
public void run() {
while(true){
String content=null;
try {
//开启多线程防止该线程阻塞,程序崩溃
content=Din.readUTF();
System.out.println(content);
//将内容立刻转发给每一个客户端
for(Client c:cs){
c.send(content);
}
} catch (IOException e) {
e.printStackTrace();
try {
Din.close();
in.close();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
}
}
UDP套接字
UDP(数据报传输协议)是运输层面向非连接的不可靠传输协议,但优点是传输速率较快
【注】UPD虽然是面向非连接的协议,但是也有与TCP三次握手相类似的数据包解包和打包操作
UDP客户端:UDP客户端发送消息通过DatagramPack类和DatagramSocket类的相应方法进行数据打包然后发送
eg:
DatagramSocket skt=new DatagramSocket();
DatagramPacket pkt;
pkt=new DatagramPacket(outBuf, outBuf.length,address,9000);//先打包数据
//outBuf为将要发送的数据,可以通过键盘输入,outBuf.length为将要发送的数据长度,address为服务器接收方ip地址,9000为端口号
skt.send(pkt); //数据打包好后发送
UDP服务器端:先创建需要接收的空数据包准备接收数据,然后将接收到的数据包解包
DatagramSocket skt=new DatagramSocket();
DatagramPacket pkt;
pkt=new DatagramPacket(inbuf, inbuf.length); //创建空包准备解包
//inbuf为创建的空字节数组,inbuf.length为数组长度
skt.receive(pkt); //接收数据包并解包
UDP客户端代码:
package com.Socket.UDPSocket;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
public class ClientThread extends Thread{
public ClientThread(){}
public void run(){
String str1;
String serverName;
try {
//必须使用UDPServer服务器电脑的名字才能完成数据发送,否则无法完成数据的发送
serverName="DESKTOP-1SNT9HN";
System.out.println("请发送信息给服务器《"+serverName+"》");
DatagramSocket skt=new DatagramSocket();
DatagramPacket pkt;
while(true){
BufferedReader buf;
buf=new BufferedReader(new InputStreamReader(System.in));
System.out.println("请输入信息");
str1=buf.readLine(); //从键盘输入数据
byte[] outBuf=new byte[str1.length()]; //以字节流形式发送数据
outBuf=str1.getBytes();
//下面获取服务器端地址
InetAddress address=InetAddress.getByName(serverName);
//发送数据时必须指明要发送的数据、数据长度、地址、端口号
pkt=new DatagramPacket(outBuf, outBuf.length,address,9000);//先打包数据
skt.send(pkt); //发生数据报
}
} catch (Exception e) {}
}
}
package com.Socket.UDPSocket;
public class UDPClient {
public static void main(String[] args){
UDPClient frm=new UDPClient();
}
ClientThread ct;
public UDPClient(){
ct=new ClientThread(){};
ct.start();
}
}
UDP服务端代码:
package com.Socket.UDPSocket;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
public class SerThread extends Thread {
public SerThread(){}
public void run(){
String str1;
//使用数据报套接字建立对象接受发生数据
try {
DatagramSocket skt=new DatagramSocket(9000);
System.out.println("服务器名:");
//显示服务器计算机名称
System.out.println(InetAddress.getLocalHost().getHostName());
while(true){
byte[] inbuf=new byte[256];
//下面是获取服务器地址
DatagramPacket pkt;
//创建并接收pkt对象的接收消息
//接受消息时只需指明要接受的数据、数据长度
pkt=new DatagramPacket(inbuf, inbuf.length);
skt.receive(pkt);
//提取接收到的分组中的数据并转换成字符串
str1=new String(pkt.getData());
str1=str1.trim();//去掉字符串中的首尾空格
if(str1.length()>0){
int pot=pkt.getPort();
System.out.println("远程端口号"+pot);
System.out.println("服务器已经接受到的消息"+str1);
}
}
} catch (Exception e) {return;}
}
}
package com.Socket.UDPSocket;
public class UDPServer {
public static void main(String[] args) {
UDPServer frm=new UDPServer();
}
String strbuf=" ";
SerThread st;
public UDPServer(){
st=new SerThread();
st.start();
}
}