基于TCP协议的网络编程学习笔记(1)

原创 2015年11月18日 10:09:35

TCP/IP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket ,从而在通信的两端之间形成网络虚拟链路。一旦建立了虚拟的网络链路,两端的程序就可以通过虚拟链路进行通信。 JAVA对基于TCP/IP协议的网络通信提供了良好的封装。java使用Socket对象来代表两端的通信端口,并通过Socket产生IO流来进行网络通信.
(一)阻塞式的Scoket通信
(1)使用ServletSocket创建TCP服务器
  TCP通信的两个实体之间并没有服务器,客户端之分,但那是两个通信实体已经建立虚拟链路之后。。在两个通信实体没有建立虚拟链路之前,必须有一个通信实体先做出“主动姿态”,主动接收来自其它通信实体的连接请求.
  Java中能接收其它通信实体连接请求的类是ServerSocket,ServerSocket对象用于监听来自客户端的Socket连接,如果没有连接,它将一直处于等待状态。
  Socket accept():如果接收到一个客户端的 Socket连接请求,该方法将返回一个与客户端Socket对应的Socket,否则该方法一直处于等待状态,线程也被阻塞。
为了创建ServerSocket对象,ServerSocket类提供了如下几个构造器
ServerSocket(int port)
ServerSocket(int port,int backlog)
ServerSocket(int port,int backlog,InetAddress localAddr)
  当ServerSocket使用完毕,应使用ServerSocket的close()方法来关闭该ServerSocket。通常情况下,服务器不应该只接爱一个客户端请求,而应该不断地接受来自客户端的所有请求,所以java程序通常会通过循环,不断地调用ServerSocket的accept()方法。
如下代码断

 //创建一个ServerSocket,用于监听客户端Socket的连接请求
  ServerSocket ss = new ServerSocket(30000);
  //采用循环不断接受来自客户端的请求
  while (true)
  {
   //每当接受到客户端Socket的请求,服务器端也对应产生一个Socket
   Socket s = ss.accept();
   //将Socket对应的输出流包装成PrintStream
   PrintStream ps = new PrintStream(s.getOutputStream());
   //进行普通IO操作
   ps.println("欢迎您跟随阿堂(网络时空)一起来学习Scoket的相关知识");
   //关闭输出流,关闭Socket
   ps.close();
   s.close();
  }

(2)使用Socket进行通信
客户端通常使用Socket的构造器来连接到指定服务器,Socket通常可使用如下两个构造器

Socket(InetAddress/String remoteAddress,int port)
Socket(InetAddress/String remoteAddress,int port,InetAddress localAddr,int localPort)

Socket socket = new Socket("127.0.0.1" , 30000);

// 下面就可以使用Socket进行通信了
……
  当程序执行上面代码时,该代码将连接到指定ip地址和端口的服务器,让服务器的ServerSocket的accept()方法向下执行,于是服务器和客户端就产生一对互相连接的Socket了
  当客户端,服务器产生了对应的Socket之后,这时候程序就无须区分服务器,客户端了,而是通过各自的Socket进行通信,Socket提供了如下两个方法来获取输入流和输出流
InputStream getInputStream():返回该Socket对象对应的输入流,让程序通过该输入流从Socket中取出数据.
OutputStream getOutputStream():返回该Socket对象对应的输出流,让程序通过该输出流向Socket中输出数据.
如:

import java.net.*;
import java.io.*;
public class Server
{
 public static void main(String[] args)
  throws IOException
 {
  //创建一个ServerSocket,用于监听客户端Socket的连接请求
  ServerSocket ss = new ServerSocket(30000);
  //采用循环不断接受来自客户端的请求
  while (true)
  {
   //每当接受到客户端Socket的请求,服务器端也对应产生一个Socket
   Socket s = ss.accept();
   //将Socket对应的输出流包装成PrintStream
   PrintStream ps = new PrintStream(s.getOutputStream());
   //进行普通IO操作
   ps.println("服务器向你发送信息啦!");
   //关闭输出流,关闭Socket
   ps.close();
   s.close();
  }
 }
}

(3)加入多线程
  当我们使用传统的BufferReader的readLine()方法读取数据时,当该方法成功返回之前,线程被阻塞,程序无法继续执行。考虑到这个原因,因此服务器应该为每个Socket单独启动一条线程,每条线程与一个客户端进行通信.
  同样,客户端读取服务器数据时,线程也会被阻塞,所以系统也应该为客户端单独启动一条线程,该客户端线程专门负责读取服务器数据.
  下面的实现一个命令行的C/S聊天室应用,服务器应该包含多条线程,每个Socket对应一条线程,该线程负责读取Socket对应输入流的数据(从客户端发送过来的数据),并将读到的数据向每个 Socket输出流发送一遍(将每一个客户端的数据”广播”给其它客户端),因此需要在服务器端使用List来保存所有的Socket。
服务器端提供了两个类:一个是创建 ServerSocket监听的主类,一个是负责处理每个Scoket通信的线程类
如下所示

import java.net.*;
import java.io.*;
import java.util.*;
public class MyServer
{
 //定义保存所有Socket的ArrayList
 public static ArrayList<Socket> socketList = new ArrayList<Socket>();
    public static void main(String[] args)
  throws IOException
    {
        ServerSocket ss = new ServerSocket(30000);
  while(true)
  {
   //此行代码会阻塞,将一直等待别人的连接
   Socket s = ss.accept();
   socketList.add(s);
   //每当客户端连接后启动一条ServerThread线程为该客户端服务
   new Thread(new ServerThread(s)).start();
  }
    }
}
import java.io.*;
import java.net.*;
import java.util.*;
//负责处理每个线程通信的线程类
public class ServerThread implements Runnable
{
 //定义当前线程所处理的Socket
 Socket so = null;
 //该线程所处理的Socket所对应的输入流
 BufferedReader br = null;
 public ServerThread(Socket s)
  throws IOException
 {
  this.so = s;
  //初始化该Socket对应的输入流
  br = new BufferedReader(new InputStreamReader(so.getInputStream()));
 }
 public void run()
 {
  System.out.println("----Client :" + s.getPort() + " connected-----");
        while (true) {
            String line = null;
            while ((line = readFromClient()) != null) {
                for (Socket socket : MyTCPServer.socketList) {
                    if (socket == s) {
                        continue;
                    }
                    try {
                        System.out.println("send to socket :" + s.getPort());
                        PrintStream ps = new PrintStream(socket.getOutputStream());
                        ps.println(line);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
 }
 //定义读取客户端数据的方法
 private String readFromClient()
 {
  try
  {
   return br.readLine(); 
  }
  //如果捕捉到异常,表明该Socket对应的客户端已经关闭
  catch (IOException e)
  {
   //删除该Socket。
   MyServer.socketList.remove(s);
  }
  return null;
 }
}

  客户端也同样提供了两个类:一个是负责读取键盘输入的Myclient,并将用户输入的数据写入Socket对应的输出流中,也就是客户端程序的主线程类,一个是负责读取Socket对应输入流中的数据(从服务器发送过来的数据).

import java.net.*;
import java.io.*;

public class MyClient
{
    public static void main(String[] args)
  throws IOException
    {
  Socket s = s = new Socket("127.0.0.1" , 30000);
  //客户端启动ClientThread线程不断读取来自服务器的数据
  new Thread(new ClientThread(s)).start();
  //获取该Socket对应的输出流
  PrintStream ps = new PrintStream(s.getOutputStream());
 System.out.println("input:");
            Scanner scanner = new Scanner(System.in);
            while (true) {
//              System.out.println(scanner.next());
                ps.println(s.getLocalPort()+":"+scanner.next());
            }
    }
}
import java.io.*;
import java.net.*;
import java.util.*;

public class ClientThread implements Runnable
{
 //该线程负责处理的Socket
 private Socket s;
 //该现成所处理的Socket所对应的输入流
 BufferedReader br = null;
 public ClientThread(Socket s)
  throws IOException
 {
  this.s = s;
  br = new BufferedReader(
   new InputStreamReader(s.getInputStream()));
 }
 public void run()
 {
  System.out.println("----Client : "
                + s.getLocalPort() + " start-----");
        try {
            while (true) {
                String line = null;
                while ((line = br.readLine()) != null) {
                    String[] split = line.split(":");
                    String src = split[0];
                    String content = split[1];
                    System.out.println("receive from " + src + ":" + content);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
 }
}
版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

网络编程学习笔记(三)TCP协议及客户端与服务端交互Demo

TCP的一些基本概念TCP(Transmission Control Protocol 传输控制协议)是一种面向连接的、可靠的、基于字节流(当应用层向TCP层发送用于网间传输的、用8位字节表示的数据流...

19. JAVA 网络编程 Part 1 (IP与InetAddress类、URL与URLConnection类、URLEncoder与URLDecoder类、TCP&UDP)---- 学习笔记

本章目标: 了解IP地址与InetAddress类的关系了解如何使用URL

Socket网络编程学习笔记(1)

  • 2009年06月22日 22:47
  • 32KB
  • 下载

Windows网络编程学习笔记(5) TCP服务端向客户端发送Hello World!

Windows下Winsock TCP/IP 网络编程。 本章将详解send()/WSASend() 、 recv()/WSARecv() 和 函数,然后你就可以编写一个可运行的通讯程序了。 程序包括...
  • Raito__
  • Raito__
  • 2016年05月14日 22:55
  • 1959

JavaSE学习笔记--网络编程之TCP

---------------------- http://edu.csdn.net/heima" target="blank">android培训、http://edu.csdn.net/heima...

TCP-IP学习笔记二:NIO的网络编程Buffer简单使用

TCP/IP学习笔记二:NIO的网络编程Buffer简单使用标签(空格分隔):网络编程 NIO BufferNIO的有三种模型:ByteBuffer (position/limit/capacity)...
  • MOTUI
  • MOTUI
  • 2016年10月11日 21:35
  • 453

网络编程(UDP/TCP)+JAVA学习笔记-DAY26

网络编程概述、网络参考模型、网络编程三要素、Socket通信原理图解、TCP协议...

黑马程序员________Java中UDP和TCP网络编程学习笔记

------- android培训、java培训、期待与您交流! ----------                                          网络通信...

Linux网络编程--TCP的套接字通信学习笔记

TCP关于套接字socket通信涉及服务端和客户端两端的程序编写,如下: A.  TCP_server端; 1. 创建套接字socket 2.设置服务端ip并与socket进行绑定 3.设置服...

TCP/IP网络编程 学习笔记_15 --多播与广播

前言:想想这么一种情况,网络电台可能需要同时向成千上万的用户传输相同的数据,如果用我们以前讲过的传输形式,每个用户都传输一次,这样肯定是不合理的。因此,就引入了多播技术来解决这个问题,它可以同时向大量...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:基于TCP协议的网络编程学习笔记(1)
举报原因:
原因补充:

(最多只允许输入30个字)