Sockey编程之基于 TCP 协议的 Socket 编程

1.TCP 与 UDP 协议都在套接字编程中的区别

 (1)UDP

    数据报套接字

    只需一个接收函数 recvfrom()和一个发送函数 sendto()

 (2)TCP

     流式套接字

     服务器监听函数 listen()

     客户端连接函数 connect()

     服务器接收函数 accept()

     发送函数 send()和接收函数 recv()

2.原理

(1)服务器端

(1)创建ServerSocket对象,绑定监听端口;

(2) 通过accept()方法监听客户端请求;

(3) 连接建立后,通过输入流读取客户端发送的请求信息;

(4) 通过输出流向客户端发送相应信息;

(5) 关闭响应资源

(2)客户端

(1) 创建Socket对象,指明需要连接的服务器地址和端口;
(2) 连接建立后,通过输出流向服务器端发送请求信息;
(3) 通过输入流获取服务器端返回的响应信息;
(4) 关闭响应资源。

3.代码编写

(1)客户端

1.首先初始化Java的JFrame()窗口形成图形化的界面;

public static void init(){
		JFrame frame = new JFrame("TCP客户端");
		frame.setBounds(100, 100, 753, 500);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.getContentPane().setLayout(null);
		
		JLabel lblip = new JLabel("我的名字:");
		lblip.setBounds(56, 72, 93, 24);
		frame.getContentPane().add(lblip);
		
		JLabel label = new JLabel("服务器IP:");
		label.setBounds(56, 24, 93, 24);
		frame.getContentPane().add(label);
		
		JLabel label_1 = new JLabel("端口号:");
		label_1.setBounds(486, 24, 70, 24);
		frame.getContentPane().add(label_1);
		
		text_ip = new JTextField("127.0.0.1");
		text_ip.setBounds(139, 24, 232, 30);
		frame.getContentPane().add(text_ip);
		text_ip.setColumns(10);
		
		text_name = new JTextField();
		text_name.setColumns(10);
		text_name.setBounds(139, 69, 232, 30);
		frame.getContentPane().add(text_name);
		
		text_port = new JTextField();
		text_port.setColumns(10);
		text_port.setBounds(551, 21, 106, 30);
		frame.getContentPane().add(text_port);
 
		JButton button_login = new JButton("连接服务器");
		button_login.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) { 
				try {
					int port=Integer.parseInt(text_port.getText());
					System.out.println("连接服务器!");
					sc=new Socket("localhost",port);              //绑定服务端地址与端口号
					System.out.println("连接服务器成功!");
					
    				new Thread(new Runnable(){
						public void run(){
							try{
								inputStreamReader=new InputStreamReader(sc.getInputStream());//提高效率,将自己字节流转为字符流
								bufferedReader=new BufferedReader(inputStreamReader);//加入缓冲区 
								 String info=null;
								  while(true){
									  info=bufferedReader.readLine();			          
									  System.out.println("接收到信息为:"+info);
						   	          textArea.append(info+"\n"); 
								  } 
								}
							catch(IOException e){
								textArea.append("服务器断开\n");
							}
						}
					}).start();

				} catch (IOException e) { 
					e.printStackTrace();
				}
			}
		});
		button_login.setBounds(496, 67, 161, 35);
		frame.getContentPane().add(button_login);
 
		textArea= new JTextArea(10,15); 
		textArea.setLineWrap(true);
		textArea.setBounds(66, 119, 588, 232);
//		JScrollPane jsp = new JScrollPane(textArea);//给文本区添加滚动条
		frame.getContentPane().add(textArea);
		
		JTextField text_send = new JTextField();
		text_send.setBounds(67, 379, 419, 40);
		frame.getContentPane().add(text_send);
		text_send.setColumns(10);
		
		JButton button_send = new JButton("发送");
		button_send.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent arg0) {
			String message=text_name.getText()+":"+text_send.getText()+"\n"+"END";
			String mes=text_name.getText()+":"+text_send.getText()+"\n";			
			textArea.append(mes); 
			send(message);
			text_send.setText("");
			}
		});
		button_send.setBounds(542, 379, 115, 40);
		frame.getContentPane().add(button_send);
		frame.setVisible(true);
	}

2.连接绑定服务器,连接服务器的9999端口,用socket(“localhost”,port)进行建立与服务器9999端口的套接字连接;

int port=Integer.parseInt(text_port.getText());
System.out.println("连接服务器!");
sc=new Socket("localhost",port);              //绑定服务端地址与端口号
System.out.println("连接服务器成功!");

3.当连接到服务器时打开线程Thread()读取服务器发过来的数据,用InputStreamReader(sc.getInputStream)将字节流转换为字符流并且将字符流存入到BufferedReader缓冲区中,while(true)进行循环不断的接收服务器发送过来的信息bufferReader.readLine()去一行一行的获取数据并且将数据显示在文本框中

new Thread(new Runnable(){
public void run(){
try{
inputStreamReader=new InputStreamReader(sc.getInputStream());//提高效率,将自己字节流转为字符流
bufferedReader=new BufferedReader(inputStreamReader);//加入缓冲区 
String info=null;
while(true){
info=bufferedReader.readLine();			          
System.out.println("接收到信息为:"+info);
textArea.append(info+"\n"); 
} 
}
catch(IOException e){
textArea.append("服务器断开\n");
}
}
}).start();

4.当点击发送时,在发送按钮上用addActionListener监听事件,当点击发送时,获取文本框中的信息和发送人的姓名;并且在自己的文本框中显示,调用send()函数进行信息的发送,当发送成功后将发送信息框设置为空;

button_send.addActionListener(new ActionListener() {
	public void actionPerformed(ActionEvent arg0) {
	String message=text_name.getText()+":"+text_send.getText()+"\n"+"END";
	String mes=text_name.getText()+":"+text_send.getText()+"\n";			
textArea.append(mes); 
	send(message);
	text_send.setText("");
}
});

5.发送信息的函数,send()函数进行信息的发送PrintWriter获取写数据getOutputStream()去获取输出数据流将消息打印输出并且用pw.flush()缓存发送出去;

	public static void send(String mesg){
	PrintWriter pw;
	try {
		pw = new PrintWriter(sc.getOutputStream());
		pw.println(mesg);
		pw.flush();
	} catch (IOException e) {
		System.out.println("客户端发送信息失败!");
		e.printStackTrace();
	}
	}

ServerThread类

多线程类的实现处理客户端发送来的数据并且对客户端的返回数据信息

1.首先初始化这个线程类初始化并且接收传递过来的sc(socket连接信息),cs(连接本服务器的客户端的列表); 

public ServerThread(Socket sc,Vector<Socket> cs){
		this.sc=sc;
		this.cs=cs;	}

2.获取输入流,用sc.InputStream()获取到输入的字节流将字节流转换为字符流InputStreamReader,并且将字符流放入到缓冲区BufferReader当中,用while循环一直等待输入端的输入缓冲区并且用readLine()函数去读这个缓冲区的数据当读到结束符“END”时停止;将信息放在info中存储

is=sc.getInputStream();          //获取输入的字节流
isr=new InputStreamReader(is);   //转换为字符流
br=new BufferedReader(isr);      //放入到缓冲区
String info=null; 
String temp=null; 		
while (!"END".equals(temp = br.readLine())) {
info=temp;
System.out.println("info:"+info);
	   System.out.println("已接收到客户端连接!");
	   System.out.println("服务端收到客户端信息:"+info+"    当前客户端的IP为:"+sc.getInetAddress().getHostAddress()+"   端口号为:"+sc.getPort());  
		}
	sc.shutdownInput();

3.输出流转发信息到所有的客户端系统,对客户端列表的所有客户端进行遍历,发送给不是本客户端端口的所有其他客户端,用PrintWriter去输出数据获取客户端的输出数据流soc.getOutputStream(),打印Info信息给输出端调用flush()方法将缓冲输出

for(Socket soc : cs){                         //发送给已连接的所有客户端
	if(!this.sc.equals(soc)){
		pw=new PrintWriter(soc.getOutputStream());
		pw.println(info);
		pw.flush();    //调用flush()方法将缓冲输出
		System.out.println("发送成功,发送给"+soc.getPort()+"   "+info);
	}
	}
	} catch (Exception e) {
		System.out.println("线程已关闭!"); 
	}

(2)服务器端

1.对服务器进行连接,对按钮增加监听,当按下按键时连接端口,对服务器进行连接,绑定地址和端口号,主机采用本地服务器127.0.0.1进行连接这里绑定9999端口作为服务器端口号ServerSocket ssc=new ServerSocket(9999);  创建一个服务器9999端口接收信息

2.监听服务器端口一直监听是否有连接到9999端口的客户端accept()方法进行监听,当没有客户端连接时,会一直处于阻塞的状态;当存在一个客户端的连接时,将客户端加入到 之前初始化的static public Vector<Socket> cs=new Vector<Socket>();序列中count++记录当前连接的客户端的数量打印出当前客户端的IP号和端口号

	while(true){
		sc=ssc.accept();   //accept()方法是用于监听接收客户端的连接
		//创建新的线程
	cs.add(sc);
	ServerThread st=new ServerThread(sc,cs);	//启动线程
	st.start();
	count++;   //统计当前连接的客户端的数量
	System.out.println("客户端数量:"+count);
	InetAddress addr=sc.getInetAddress();
	System.out.println("当前客户端的IP:"+addr.getHostAddress()+"  端口号:"+sc.getPort());
	}
} catch (IOException e) {
	System.out.println("TCP服务器启动失败!");
	e.printStackTrace();
}

4.实验结果

(1)客户端进行连接服务器,输入端口号9999,点击连接服务器按键,可以看到控制台显示客户端数量与客户端的IP地址和端口号;以下也有客户端的显示界面如下所示:

                                                  

(2)当有多个客户端进行连接的时候,服务端统计全部的客户端连接信息;并且初始化客户端的显示页面,在这里我们连接三个客户端;

3.现在开始进行信息的发送,可以看到其他客户端可以收到信息

                                                         

4.其他客户端进行相应,并作出回复消息,可以看到基于tcp的多用户线程通信成功实现;

                                                          

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值