TCP网络程序设计是指用Socket类编写通信程序。利用TCP协议进行通信的两个应用程序是有主次之分的,一个称为服务器程序,另一个称为客户机程序。两者的功能和编写方法不大一样。服务器端与客户端的交互过程如图所示:
java.net包中的InetAddress类是与IP地址相关的类,利用该类可以获取IP地址、主机地址等信息。InetAddress类的常用方法如下表所示:
方法 | 返回值 | 说明 |
---|---|---|
getByName(String host) | InetAddress | 获取与Host相对应的InetAddress对象 |
getHostAddress() | String | 获取InetAddress对象所包含的IP地址 |
getHostName() | String | 获取此IP地址的主机名 |
getLocalHost() | InetAddress | 返回本地主机的InetAddress对象 |
代码示例如下:
import java.net.*;
public class Address{
public static void main(String[] args){
InetAddress ip;
try{
ip = InetAddress.getLocalHost();
String localname = ip.getHostName();
String localip = ip.getHostAddress();
System.out.println("本机名:" + localname);
System.out.println("本机IP地址:" + localip);
}catch(UnknownHostException e){
e.printStackTrace();
}
}
}
java.net包中的ServerSocket类用于表示服务器套接字,其主要功能是等待来自网络上的”请求“,它可通过指定的端口来等待连接的套接字。服务器套接字一次可以与一个套接字连接。如果多台客户机同时提出连接请求,服务器套接字会将请求连接的客户机存入队列中,然后从中取出一个套接字,与服务器新建的套接字连接起来。若请求连接数大于最大容纳数,则多出的连接请求被拒绝。队列的默认大小是50。
ServerSocket类的构造方法有如下几种:
ServerSocket类常用的方法如下表所示:
方法 | 返回值 | 说明 |
---|---|---|
accept() | Socket | 等待客户机的连接。若连接,则创建一个套接字 |
isBound() | boolean | 判断ServerSocket的绑定状态 |
getInetAddress | InetAddress | 返回此服务器套接字的本地地址 |
isClosed() | boolean | 返回服务器套接字的关闭状态 |
close() | void | 关闭服务器套接字 |
bind(SocketAddress endpoint | void | 将ServerSocket绑定到特定地址(IP地址和端口号) |
getInetAddress | int | 返回服务器套接字等待的端口号 |
调用ServerSocket类的accept()方法,会返回一个和客户端Socket对象相连接的Socket对象。服务器端的Socket对象使用getOutputStream()方法获得的输出流,将指向客户端Socket对象使用getInputStream()方法获得的那个输出流。也就是说,当服务器向输出流写入信息时,客户端通过相应的输入流就能读取,反之亦然。
accept()方法会阻滞线程的继续执行,直到接收到客户的呼叫。如果没有客户呼叫服务器,那么System.out.println(“连接中”)语句将不会执行。
yu = server.accept();
System.out.println("连接中")
明白了TCP程序工作的过程,就可以编写TCP服务器程序了。在网络编程中如果只要求客户机向服务器发送消息,不要求服务器向客户机发送消息,称为单向通信。客户机套接字和服务器套接字连接成功后,客户机通过输出流发送数据,服务器则通过输入流接收数据。下面是简单的单向通信的实例。
TCP服务器程序:
import java.io.*;
import java.net.*;
public class MyTcp{
private BufferedReader reader;
private ServerSocket server;
private Socket socket;
void getserver(){
try{
server = new ServerSocket(8998);
System.out.println("服务器套接字已经创建成功")
while(true){
System.out.println("等待客户机的连接");
socket = server.accept();
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
getClientMessage();
}
}catch(Exception e){
e.printStackTrace();
}
}
private void getClientMessage(){
try{
while(true){
System.out.println("客户机:"+reader.readLine();)
}
}catch(Exception e){
e.printStackTrace();
}
try{
if(reader != null){
reader.close()
}
if(reader != null){
socket.close()
}
}catch(IOException e){
e.printStackTrace();
}
}
public static void main(String[] args){
MyTcp tcp = new MyTcp();
tcp.getserver();
}
}
客户端程序:
package com.lzw;
public class MyClien extends JFrame{
private PrintWriter writer;
Socket socket;
private JTextArea ta = new JTextArea();
private JTextField tf = new JTextField();
Container cc;
public MyClien(String title){
super(title); //调用父类的构造方法
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
cc = this.getContentPane();
final JScrollPane scrollPane = new JScrollPane();
scrollPane.setBorder(new BevelBorder(BevelBorder.RAISED));
getContentPane().add(scrollPane, BorderLayout.CENTER);
scrollPane.setViewportView(ta);
cc.add(tf,"South"); //将文本框放在窗体的下部
tf.addActionListener(new ActionLisener(){
//绑定事件
public void actionPerformed(ActionEvent e){
writer.println(tf.getText()); //将文本框中的信息写入流
ta.append(tf.getText() + '\n'); //将文本框中的信息显示在文本域
ta.setSelectionEnd(ta.getText().length());
tf.setText(""); //将文本框清空
}
});
}
private void connect(){
ta.append("尝试连接\n");
try{
socket = new Socket("127.0.0.1", 8998);
writer = new PrintWriter(socket.getOutputStream(),true);
ta.append("完成连接\n")
}catch(Exception e){
e.printStackTrace();
}
}
public static void main(String[] args){
MyClien = new MyClien("向服务器送数据");
clien.setSize(200,200); //设置窗体大小
clien.setVisible(true); //将窗体显示
clien.connect(); //调用连接方法
}
}