网络编程+go+java_GO语言的进阶之路-网络编程之socket

GO语言的进阶之路-网络编程之socket

作者:尹正杰

版权声明:原创作品,谢绝转载!否则将追究法律责任。

一.什么是socket;

在说socket之前,我们要对两个概念要有所了解,就是IP和端口。

1.什么是IP;

IP地址是我们进行TCP/IP通讯的基础,每个链接到网络的计算机都必须有一个IP地址。在这里我不打算给大家说IPV4和IPV6,也不打算说主机位和网络位。

我们可以简单的理解,在局域网中,IP就是用来标识主机的。(大家不要钻牛角尖说NAT这种情况,我们在这里是忽略的。)

2.什么是端口;

如果说IP是用来标识主机的,那么端口就是用来标识这台主机的所有服务。

这样我们就方便理解了,端口是用来标识服务的,只要主机的服务启动,然后我们访问主机的对应端口就能被提供服务。欢聚话说,局域网中如果别人知道你IP和端口,就能访问到的你的服务了(前提下是别人的主机和你的主机是要互通的。)

3.什么是socket;

网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket。

建立网络通信连接至少要一对端口号(socket)。socket本质是编程接口(API),对TCP/IP的封装,TCP/IP也要提供可供程序员做网络开发所用的接口,这就是Socket编程接口;HTTP是轿车,提供了封装或者显示数据的具体形式;Socket是发动机,提供了网络通信的能力。

Socket的英文原义是“孔”或“插座”。作为BSD UNIX的进程通信机制,取后一种意思。通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,可以用来实现不同虚拟机或不同计算机之间的通信。每种服务都打开一个Socket,并绑定到一个端口上,不同的端口对应于不同的服务。Socket正如其英文原意那样,像一个多孔插座。插座是用来给插头提供一个接口让其通电的,此时我们就可以将插座当做一个服务端,不同的插头当做客户端。

二.客户端Socket;

1.串行指定读取客户端返回内容大小;(这种方法我不推荐使用!我推荐使用的是后三种方式读取。请看下面的备注。)

1 /*

2 #!/usr/bin/env gorun

3 @author :yinzhengjie4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/

5 EMAIL:y1053419035@qq.com6 */

7

8 package main9

10 import(11 "net"

12 "log"

13 "fmt"

14 "reflect"

15 "io"

16 )17

18 func main() {19 addr := "wwww.baidu.com:80" //定义主机名20 conn,err := net.Dial("tcp",addr) //拨号操作,需要指定协议。21 if err !=nil {22 log.Fatal(err)23 }24 fmt.Println("访问公网IP地址是:",conn.RemoteAddr().String()) /*获取“conn”中的公网地址。注意:最好是加上后面的String方法,因为他们的那些是不一样的哟·当然你打印的时候25 可以不加输出结果是一样的,但是你的内心是不一样的哟!*/

26 fmt.Printf("客户端链接的地址及端口是:%v\n",conn.LocalAddr()) //获取到本地的访问地址和端口。27 fmt.Println("“conn.LocalAddr()”所对应的数据类型是:",reflect.TypeOf(conn.LocalAddr()))28 fmt.Println("“conn.RemoteAddr().String()”所对应的数据类型是:",reflect.TypeOf(conn.RemoteAddr().String()))29 n,err := conn.Write([]byte("GET / HTTP/1.1\r\n\r\n")) //向服务端发送数据。用n接受返回的数据大小,用err接受错误信息。30 if err !=nil {31 log.Fatal(err)32 }33 fmt.Println("向服务端发送的数据大小是:",n)34

35 buf := make([]byte,1024) //定义一个切片的长度是1024。36

37 n,err = conn.Read(buf) //接收到的内容大小。38

39 if err != nil && err != io.EOF { //io.EOF在网络编程中表示对端把链接关闭了。40 log.Fatal(err)41 }42 fmt.Println(string(buf[:n])) //将接受的内容都读取出来。43 conn.Close() //断开TCP链接。44 }45

46

47

48 #以上代码输出结果如下:

49 访问公网IP地址是: 111.13.101.208:80

50 客户端链接的地址及端口是:172.16.3.210:49568

51 “conn.LocalAddr()”所对应的数据类型是: *net.TCPAddr52 “conn.RemoteAddr().String()”所对应的数据类型是: string53 向服务端发送的数据大小是: 18

54 HTTP/1.1 400Bad Request55 Date: Mon, 31 Jul 2017 06:22:41GMT56 Server: Apache57 Content-Length: 226

58 Connection: Keep-Alive59 Content-Type: text/html; charset=iso-8859-1

备注:io.Copy不会主动调用close,io.Copy结束的条件是reader得到EOF。感兴趣的可以看下源码。

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 func copyBuffer(dst Writer, src Reader, buf []byte) (written int64, err error) {2 //If the reader has a WriteTo method, use it to do the copy.3 // Avoids an allocation anda copy.4 if wt, ok :=src.(WriterTo); ok {5 returnwt.WriteTo(dst)6 }7 // Similarly, ifthe writer has a ReadFrom method, use it to do the copy.8 if rt, ok :=dst.(ReaderFrom); ok {9 returnrt.ReadFrom(src)10 }11 if buf ==nil {12 buf = make([]byte, 32*1024)13 }14 for{15 nr, er :=src.Read(buf)16 if nr >0 {17 nw, ew :=dst.Write(buf[0:nr])18 if nw >0 {19 written +=int64(nw)20 }21 if ew !=nil {22 err =ew23 break

24 }25 if nr !=nw {26 err =ErrShortWrite27 break

28 }29 }30 if er !=nil {31 if er !=EOF {32 err =er33 }34 break

35 }36 }37 returnwritten, err38 }

猛戳我可以看io.Copy的实现方式。

2.按照指定大小循环读取;

1 /*

2 #!/usr/bin/env gorun

3 @author :yinzhengjie4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/

5 EMAIL:y1053419035@qq.com6 */

7

8 package main9

10 import(11 "net"

12 "log"

13 "fmt"

14 "reflect"

15 "io"

16 )17

18 func main() {19 addr := "wwww.baidu.com:80" //定义主机名20 conn,err := net.Dial("tcp",addr) //拨号操作,需要指定协议。21 if err !=nil {22 log.Fatal(err)23 }24 fmt.Println(conn.RemoteAddr().String()) //最好是加上后面的String方法,因为他们的那些是不一样的哟·当然你打印的时候可以不加。25 fmt.Println(conn.LocalAddr())26 fmt.Println(reflect.TypeOf(conn.LocalAddr()))27 fmt.Println(reflect.TypeOf(conn.RemoteAddr().String()))28 n,err := conn.Write([]byte("GET / HTTP/1.1\r\n\r\n")) //向服务端发送数据。用n接受返回的数据大小,用err接受错误信息。29 if err !=nil {30 log.Fatal(err)31 }32 fmt.Println("写入的大小是:",n)33

34 buf := make([]byte,10) //定义一个切片的长度是1024。35

36 for{37 n,err = conn.Read(buf) //接收到的内容大小。38 if err ==io.EOF {39 conn.Close()40 }41 fmt.Print(string(buf[:n]))42 }43

44 fmt.Println(string(buf[:n])) //将接受的内容都读取出来。45

46 }47

48

49

50

51 #以上代码输出结果如下:

52 111.13.101.208:80

53 172.16.3.210:63838

54 *net.TCPAddr55 string56 写入的大小是: 18

57 HTTP/1.1 400Bad Request58 Date: Mon, 31 Jul 2017 10:38:42GMT59 Server: Apache60 Content-Length: 226

61 Connection: Keep-Alive62 Content-Type: text/html; charset=iso-8859-1

63

64

65

66

400 Bad Request

67

68

Bad Request

69

Your browser sent a request that this server could not understand.

70

71

3.按行读取;

1 /*

2 #!/usr/bin/env gorun

3 @author :yinzhengjie4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/

5 EMAIL:y1053419035@qq.com6 */

7

8 package main9

10 import(11 "net"

12 "log"

13 "fmt"

14 "reflect"

15 "io"

16 "bufio"

17 )18

19 func main() {20 addr := "wwww.baidu.com:80" //定义主机名21 conn,err := net.Dial("tcp",addr) //拨号操作,需要指定协议。22 if err !=nil {23 log.Fatal(err)24 }25 fmt.Println(conn.RemoteAddr().String()) //最好是加上后面的String方法,因为他们的那些是不一样的哟·当然你打印的时候可以不加。26 fmt.Println(conn.LocalAddr())27 fmt.Println(reflect.TypeOf(conn.LocalAddr()))28 fmt.Println(reflect.TypeOf(conn.RemoteAddr().String()))29 n,err := conn.Write([]byte("GET / HTTP/1.1\r\n\r\n")) //向服务端发送数据。用n接受返回的数据大小,用err接受错误信息。30 if err !=nil {31 log.Fatal(err)32 }33 fmt.Println("写入的大小是:",n)34

35 r := bufio.NewReader(conn) //将这个链接(connection)包装以下。将conn的内容都放入r中,但是没有进行读取,让步我们一会对其进行操作。36 for{37 line,err := r.ReadString('\n') //将r的内容也就是conn的数据按照换行符进行读取。38 if err ==io.EOF {39 conn.Close()40 }41 fmt.Print(line)42 }43

44

45 }46

47

48

49

50 #以上代码输出结果如下:

51 111.13.101.208:80

52 172.16.3.210:64613

53 *net.TCPAddr54 string55 写入的大小是: 18

56 HTTP/1.1 400Bad Request57 Date: Mon, 31 Jul 2017 10:41:48GMT58 Server: Apache59 Content-Length: 226

60 Connection: Keep-Alive61 Content-Type: text/html; charset=iso-8859-1

62

63

64

65

400 Bad Request

66

67

Bad Request

68

Your browser sent a request that this server could not understand.

69

70

4.io读取http请求

1 /*

2 #!/usr/bin/env gorun

3 @author :yinzhengjie4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/

5 EMAIL:y1053419035@qq.com6 */

7

8

9 package main10

11 import(12 "net"

13 "log"

14 "fmt"

15 "reflect"

16 "io"

17 "os"

18 )19

20 func main() {21 addr := "wwww.baidu.com:80" //定义主机名22 conn,err := net.Dial("tcp",addr) //拨号操作,需要指定协议。23 if err !=nil {24 log.Fatal(err)25 }26 fmt.Println("访问公网IP地址以及端口是:",conn.RemoteAddr().String()) /*获取“conn”中的公网地址。注意:最好是加上后面的String方法,因为他们的那些是不一样的哟·当然你打印的时候27 可以不加输出结果是一样的,但是你的内心是不一样的哟!*/

28 fmt.Printf("客户端链接的地址及端口是:%v\n",conn.LocalAddr()) //获取到本地的访问地址和端口。29 fmt.Println("“conn.LocalAddr()”所对应的数据类型是:",reflect.TypeOf(conn.LocalAddr()))30 fmt.Println("“conn.RemoteAddr().String()”所对应的数据类型是:",reflect.TypeOf(conn.RemoteAddr().String()))31 n,err := conn.Write([]byte("GET / HTTP/1.1\r\n\r\n")) //向服务端发送数据。用n接受返回的数据大小,用err接受错误信息。32 if err !=nil {33 log.Fatal(err)34 }35 fmt.Println("写入的大小是:",n)36 io.Copy(os.Stdout,conn)37 conn.Close()38 }39

40

41

42

43 #以上代码输出结果如下:

44 访问公网IP地址以及端口是: 111.13.101.208:80

45 客户端链接的地址及端口是:172.16.3.210:52409

46 “conn.LocalAddr()”所对应的数据类型是: *net.TCPAddr47 “conn.RemoteAddr().String()”所对应的数据类型是: string48 写入的大小是: 18

49 HTTP/1.1 400Bad Request50 Date: Tue, 01 Aug 2017 02:35:11GMT51 Server: Apache52 Content-Length: 226

53 Connection: Keep-Alive54 Content-Type: text/html; charset=iso-8859-1

55

56

57

58

400 Bad Request

59

60

Bad Request

61

Your browser sent a request that this server could not understand.

62

63

三.服务端Socket;

1.串行服务端;

当客户端链接过来的时候,我们服务端可以给客户端回复特定的字符串等等。我们就以下面这段代码为例子:

1 /*

2 #!/usr/bin/env gorun

3 @author :yinzhengjie4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/

5 EMAIL:y1053419035@qq.com6 */

7

8 package main9

10 import(11 "net"

12 "log"

13 "time"

14 )15

16 func main() {17 addr := "0.0.0.0:8080" //表示监听本地所有ip的8080端口,也可以这样写:addr := ":8080"

18 listener,err := net.Listen("tcp",addr) //使用协议是tcp,监听的地址是addr19 if err !=nil {20 log.Fatal(err)21 }22 defer listener.Close() //关闭监听的端口

23 for{24 conn,err := listener.Accept() //用conn接收链接25 if err !=nil {26 log.Fatal(err)27 }28 conn.Write([]byte("Yinzhengjie\n")) //通过conn的wirte方法将这些数据返回给客户端。29 conn.Write([]byte("hello Golang\n"))30 time.Sleep(time.Minute) //在结束这个链接之前需要睡一分钟在结束当前循环。31 conn.Close() //与客户端断开连接。32 }33 }

注意,我们需要在服务端运行代码,然后在客户端进行telnet连接,如果操作的呢?很简单,我用了2台网络设备做测试:

路由器连接:

4bd3b65313032041578a4943dfd64b71.png

防火墙连接:

f228172ff076bdea66e1867f35236630.png

我用两个网络设备在1分钟内同时连接服务器,发现先连接的路由器收到了回复,然后就一直在sleep,没有下文了,这个时候防火墙也在连接,但是一直是在连接状态,也未出现断开的情况,等过了一分钟之后,分别出现了以下现象:

路由器连接:

e01ba837d796fa198f6c06d991728c5c.png

防火墙连接:

0814ca15cd7c193b63053b7fa193cb27.png

小伙伴们或许发现了规律,当路由器断开连接的时候,防火墙开始受到数据了,然后在过一分钟后防火墙也断开连接了。因为我的代码中在执行写的是睡眠一分钟后就断开连接。

一分钟后防火墙连接:

f4d9c27549facbf09ba5949eab9cefc5.png

2.并发服务端;

1 /*

2 #!/usr/bin/env gorun

3 @author :yinzhengjie4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/

5 EMAIL:y1053419035@qq.com6 */

7

8 package main9

10 import(11 "net"

12 "log"

13 "time"

14 )15

16 func Handle_conn(conn net.Conn) { //这个是在处理客户端会阻塞的代码。17 conn.Write([]byte("Yinzhengjie\n")) //通过conn的wirte方法将这些数据返回给客户端。18 conn.Write([]byte("尹正杰是一个好男孩!\n"))19 time.Sleep(time.Minute)20 conn.Close() //与客户端断开连接。21 }22

23 func main() {24 addr := "0.0.0.0:8080" //表示监听本地所有ip的8080端口,也可以这样写:addr := ":8080"

25 listener,err := net.Listen("tcp",addr)26 if err !=nil {27 log.Fatal(err)28 }29 defer listener.Close()30

31 for{32 conn,err := listener.Accept() //用conn接收链接33 if err !=nil {34 log.Fatal(err)35 }36 go Handle_conn(conn) //开启多个协程。37 }38 }

同意,我还是用路由器和防火墙做测试,发现这次效果迥然不同。

路由器连接效果:

660677ef0ae2472569b32ac2fafa2b9e.png

防火墙连接效果:

5509ce10e6d29be4e0c2ca8d09eb6822.png

我们很明显发现,当两个网络设备同事去连接服务器的时候,此次的输出结果是不一致的,2个客户端同事获得了回应,这就是服务器的并发效果,可以在同一时间处理多个链接。等待一分钟后,两个客户端都会自动断开了链接。

路由器一分钟后状态:

ec9f0bdff8023ce98d5af34bf9d3099e.png

防火墙一分钟后状态:

aa126da4f9ae0dd4ce4d74d38a4a94da.png

3.web并发服务器;

其实我们写的代码也可以让访问对象是浏览器,这个时候我们返回其特定的html标签标签即可,代码如下:

1 /*

2 #!/usr/bin/env gorun

3 @author :yinzhengjie4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/

5 EMAIL:y1053419035@qq.com6 */

7

8 package main9

10 import(11 "net"

12 "log"

13 )14

15

16 var content = `HTTP/1.1 200OK17 Date: Sat, 29 Jul 2017 06:18:23GMT18 Content-Type: text/html19 Connection: Keep-Alive20 Server: BWS/1.1

21 X-UA-Compatible: IE=Edge,chrome=1

22 BDPAGETYPE: 3

23 Set-Cookie: BDSVRTM=0; path=/

24

25

26

27

28

29

尹正杰的个人主页

30

31

32

33

尹正杰

34

hello golang

35

36

37

38 `39

40 func Handle_conn(conn net.Conn) { //这个是在处理客户端会阻塞。41 conn.Write([]byte(content)) //将html的代码返回给客户端,这样客户端在web上访问就可以拿到指定字符。42 conn.Close()43 }44

45

46 func main() {47 addr := "0.0.0.0:8080" //表示监听本地所有ip的8080端口,也可以这样写:addr := ":8080"

48 listener,err := net.Listen("tcp",addr)49 if err !=nil {50 log.Fatal(err)51 }52 defer listener.Close()53

54 for{55 conn,err := listener.Accept() //用conn接收链接56 if err !=nil {57 log.Fatal(err)58 }59 go Handle_conn(conn) //将接受来的链接交给该函数去处理。60 }61 }

客户端访问效果如下:

f3278caaddef6c5d3bfe50015568b54a.png

我们发现30s过后后页面发生了变化如下:

0a3736264ce722c347557b5c79259a21.png

通过这个案例,可能大家会想我能不能把文件的内容传给客户端呢?当然是可以的啦~写法也很简单,就直接把读内容转换成“[]byte”类型然后返回给客户端即可,在这里我就不多废话了。

四.socket链接的应用;

1.写个FTP服务器初级版本;

服务端:

1 /*

2 #!/usr/bin/env gorun

3 @author :yinzhengjie4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/

5 EMAIL:y1053419035@qq.com6 */

7

8 package main9

10 import(11 "net"

12 "log"

13 "io"

14 "fmt"

15 "bufio"

16 "strings"

17 "os"

18 "path/filepath"

19 "io/ioutil"

20 )21

22 var (23 cmd string24 file_name string25 )26

27 func main() {28 addr := "0.0.0.0:8080" //表示监听本地所有ip的8080端口,也可以这样写:addr := ":8080"

29 listener,err := net.Listen("tcp",addr)30 if err !=nil {31 log.Fatal(err)32 }33 defer listener.Close()34 conn,err := listener.Accept() //用conn接收链接35 if err !=nil {36 log.Fatal(err)37 }38 conn.Write([]byte("欢迎来到尹正杰迷你FTP服务器!"))39 r := bufio.NewReader(conn) //将这个链接(connection)包装以下。将conn的内容都放入r中,但是没有进行读取,让步我们一会对其进行操作。40 for{41 line,err := r.ReadString('\n') //将r的内容也就是conn的数据按照换行符进行读取。42 if err ==io.EOF {43 conn.Close()44 }45 fmt.Print(line)46 line = strings.TrimSpace(line) //去掉换行符。47 fmt.Println(len(strings.Fields(line)))48 if len(line) == 0 { //为了让客户端长时间和服务器通话。49 continue

50 }51 cmd =strings.Fields(line)[0]52 if len(strings.Fields(line)) > 1{53 file_name = strings.Fields(line)[1] //需要获取服务器的文件54 }55 pwd,err :=os.Getwd()56 if err !=nil {57 panic("获取路径出错了!")58 }59 file_name =filepath.Join(pwd,file_name)60 fmt.Println(file_name)61 switch cmd{62 case "GET","get":63 f,err := os.Open(file_name) //打开文件的内容。64 if err !=nil {65 fmt.Println(err)66 }67 defer f.Close()68 buf,err :=ioutil.ReadAll(f)69 if err !=nil {70 log.Print(err)71 return

72 }73 conn.Write(buf)74 case "PUSH","push":75 fmt.Println("上传文件的语句")76 conn.Write([]byte("上传文件的命令\n"))77

78 case "EXIT","exit":79 //conn.Close()80 return

81 default:82 fmt.Println("您输入的命令无效!")83 conn.Write([]byte("您输入的指令有问题!\n"))84 }85 }86 }

客户端:

1 /*

2 #!/usr/bin/env gorun

3 @author :yinzhengjie4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/

5 EMAIL:y1053419035@qq.com6 */

7

8 package main9

10 import(11 "net"

12 "log"

13 "fmt"

14 "bufio"

15 "os"

16 "io"

17 )18

19 var (20 cmd string21 line string22 )23

24 func main() {25 addr := "172.16.3.210:8080" //定义主机名26 conn,err := net.Dial("tcp",addr) //拨号操作,用于连接服务端,需要指定协议。27 if err !=nil {28 log.Fatal(err)29 }30

31 buf := make([]byte,10240) //定义一个切片的长度是10240。32 n,err := conn.Read(buf) //接收到的内容大小为我们提前定义好的大小。33 if err != nil && err != io.EOF { //io.EOF在网络编程中表示对端把链接关闭了。34 log.Fatal(err)35 }36 fmt.Println(string(buf[:n])) //将接受的内容都读取出来。37

38

39

40 f :=bufio.NewReader(os.Stdin)41 for{42 //fmt.Print("请输入>>>:")43 line,err = f.ReadString('\n') //定义一行的内容,结束标识符是换行符“\n”44 fmt.Sscan(line,&cmd)45 if len(line) == 1{46 continue

47 }48 //fmt.Print(line)49 go sender(conn,line)50 }51 conn.Close() //断开TCP链接。52 }53

54 func sender(conn net.Conn ,line string) {55 n,err := conn.Write([]byte(line)) //向服务端发送数据。用n接受返回的数据大小,用err接受错误信息。56 if err !=nil {57 log.Fatal(err)58 }59

60 buf := make([]byte,10) //定义一个切片的长度是1024。61

62 for{63 n,err = conn.Read(buf) //接收到的内容大小。64 if err ==io.EOF {65 conn.Close()66 }67 fmt.Print(string(buf[:n]))68 }69 return

70 }

测试结果:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 欢迎来到尹正杰迷你FTP服务器!2 get a.txt3 11111

4 22222

5 33333

6

7 get \Day9\yinzhengjie.html8

9

10

11

12

13

尹正杰的个人主页

14

15

16

17

18

19

20

尹正杰

21

尹正杰

22

尹正杰

23

尹正杰

24

尹正杰

25

尹正杰

26

You are a good boy!

27

28

尹正杰

29

You are a good boy!

30

31

素胚勾勒出青花笔锋浓转淡
瓶身描绘的牡丹一如你初妆
冉冉檀香��事我�透过窗宣纸上赆然
�笔至此搁一半釉色�被私藏
而佥�图韵呠嫣然的一笑如含苞待放

32 yinzhengjie

33

34 尹正杰博客

35

36 Golang第一!--a标签特有的�章 Gol>

37

38 Golang�>

39

40

41

42

43 Golang进阶之路Day1

44 Go语言官方自称,之所以开发Go 语言,是因为“近10年来开发程序之难让我们有点沮丧”。 这一定位暗示了Go语言希望取代C和Java的地位,成为最流行的通用开发语言。博客地址:http://www.cnblogs.com/yinzhengjie/p/6482675.html45

46

47

48
Golang进阶之路Day2

49 前者大家应该都很熟悉,因为我在上一篇(http://www.cnblogs.com/yinzhengjie/p/6482675.html)关于GO的博客中用"go build"命令编译不同的版本,但是在这里我们还是要演示一下go build的花式用法。博客地址:http://www.cnblogs.com/yinzhengjie/p/7000272.html50

51

52

53 Golang进阶之路Day3

54 当然我这里只是介绍��冰山�了Golang�角,对Golang感兴趣的小伙伴,可以看一下Golang官网的文档说明。毕竟官方才是最权�出国内地址:https://golang.org/pkg/!博客威的,�tp://www.c��址:htnblogs.com/yinzhengjie/p/7043430.html55

56

57

58

我是块标签

59 我是内联标签

60

61

62

63 一

64

菜单二

65

菜单三

66

67

68

69

第�菜单70 第二章

71

第三��章章

72 ol>

73 <

74

75

北京

76

朝阳区亦庄�t>

77

78 �济开发

�t>

79

海淀区

80

河�台区

81 家庄

82

保定石d>陕西

83

西安

84

安康t>

85

86

87

88

89

91

92

实现- 姓名

94 ��行内�>年龄性�h>

95 ��

96

97

98

99

100

尹正杰

101

�粗效果

102

103

105

尹正杰

106

26

107

108 'tr'表示

109

yinzhengjie

110 d'标签�

111

112

113

yinzhengjie

114

26

115

116

117

118

119

以上客户端使用方法戳我

该脚本并没有完成完整的FTP,只是写了一个引子,放在这里,如果以后我有兴趣了,可能会把它优化一下,我把这段代码贴在这里就是为了提供一个网络socket的传输案例,方便我以后查看,哈哈~

2.后台聊天程序;

该程序主要实现了,不同用户登录服务器可以输入相同的密码,然后大家可以互相通信的小程序。具体代码和注释如下:

1 /*

2 #!/usr/bin/env gorun

3 @author :yinzhengjie4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/

5 EMAIL:y1053419035@qq.com6 */

7

8 package main9

10 import(11 "bufio"

12 "fmt"

13 "log"

14 "net"

15 "strings"

16 "time"

17 )18

19 var globalRoom *Room =NewRoom()20

21 type Room struct {22 users map[string]net.Conn23 }24

25 func NewRoom() *Room {26 return &Room{27 users: make(map[string]net.Conn),28 }29 }30

31 func (r *Room) Join(user string, conn net.Conn) {32 _, ok :=r.users[user]33 ifok {34 r.Leave(user) //如果存在用户user就提出之前的链接,调用Leave方法实现。35 }36 r.users[user] =conn37 fmt.Printf("%s 登录成功。\n", user)38 conn.Write([]byte(user + ":加入聊天室!\n"))39 }40

41 func (r *Room) Leave(user string) {42 conn, ok :=r.users[user]43 if!ok {44 fmt.Printf("%v用户不存在!", user)45 }46 conn.Close() //如果存在用户就断开链接。47 delete(r.users, user) //将用户从字典中删除。48 fmt.Printf("%s 离开", user)49 }50

51 func (r *Room) Broadcast(who string, msg string) {52 time_info := time.Now().Format("2006年01月02日 15:04:05") //这个是对日期定义一个格式化输出。告诉你一个记住它的方法:2006-01-02 15:04:05对应着2006 1(01) 2(02) 3(15) 4(04) 5(05) 哈哈53 tosend := fmt.Sprintf("%v %s:%s\n", time_info,who, msg)54 for user, conn := range r.users { //遍历所有用户,55 if user ==who {56 continue //当发现用户是自己就不发送数据。即跳过循环。57 }58 conn.Write([]byte(tosend)) //将数据发送给登陆的用户。59 }60 }61

62 func Handle_Conn(conn net.Conn) {63 defer conn.Close()64 r := bufio.NewReader(conn) //将用户的输入存入“r”中,方便一会我们按块读取。65 line, err := r.ReadString('\n')66 if err !=nil {67 fmt.Println(err)68 return

69 }70 line =strings.TrimSpace(line)71 fields :=strings.Fields(line)72 if len(fields) != 2{73 conn.Write([]byte("您输入的字符串用户名活密码无效,程序强制退出!\n"))74 return

75 }76 user :=fields[0]77 password := fields[1]78 if password != "123"{79 return

80 }81 globalRoom.Join(user, conn)82 globalRoom.Broadcast("System", fmt.Sprintf("%s join room", user))83 for { //获取用户的输入。84 conn.Write([]byte("按回车键发送消息:>>>"))//这里是给客户端增加一个提示符85 line, err := r.ReadString('\n') //循环读取用户输入的内容。换行符为“\n”86 if err != nil { //当用户主动关闭连接是,会出现报错就直接直接终止循环。87 break

88 }89 line = strings.TrimSpace(line) //去掉换行符90 fmt.Println(user,line)91 globalRoom.Broadcast(user, line) //将用户输入的消息进行广播。92 }93 globalRoom.Broadcast("System", fmt.Sprintf("%s Leave room", user))94 globalRoom.Leave(user) //踢掉用户。95 }96

97 func main() {98 addr := "0.0.0.0:8888"

99 listener, err := net.Listen("tcp", addr)100 if err !=nil {101 log.Fatal(err)102 }103 defer listener.Close()104 for{105 conn, err :=listener.Accept()106 if err !=nil {107 log.Fatal(err)108 }109 go Handle_Conn(conn)110 }111 }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值