行くコア開発の研究ノート(30) - TCP / IP、ネットワークプログラミング

TCP / IPの知識は、おおよそのポイントをまとめるスキップ

  1. アプリケーション層、トランスポート層、ネットワーク層、ネットワークインタフェース層:OSI 7層モデル理論は、その4層構造TCP / IPモデルを理解することを理解。
  2. TCP / IPプロトコルは、プロトコルの多くが含まれ、TCPとIPは、最も代表的なの間で2つだけです。
  3. IMの代表は、TCPソケットをプログラミングに属し、B / Sの構造:C / Sの構造は、代表モールJD、HTTP、TCPに属するプログラムは、HTTP / IPプロトコルの一部です。
  4. ネットワークセクションについて、あなたはより良いフォローアップ開発するために、部門がそれらを繰り返すことはしません、TCP / IPを深く理解する必要があります。
  5. ポートあまり開いて、リスクの複数のコピーを開くには、サーバが少なく開いているポートを開くことができ、システムは純粋なバージョンである必要があります。シングル・ポートは単一のプログラムに対応し、再利用することはできません。

TCPソケットは、クライアント側とサーバー側のプログラミング

  1. マルチクライアントは、一般的に少数のサーバーに対応します。
  2. サーバの処理フロー:
    リスニングポート(リスニング);
    クライアントとサーバー側のリンクを確立するために、クライアントのTCP要求を受信するステップと、
    (要求パケットを送信することにより、クライアント)リンク要求を処理し、ゴルーチンを作成します。
  3. プロセスフロークライアント:
    リンク確立とサービス側(ポートリンクサーバーランダムに定義されたポート上でリスニング);
    送信要求データ、受信サーバーは、データの結果を返し、
    リソース消費を占め近いリンク(TCP、あなたがメモリを防ぐためにシャットダウンを延期したいファイルを開くに類似リーク);

情報を送受信するためのC / Sモデル、入力端子、出力端子を書き込む実際、クライアントおよびサーバ

サーバー側の分析とコメント:

package main

import (
	"fmt"
	"net"
)

func recv(conn net.Conn) {
	//循环接收客户端发送来的请求
	defer conn.Close()    //如果不关闭服务器因为连接没有释放,后续客户端无法登陆了
	for {
		//创建一个新的切片
		buf := make([]byte,1024)
		//fmt.Printf("服务器在等待客户端%v发送信息\n",conn.RemoteAddr().String())
		n, err := conn.Read(buf)  //如果客户端conn不发信息,没有write操作,会一直阻塞,优化做一个timeout
		if err != nil {
			return
		}
		//显示信息到服务器终端,buf[:n]是真正读到的信息,否则切片后一大串东西都会出来,很乱套
		fmt.Print(string(buf[:n]))
	}
}

func main() {
	/*
	需求分析:
	1. 服务端: 监听端口6666,要为多个客户端提供服务;
	   不能阻塞,所以每个客户端请求都用一个goroutine提供服务;
		MPG模型,P调度处一个goroutine为客户端x提供服务,实际上是不同的G在为不同的client提供服务;
		全部开协程,就变成了并发的了,同时进行请求响应。
	2. 客户端: 端口随机,通过Socket发送请求指定到服务器端6666端口
	 */

	/*
	服务器端代码编写: package net中
	大部分使用者只需要Dial、Listen和Accept函数提供的基本接口,以及相关的Conn和Listener接口。
	crypto/tls包提供了相同的接口和类似的Dial和Listen函数。

	Dial函数和服务端建立连接,Listen函数创建的服务端。
	 */
	listener, err := net.Listen("tcp", "0.0.0.0:6666")   //返回listener为接口类型资源
	if err != nil {
		fmt.Println("连接错误,程序退出")  //监听都错误,后面不用玩了
		return
	}
	defer listener.Close()   //类似文件,及时关闭
	//监听成功,获得ln为&{0xc00008e000}
	fmt.Printf("监听成功,获得lnr为%v\n",listener) //相当于监听成功就跑路了,所以需要for循环。
	for {
		//关于conn接口类型资源拥有的方法:
		/*
		// Read从连接中读取数据
		   // Read方法可能会在超过某个固定时间限制后超时返回错误,该错误的Timeout()方法返回真
		   Read(b []byte) (n int, err error)
		   // Write从连接中写入数据
		   // Write方法可能会在超过某个固定时间限制后超时返回错误,该错误的Timeout()方法返回真
		   Write(b []byte) (n int, err error)
		   // Close方法关闭该连接
		   // 并会导致任何阻塞中的Read或Write方法不再阻塞并返回错误
		   Close() error
		   // 返回本地网络地址
		   LocalAddr() Addr
		   // 返回远端网络地址
		   RemoteAddr() Addr
		 */
		conn, err := listener.Accept()  // Accept等待并返回下一个连接到该接口的连接, conn为接口类型实例,通俗点,通信连接线对象
		if err != nil {
			continue    //这里出错了不要使用return或者break,因为并发连接千万,不能因为一个就终止服务器监听
		} else {
			fmt.Printf("Accept() successed conn = %v\n",conn)
			fmt.Printf("客户端IP= %v\n",conn.RemoteAddr().String())   // 返回远端网络地址

		}
		
		//起一个协程为连接进来的客户端提供一对一服务,监听是通过服务器主程序
		// 端口仍然是同一个,类似前台,但是提供读写等,为客户端服务的是协程
		go recv(conn)
	}
}

クライアントクライアントと注釈解析:

package main

import (
	"bufio"
	"fmt"
	"net"
	"os"
	"strings"
)

func main() {
	//客户端使用net.Dial()来完成,无需要监听,只需要conn即可
	conn, err := net.Dial("tcp","127.0.0.1:6666")
	if err != nil {
		fmt.Println("连接错误,错误原因为:",err,"请重新连接")
		return
	} else {
		fmt.Println("conn is successfully connect~",conn)
	}

	reader := bufio.NewReader(os.Stdin)

	for {
		ln, err := reader.ReadString('\n')
		if err != nil {
			fmt.Println("Reading String Error!")
		}


		ln = strings.Trim(ln,"\r\n")
		if ln == "exit" {
			fmt.Println("客户端由于exit命令退出")
			break
		}

		_, err = conn.Write([]byte(ln + "\n"))
		if err != nil {
			fmt.Println("Writing error!",err)
		}
	}
}
公開された49元の記事 ウォン称賛18 ビュー3995

おすすめ

転載: blog.csdn.net/weixin_41047549/article/details/90677920