说到Socket
,想必你们会以为陌生又熟悉。许多同窗据说过Socket,但仅仅知道它翻译成中文叫作套接字
,除此以外彷佛并无太多的了解了。那么今天我就来抛砖引玉地聊一聊Socket。有人说web
The lower application layers are all about socket programming
应用的底层全是和socket打交道编程
一看到涉及底层,有的同窗就表示:
其实这些东西并不深奥,只要花一些时间去看,确定是可以看懂的,而且一但找到了点儿感受,会以为Socket很是有趣。
你难道很差奇浏览器是怎样和web服务器勾搭在一块儿来取悦你的吗?许多网络应用都经过Socket来交流,因此Socket在网络编程里占有了很重要的地位。
那么言归正传,到底什么是Socket
的呢?——大学教材上的答案是套接字
。我我的以为这是一个不太好的翻译,虽说仔细一想有那么点儿意思,可是99%的人即便看见套接字
这个词,依然不知道是什么鬼,因此没有翻译的必要。就像Rap
你硬要说中文翻译叫拉普
也没啥意义对吧。
在Unix中,有一种说法叫浏览器
Everything is a file
一切皆文件服务器
因此你只须要记住Socket是某种类型文件的抽象
怎么理解这句话呢?想象一下,假设你要开发一个网络应用,须要在两个客户端之间发消息。整个过程可能包含如下步骤:网络
文件
。它是一个链接了两个用户的文件,任何一个用户向Socket里写数据,另外一个用户都能看获得,无论这两个用户分布在世界上相距多么遥远的角落,感受就像坐在一块儿传纸条同样。Socket
应该更容易理解吧?这种抽象是很是重要的,由于它屏蔽了更底层的东西,我就想写个程序发送下数据,为何要关系物理层怎么传输呢,对吧。这样的话,网络编程是否是就很是简单了呢?
那么下面咱们用Go语言做为示例,演示一下。并发
package main import ( "fmt" "net" "os" ) func main() { //使用tcp协议,要监听的是6666端口 tcpAddr, err := net.ResolveTcpAddr("tcp", "localhost:6666") checkError(err) //开始监听 fd, err := net.ListenTcp("tcp", tcpAddr) checkError(err) for { //获取Socket conn, err := fd.Accept() if err != nil { continue } //你的逻辑 go Handle(conn) } } func Handle(conn *net.Conn) { //You can do anything you want to here... } func checkError(err error) { if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } }
端口是什么概念?能够近似这么想:一台电脑就是你家的小区,你买东西若是填的地址是你家小区,那么快递员最多能把东西送到小区门口;可是若是你写上了你家的门牌号,那么快递员就能送到你家门口。一样的,电脑上同时运行着不少程序,好比QQ,旺旺…可是电脑只有只有一个IP地址,一条消息来了没人知道这个消息是给谁的,因而就有了端口的概念。QQ在这台电脑的4567端口,旺旺在这台电脑1234端口。发消息的人只要知道它在什么端口,就能准确地把消息发过来了。
一样的,网络通讯两端的人得事先约定好一个端口,而后一我的守着这个端口,待另外一方链接了这个端口,这才算创建了Socket链接。就好两我的打电话,不须要关心信号怎么转换和传输,但在创建此次通话以前必须有人拨号,同时有人守在电话旁。
因而上面的代码应该就能够理解了吧?
一个程序猿走到"localhost:6666"这个“电话”旁边app
tcpAddr,err := net.ResolveTcpAddr("tcp","localhost:6666")`
而后坐下来等电话响socket
fd,err := net.ListenTcp("tcp", tcpAddr)
他也不知道女友何时打电话过来,因而开始了漫长的等待tcp
//一个死循环 for { conn,err := fd.Accept() //电话没有响就一直堵在上面这条语句 }
在漫长地等待中,忽然电话响了,而后开始了一段佳话(程序终于不堵了,接着向下执行)函数
go Handle(conn)
Handle
方法就用来处理对话,数据都在conn
里面,只须要学习相关的API就能知道怎么把具体的内容取出来了。
整个过程是否是很简单?
有些机智的同窗可能已经发现了,要这样的话,两我的都在等对方打电话过来,岂不是就终身无缘了(这种误会就像韩剧)。对的,因此咱们还须要知道,如何给对方拨号。这是很关键的一步,通常妹子很差意思,那么天然咱们得上了。
怎么拨号呢?请看代码:
import ( "fmt" "io/ioutil" "net" "os" ) func main() { tcpAddr, err := net.ResolveTcpAddr("tcp", "localhost:6666") checkError(err) conn, err := net.DialTcp("tcp", nil, tcpAddr) checkError(err) _, err = conn.Write([]byte("妹子,约吗?")) result, err := ioutil.ReadAll(conn) //不约,叔叔咱们不约 } func checkError(err error) { if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } }
解释一下,通常妹子都比较含蓄,告诉你联系方式不那么直接,你得破解一下
tcpAddr := net.ResolveTcpAddr("tcp", "localhost:6666")
呵呵,嘴上说不要身体却很诚实嘛,这么容易就破解了。(实际上是Go的包比较好用好吗!)
而后按着电话号拨打电话
conn, err := net.DialTcp("tcp", nil, tcpAddr)
电话打通了,conn
就表明此次通话。屌丝们已经急不可耐了,因而大喊一句:
_, err = conn.Write([]byte("妹子,约吗?"))
为何我第一个返回值用_
,这表示我不想知道函数的返回结果,即Write了多少个字节。我问妹子约不约,你说我关不关心这句话包含几个字节?
result, err := ioutil.ReadAll(conn)
妹子给的回复就在result里,慢慢去琢磨吧……
以上示例并不完整,完整的示例网上处处可见,但愿你们能本身写一写。
本篇只是粗浅地讲了讲什么是Socket编程以及基本过程,以后会有更细致地讲解(好比:并发)。
做者: PHPBird 连接:http://www.imooc.com/article/1668来源:慕课网