那一天,人们终于回想起了被BUG支配的恐惧
Toggle navigation
Home
AboutMe
Links
Archives
Tags
Socket网络编程入门
2017-04-14 00:31:09
1637
0
0
weibo-007
#Socket网络编程入门 ##引子 从我们熟悉的web应用为例子,先不讨论socket,我们看一下如果两台机器之间要通讯,需要经过哪些步骤。我们看一下服务器回给我们一个简单的"Hi"时,需要经过哪些步骤。 ![image](http://note.youdao.com/yws/api/personal/file/WEB53eac25ac583cbe332c17e1285d6acc6?method=download&shareKey=06ed96c69c4479142d7116ad2db43558) 服务端: 1. 服务器的应用层会产生一个Hi字符串 2. 数据封装后交给传输层 3. 传输层封装后交给网络层 4. 网络层数据封装后交给数据链路层 5. 物理层接收后变成0和1字节流传输到Internet网络中 客户端: 1. 客户端物理层接收来自服务端的0和1字节流 2. 数据链路层接收物理层数据并解读数据报 3. 网络层接收物理层数据并解读数据报 4. 传输层解读网络层数据并解读数据报 5. 应用层接收到Hi字符 现实中的网络数据传输远远比这复杂得多,数据必须经过5层协议栈。试想如果让一个开发者从0开始实现这样的一个数据传输,难度可想而知。但是目前从事网络开发的人千千万万,他们是怎么做到的呢,这一切都要归功于Unix操作系统和Socket。因为有了Socket,网络编程变得快速而且简单了很多。 ##什么是socket Socket就是操作系统(Unix)为应用进程提供的一个方便操作网络数据收发的api接口,只要你读懂了这一套api接口,就可以把数据从一个主机传输到另一个主机,或者从其他主机接受数据,而不需要知道还有五层协议的复杂性。这就好比我们开发者读取文件的时候,file系列函数就可以轻松的帮我们获取文件的内容,我们不需要知道磁盘的磁头如何转动,如何实现定位读数据等。 下面是操作系统提供的Socket层的位置 ![image](http://note.youdao.com/yws/api/personal/file/WEB50581e027f5d71fe4f0f46e0d5eb66fc?method=download&shareKey=df42d2add72694d5a72674eb8beae5dd) 这个图的上层是应用程序层,比如我们的nginx,apache系列的软件,下层是操作系统层,比如Unix。Socket层位于应用层和传输层之间,上层应用如果想实现网络编程,只需要调用操作系统提供的Socket层提供的api即可。操作系统通过Socket大大简化了网络编程的复杂性,使得从事网络编程的开发人员更加便捷。 ##Socket相关函数一览 既然知道了Socket是操作系统为我们提供的一层网络编程接口封装,那就可以看一下操作系统给我们留了哪些接口,我们能用这些接口干什么 |函数名称|用途|说明| |--|--|--| |socket|创建套接字|服务端/客户端需要使用| |connect|连接远端服务器|仅客户端| |bind|绑定套接字的本地ip和端口号|通常客户端不需要| |listen|置服务端套接字为监听模式|仅服务端可用| |accept|接收一个连接请求并创建新套接字|仅服务端可用| |send|发送数据|服务端和客户端可用| |recv|接收数据|服务端和客户端可用| |close|关闭套接字|服务端和客户端可用| ##Socket实践 ###服务端例子 先来看一个服务端的例子,下面文件保存为server.c ``` #include <stdio.h> #include <string.h> #include <stdlib.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/socket.h> #include <netinet/in.h> int main(){ int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); struct sockaddr_in serv_addr; memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); serv_addr.sin_port = htons(1234); bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); listen(serv_sock, 20); struct sockaddr_in clnt_addr; socklen_t clnt_addr_size = sizeof(clnt_addr); int clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size); char str[] = "Hello World!"; write(clnt_sock, str, sizeof(str)); close(clnt_sock); close(serv_sock); return 0; } ``` 然后编译server.c文件生成可执行文件server ``` [root@iZ9 test]# gcc server.c -o server ``` 运行./server命令,启动服务器 ``` [root@iZ9 test]# ./server ``` 启动服务端成功,使用的端口号是1234,进程会进入等待状态。然后重新开启一个服务器窗口,我们先不编写客户端代码,直接使用telnet命令连接服务器1234端口 ``` [root@iZ9 socket]# telnet 127.0.0.1 1234 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. Hello World!Connection closed by foreign host. ``` 可以看到服务端给我们输出的Hello World! 同时服务端./server进程终止。 ###代码解释 1)创建Socket套接字 ``` int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); ``` 函数里面的参数,AF_INET 表示使用 IPv4 地址,SOCK_STREAM 表示使用面向连接的数据传输方式,IPPROTO_TCP表示使用TCP协议。这个函数返回的是一个文件描述符,可以进行读写操作。 2)绑定IP地址和端口 ``` bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); ``` bind() 函数将套接字serv_sock 与特定的IP地址和端口绑定,IP地址和端口都保存在 sockaddr_in 结构体中,所有要使用这个函数,必须先构造sockaddr_in结构体,IP和端口保存在这个结构体中。使用这个函数可以让创建的serv_sock套接字和IP+端口联系起来。 3)进入监听状态 ``` listen(serv_sock, 20); ``` 让套接字处于被动监听状态。所谓被动监听,是指套接字一直处于"睡眠"中,直到客户端发起请求才会被"唤醒"。第二个数字是20表示队列长度大小,每个请求是先进入队列才能被套接字接下来处理,这说明这个描述符上排队的最大客户端连接数是20。 4)接受客户端请求 ``` accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size); ``` accept() 函数用来接收客户端的请求,并产生一个新的描述符。这个函数返回是一个新的已连接描述符,是TCP三次握手完毕之后的新描述符。程序一旦执行到 accept() 就会被阻塞(暂停运行),直到客户端发起请求,所以当我们运行./server命令的时候,进程是自动退出的,除非有客户端请求进来。 5)向客户端发送数据 ``` write(clnt_sock, str, sizeof(str)); ``` Socket就是一种文件描述符,向客户端文件描述符clnt_sock写数据,就是向客户端发送数据 6)关闭连接 ``` close(clnt_sock); close(serv_sock); ``` 表示最后同时关闭客户端和服务端连接
Pre:
TCP连接的建立和终止
Next:
Hadoop+Hive环境搭建
0
likes
1637
Weibo
Wechat
Tencent Weibo
QQ Zone
RenRen
Submit
Sign in
to leave a comment.
No Leanote account?
Sign up now.
0
comments
More...
Table of content
No Leanote account? Sign up now.