网络编程学习大纲&&网络编程1.1

发布时间 2023-09-07 20:24:03作者: 若达萨罗

网络编程的学习大纲

  1. 计算机网络体系结构模型,网络编程专业术语(socket/IP/端口号),通信时序图
  2. 传输协议:TCP协议/UDP协议
  3. 多进程并发服务器,多线程并发服务器
  4. 网络编程IO模型:阻塞IO/非阻塞IO/多路复用/信号驱动
  5. 超时接收数据方法

回顾系统编程中进程的通信方式

  1. 管道

    • 无名管道(只能亲缘关系)------>pipe()
    • 有名管道(任意两个进程)------>mkfifo()
  2. 信号

    • 发送信号------>kill()
    • 捕捉信号------>signal()
  3. IPC对象

    • 消息队列------>接受特征类型的数据----->ftok():获取键值 msgget():创建队列,获取ID msgsnd():发送消息msgrcv():接受消息 msgctl():删除消息队列
    • 共享内存------>双方进程可以对同一片内存进行读写 shmget():创建共享内存,获取共享内存ID shmat():映射共享空间 shmdt():接触映射 shmctl():删除共享内存
    • 信号量-------->不属于通信方式,只是一种互斥的量 semget():获取信号量ID semop():PV操作 semctl():删除信号
      以上的特点:只能在同一台主机上内部通信,不能跨平台

网络编程(套接字编程)

  1. 特点

    • 既可以在同一台主机上内部通信,也可以在不同的主机之间通信
    • 自己的Ubuntu<------>自己的Ubuntu
    • 自己的Ubuntu<------>同一个局域网内除了自己以外的任意一台主机
      总结一下:网络通信的前提,只要你在某一个局域网内,就可以与局域网内任意一台主机通信
  2. 协议

    • 概念:应用的角度出发,在不同的主机之间通信,双方都必须要遵循同一种规则。协议可以理解为“规则”,是数据传输和数据解释的规则

    假设A,B双方想发文件
    规则:第一次传输文件名,接收方收到文件名,应答OK给传输方;第二次发送文件的大小尺寸,接收方收到以后,应答一个OK给传输方;第三次 传输文件的内容,接收方收到以后,应答一个OK给传输方;由此,无论AB之间传输何种文件,都是通过三次数据传输来实现。AB之间就形成了一个简单的数据传输规则。双方都可以根据这种规则进行收发数据。这种相互遵循的规定,我们称之为协议


    这种仅在AB之间通信的称之为原始协议。当此协议被更多人使用,不断的去增加,改进,维护,完善。最终就形成了一个稳定的,完整的文件传输协议,被广泛的应用各种文件传输,该协议就成为一个标准协议。最早的ftp协议(File Transfer Protocol)就是由此衍生出来的


    TCP协议注重数据的传输,传输控制协议(Transmission Control Protocol)
    HTTP协议注重数据的解释,超文本传输协议(Hypertext Transport Protocol)

  3. 常见的协议

    • 传输层 常见的协议有TCP/UDP协议
    • 应用层 常见的协议有HTTP协议,FTP协议
    • 网络层 常见的协议有IP协议,ICMP,IGMP协议
    • 网络接口层:常见的协议有ARP协议,RARP协议

    TCP传输控制协议:是一种面向连接,可靠的,基于字节流的传输层控制协议
    UDP用户数据报协议(User Datagram Protocol):是OSI参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠的传输服务
    HTTP超文本传输协议:互联网应用最为广泛的一种网络协议。
    FTP:文件传输协议
    IP协议:互联网协议(Internet Protocol)
    ICMP:网间控制报文协议(Internet Control Message Protocol),它是TCP/IP协议簇的一个子协议。用于IP主机,路由器之间传递控制消息。
    IGMP协议:因特网组管理协议(The Internet Group Management Protocol),是因特网协议家族中的一个组播协议。该协议运行在主机和组播路由器之间
    ARP协议:地址解析协议(Address Resolution Protocol),通过已知的IP,寻找对应的MAC地址
    RARP协议:反向地址转换协议,通过MAC地址确定IP地址

  4. 网络应用程序设计模型

    • C/S
      模型网络应用设计模式,客户机(client)/服务机(server)模式。需要在各自的通讯两端各自部署服务器与客户端来完成数据通信

    • B/S模型
      浏览器(browser)/服务器(server)模式。只需要在一端部署服务器,而另外一端使用PC都默认的配置的浏览器即可完成数据的传输

    • 优缺点
      对于C/S模式而言,其优点明显。客户端位于目标主机上,可以保证性能,将数据缓存至客户端的本地,从而提高数据的传输效率。一般服务器和客户端斗士由一个团队开发出来的,它们之间采用的协议就相对灵活。可以在标准协议进行裁剪及定制
      例如,腾讯公司采用的通信协议就是ftp协议的修改裁剪版
      因此,传统的网络应用程序及较大的一些网络应用程序都是首选C/S模式进行开发,比如网络游戏魔兽世界。3D画面,数据比较庞大,使用C/S模式可以进行本地的数据缓存,从而提高游戏体验。
      C/S模式缺点也比较突出,客户端与服务器都需要团队开发,时间成本比较高,开发周期长。从用户的角度出发,需要将用户端插入到用户主机上,对用户主机的安全性就有问题

      B/S模型相比C/S,由于没有独立的客户端,使用浏览器作为客户端,开发工作量比较少,只需要开发服务器,使用浏览器,移植性就很强,不受平台约束。比如偷菜游戏,在各个平台都可以运行。

      B/S缺点:因为使用第三方浏览器,因此网络应用支持受限。因为把客户端数据放到用户主机,存储数据就不行,用户体验观感没有那么好。协议必须与浏览器一样的,标准的http协议进行通信,协议选择不灵活
      因此在开发,根据实际需求来进行选择模式开发

计算机网络体系架构模型

  1. 概念

    • 指的是主机内部集成的结构和每一层协议的集合。每一台主机本身就存在一个相同的网络体系结构
  2. 作用

    • 封装数据和解析数据
  3. 分类

    • 由于网络数据的传输过程比较复杂,因此需要将传输过程按照所完成的功能进行每一层的分层,每一层对应一个单一的明确的功能,就使得每一层实现的功能容易行业标准,代码便于维护
      OSI开放式系统互联参考模型
      口诀:物数网传会表应
      image
  4. OSI参考模型(七层模型)

    • 物理层:主要是定义物理设备标准,如网线的接口模型,光纤的接口类型,各种传输介质的速率。它的主要作用,传输比特流(就是1,0转换为电流强度来进行转换(数模转换),到达目的地,再转换为1,0(模数转换))。这一层的数数据较比特
    • 数据链路层:定义了如何让格式化数据以帧为单位进行传输,如何控制对物理介质的访问。还提供了错误检测和纠正,确保可靠传输。比如串口通信:115200(波特率),8位数据位,无奇偶校验位,1位停止位等
    • 网络层:在位于不同的地理位置的网络中的两个主机系统之间提供连接和路径选择(给不同地方的主机提供连接服务)Internet的发展使得从世界各站点访问信息的用户增加,而网络层就是管理这种连接的层
    • 传输层:定义了一些传输的协议和端口号(WWW 端口80等),如TCP协议(传输控制协议,传输效率低,可靠性强,用于传输可靠性高,数据量大的数据),UDP协议(用户数据报协议,与TCP协议相反,用于传输可靠性不高,数据量小的数据,如QQ语音视频聊天)。主要是将下层接收的数据进行分段和传输,到达目的再重组,通常把这一层的数据叫段
    • 会话层:通过传输层(端口号:传输端口与接收端口)建立起数据传输的通路。主要在你的系统之间发起会话或者接受会话请求(设备之间需要互相认识可以是IP也可以是MAC地址或者主机名)
    • 表示层:确保一个系统的应用层所发送的数据,能够被另一个系统的应用层读取。比如一台主机使用的是拓展二一十进制交换码(EBCDIC),另外一台电脑使用ASCII来表示相同的字符。表示层使用一种通用格式老实现多种数据格式之间的交换。
    • 应用层:最靠近用户的OSI层。这一层为用户的应用程序(电子邮件,文件传输)提供网络服务
      image
  5. TCP/IP协议模型(四层)

    • 应用层,传输层,网络层,链路层
      image

TCP/IP通信模型的过程

  1. 两台计算机通过TCP/IP协议通讯的过程
    image

  2. 数据包的封装

    • 传输层及以下的机制都是内核提供,应用层有用户进程提供,应用程序对通讯数据含义的解释,而传输层以下处理通讯的细节,将一台计算机通过一定的路径发送到另一台计算机。应用层数据通过协议栈发送到网络中,每一层协议都需要加数据首部(header),称之为封装。如下图所示:
      image
      image
    • 不同的协议层对数据包有不同的称谓,在传输层叫段(segment),在网络层叫做数据报(datagram),在链路层叫做帧(frame)。数据封装成帧后发送到介质上,到达目的主机, 每层协议再剥离首部,最后将数据传给应用程序处。
  3. 以太网帧
    image

    • 其中源地址和目的地址是指网卡的硬件地址,(也叫MAC地址),长度48位(6个字节),在网卡出厂时固化,确定的。Ubuntu中使用ifconfig命令查看MAC地址
    • 协议字段有三种值:IP、ARP、RARP、帧尾CRC校验码
    • 以太网帧数据长度规定:最小46字节,最大1500字节。ARP,RARP数据包没有46字节,要在后面补充位数。
      最大值1500称之为以太网的最大传输单元(MTU)maximum transmission unit
    • 不同的网络类型有不同的MTU,如果说一个数据包,从以太路由到拨号链路上,数据包长度大于1500,则对数据包进行分片ifconfig命令的输出终端---MTU:1500.MTU概念指的是数据帧有效载荷为1500,不包括帧头长度。
  4. ARP数据报格式

    • 在网络通讯时,源主机的应用程序知道目的主机的IP地址和端口号,却不知道目的主机的硬件地址,而数据包首先是被网卡接收到的,再去处理上层协议,如果收到的数据包的硬件地址与本机不符,则直接丢弃。因此在通讯之前,必须要知道目的主机的硬件地址。ARP就是起这个作用。
      源主机发出ARP请求,询问IP地址是192.168.1.6的硬件地址是多少?并将请求发广播到本网段。目的主机收到ARP请求,发现其中的IP地址与本机一样,将自己的硬件地址填写到应答包中。
      每台主机都维护一个ARP缓存表,可以用arp -a命令查看。缓存表中表项有过期时间(一般是在20分钟),如果20分钟内都没有再次使用这个表项,则该表项失效,下一次还要发ARP请求,获取这个目的主机的地址
      ARP数据报的格式:
      image
      源MAC地址,目的MAC地址在以太网的首部和ARP请求中各出现一次,对于链路层是以太网的情况是多余的,但是链路是其它类型的网络则可能是有必要的。硬件类型指的是链路层网络类型,协议类型指要转换的地址类型。op是两个字节,字段为1.表示ARP请求,字段为2表示ARP应答
  5. IP类型
    image

    • IP数据报的首部长度和数据长度都是可变长的,但总是4字节的整数倍。对于IPv4,4位版本字段是4。4位首部长度的数值是以4字节为单位的,最小值为5,也就是说首部长度最小是4x5=20字节,也就是不带任何选项的IP首部,4位能表示的最大值是15,也就是说首部长度最大是60字节。8位TOS字段有3个位用来指定IP数据报的优先级(目前已经废弃不用),还有4个位表示可选的服务类型(最小延迟、最大?吐量、最大可靠性、最小成本),还有一个位总是0。总长度是整个数据报(包括IP首部和IP层payload)的字节数。每传一个IP数据报,16位的标识加1,可用于分片和重新组装数据报。3位标志和13位片偏移用于分片。TTL(Time to live)是这样用的:源主机为数据包设定一个生存时间,比如64,每过一个路由器就把该值减1,如果减到0就表示路由已经太长了仍然找不到目的主机的网络,就丢弃该包,因此这个生存时间的单位不是秒,而是跳(hop)。协议字段指示上层协议是TCP、UDP、ICMP还是IGMP。然后是校验和,只校验IP首部,数据的校验由更高层协议负责。IPv4的IP地址长度为32位。
  6. TCP数据报格式
    image

    • 有源端口号和目的端口号,通讯的双方由IP地址和端口号标识。32位序号、32位确认序号、窗口大小。4位首部长度和IP协议头类似,表示TCP协议头的长度,以4字节为单位,因此TCP协议头最长可以是4x15=60字节,如果没有选项字段,TCP协议头最短20字节。URG、ACK、PSH、RST、SYN、FIN是六个控制位。16位检验和将TCP协议头和数据都计算在内。

传输协议

TCP协议(打电话)

  1. 概念

    1. 用来检测网络传输中差错的传输控制协议。是一种面向连接的传输层协议,它能提供可靠性通信(即数据无误,无丢失,数据无失序,数据无重复到达的通信)
  2. 适用场合

    1. 对传输质量要求较高,以及传输大量数据的通信
    2. 在需要可靠传输的场合
    3. QQ登录账户等

UDP协议(信)

  1. 概念

    1. UDP数据报协议,是不可靠连接。在数据发送前是不需要进行连接,所以可以进行高效率的数据传输
  2. 适用场合

    1. 发送数据尺寸比较小的数据
    2. 适用于广播/z组播通信
    3. QQ等即时通讯软件的点对点的文本通讯以及音视频通讯(短视频直播)常采用UDP协议
    4. 网络多媒体服务中心通常使用UDP进行实时数据传输

网络编程中的几个比较重要的概念

  1. socket------>本身具有插座的含义,套接字。插座种类繁多,就像多种协议一样,必须提前设置好协议

    1. 概念:socket在Linux环境中,用于表示进程间网络通信的特殊文件类型。本质为内核借助缓冲区形成的伪文件。既然是文件,那么可以用文件描述符引用套接字。与管道文件相似,Linux系统将其封装成文件的目的是为了统一接口,使得读写套接字和读写文件的操作一致。区别是管道主要应用于本地进程之间通信,而套接字是网络进程之间数据传输。套接字内核实现比较复杂,不宜在初级深入学习。
      在TCP/IP协议中,“IP地址+TCP或者UDP端口号”唯一表示网络通信的一个进程。“IP地址+端口号”就对应一个socket。欲建议两个进程的通信,各自要对应一个socket套接字来标识,那么这两个套接字组成socket pair就是唯一标识的一个连接。因此socket来描述网路连接的一对一关系

    2. 套接字通信原理图
      image
      在网路通信中,套接字一定是成对存在的,一端的发送缓冲区,对应一端的接收缓冲区。我们使用的同一文件描述符指向发送缓冲区和接收缓冲区
      TCP/IP协议最早在BSD UNIX上实现的,为TCP/IP协议设计的应用层编程接口称为socket API
      image

    3. 特点
      是一套编程的函数接口,目的是建立套接字
      无论是TCP协议还是UDP协议,都是使用socket
      socket在网路模型中处于应用层与传输层之间

    4. 例子
      使用TCP协议,那么用socket创建出来的套接字就是TCP套接字

  2. IP地址
    IP地址实质上等同于一台计算机,用于在世界范围内更加方便的唯一标识某一台机器
    一个IP地址从形式上可以分成前后两段,前半段叫网络号,后半段称为主机号。网络号用来作为该主机所在网络在全世界的唯一标识,主机号在所在的网络内部的唯一标识
    由此根据网络号与主机号所占位数的不同,将IP地址分为以下的类别

    A类地址
    第一个字节为网络地址,其它三个字节为主机地址
    第一个字节的最高位固定为0
    
    B类地址
    第一个字节和第二个字节为网络地址,其它两个字节为主机地址
    第一个字节的前两位固定为10
    C类地址
    前三个字节为网络地址,最后一个字节为主机地址
    第一个字节的前三位固定为110
    
    D类(组播)地址
    不分网路地址与主机地址
    第一个字节前四位固定为1110
    
    E类IP地址
    以11110开始的,为将来使用保留的
    255.255.255.255就是广播地址
    0.0.0.0对应当前主机
    
  3. 端口号(16位)
    标识一台主机内不同的应用程序
    端口号:

    1. 系统占用端口号:1-1023(用户不能使用该端口进行通信)
    2. 可用:1024-65535
      端口选择错误:连接错误
  4. 字节序
    当一个数据需要两个或者以上的字节来存储时,就会出现所谓字节序的概念
    通常,将最低的有效位放在低地址的存储方式称为小端序,反之称之为大端序。在单机编程中,字节序时系统内部的存储细节,与程序无关。但在网络编程中,由于数据是两台主机在进行通信表达,如果字节序不一样出现牛头不对马嘴的问题
    解决这个困境的唯一方法:将网络中的数据,统一为某种固定的字节序,比如大端序,凡是主机往外发送,一律转成大端序,凡是从网络下载的数据也是大端序,网络字节序屏蔽双方通信的具体细节,从而使得双方通信
    image

虚拟机的网络配置

  1. 虚拟机的网络适配器的模式
    桥接模式:使得虚拟机跟物理机,各自拥有独立IP地址
    NAT模式:使得虚拟机共享物理机的IP地址
    以上两种模式都可以使得虚拟机内的客户机(ubuntu系统)去连接网络,区别就是虚拟机是否拥有独立IP.对于桥接模式需要注意,必须要仔细选择桥接的物理网卡,否则会出现无法联网的情况
    image
    通过虚拟网络编辑器选择桥接对象

  2. 桥接模式
    所谓桥接模式,是指虚拟网卡通过物理机的某一个网络接口,直接连接到路由去获取IP地址

  3. NAT模式
    如果虚拟机不需要单独的IP地址,可以通过NAT模式进行连接网络。NAT模式下的IP地址是由虚拟网卡自动分配,与主机地址不在同一个网段

4.仅主机模式
有时限于外部网络的局限性,在Ubuntu无需连接外网而需要宿主机Windows通信时,将VMware虚拟机网卡配置为主机模式。

TCP通信

  1. 通信过程

客户端的API

  1. 建立套接字
#include <sys/types.h>
#include <sys/socket.h>

	// 函数作用:建立套接字,返回套接字文件描述符
	int socket(
		int domain,   // 参数1:选择地址族   AF_INET:IPV4    AF_INET6:IPV6
		// 两者在windows下没有任何区别,在linux/unix有细微差别
		// PF_INET---Protocol--->在创建套接字的使用
		// AF_INET---Address--->结构体定义地址的时候
		int type,   // 参数2:选择哪一种协议   SOCK_STREAM:TCP流式   SOCK_DGRAM:UDP数据报套接字
		int protocol   // 参数3:0:表示默认协议
	);
	返回值:成功:套接字描述符   失败:-1

  1. 发起连接
#include <sys/types.h>
#include <sys/socket.h>

	int connect(
		int sockfd,   // 参数1:套接字描述符
		const struct sockaddr *addr,   // 参数2:对方的IP地址和端口号
		socklen_t addrlen   // 地址的长度(sizeof(struct sockaddr_in))
	);

struct sockaddr_in {
               sa_family_t    sin_family; /* address family: AF_INET 地址族*/
               in_port_t      sin_port;   /* port in network byte order 端口号*/
               struct in_addr sin_addr;   /* internet address IP地址*/
           };

struct in_addr {
               uint32_t    s_addr;     /* address in network byte order 无符号整形数据*/
           };

初始化IP地址和端口号--IPV4

	struct sockaddr_in serverAddr;
	serverAddr.sin_family=AF_INET;
	serverAddr.sin_port=htons(5000);//htons--->将主机端口号变成网络端口号 host to network short
	serverAddr.sin_addr. s_addr=inet_addr("192.168.1.47");//inet_addr将主机IP转换为网络IP
	connect(sockfd,(struct sockaddr *)&serverAddr,sizeof( struct sockaddr_in));

拓展的函数

#include <arpa/inet.h>

	uint16_t htons(uint16_t hostshort);   // 将主机端口号转换成网络端口号

	uint16_t ntohs(uint16_t netshort);   // 将网络端口号转换成主机端口号

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

	in_addr_t inet_addr(const char *cp);   // 将主机IP转换成网络IP   参数:主机IP

	char *inet_ntoa(struct in_addr in);   // 将网络IP转换成主机IP

  1. 发送数据
#include <sys/socket.h>

	ssize_t send(
		int socket,   // 参数1:套接字文件描述符
		const void *buffer,   // 参数2:需要发送的文本
		size_t length,   // 参数3:文本大小
		int flags   // 参数4:默认为0
	);
	// 返回值:成功:字节数   失败:-1
  1. 关闭文件---close()

服务端API

  1. 绑定
#include <sys/types.h>
#include <sys/socket.h>
	// 作用:绑定自己的IP地址和端口号
	int bind(
		int sockfd,   // 参数1:套接字描述符
		const struct sockaddr *addr,   // 参数2:自己的IP地址和端口号
		socklen_t addrlen   // 参数3:地址的大小长度
	);
	// 返回值:成功:0  失败:-1
  1. 响应铃声
#include <sys/types.h>
#include <sys/socket.h>
	// 作用:创建一个未连接的队列,同时最多连接数为backlog
	int listen(int sockfd, int backlog);   // 参数1:套接字文件描述符   参数2:同时最多连接的客户端总数
	返回值:成功:0   失败:-1

说明:典型的服务器程序可以同时服务于多个客户端,当有客户端发起连接时,服务器调用accept()返回并接受这个连接,如果有大量的客户端发起连接而服务器来不及处理,尚未接受的客户端就处于连接等待状态,listen声明sockfd处于监听状态,并且只允许最多backlog个客户端处于连接状态,如果接受到更多的连接请求就忽略
查看系统默认的backlog:cat /proc/sys/net/ipv4/ 128

  1. 等待客户连接
#include <sys/socket.h>

	// 作用:等待客户连接
	int accept(
		int socket,   // 参数1;套接字描述符
		struct sockaddr *restrict address,   // 连接上来的客户端IP地址和端口号
		socklen_t *restrict address_len   // 长度
	);
	// 如果客户端连接上来,可以获得客户端的IP地址和端口号
	// 返回值:成功返回已连接的套接字描述符
  1. 接收数据
#include <sys/socket.h>

	ssize_t recv(
		int socket,   // 参数1:套接字描述符
		void *buffer,   // 参数2:接收到的数据存储位置
		size_t length,   // 参数3:接受的数据长度
		int flags   // 参数4:一般为0
	);
	// 返回值:成功返回接收到的字节数
	// 失败返回-1
	// 返回0 表示客户端断开连接

实现两个进程互相聊天案例:
客户端

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <pthread.h>

void* func(void* arg)
{
	int sock_fd = *((int*)arg);
	while(1)
	{
		char buf[1024] = "";
		recv(sock_fd, buf, sizeof(buf), 0);
		printf("recv: %s\n", buf);
	}
}


int main(int argc, char const *argv[])
{

	// 买手机
	int sock_fd = socket(PF_INET, SOCK_STREAM, 0);
	if(-1 == sock_fd)
	{
		perror("socket");
		return -1;
	}

	// 打电话
	struct sockaddr_in serverAddr;
	serverAddr.sin_family=AF_INET;
	serverAddr.sin_port=htons(5000);//htons--->将主机端口号变成网络端口号 host to network short
	serverAddr.sin_addr. s_addr=inet_addr("192.168.1.153");//inet_addr将主机IP转换为网络IP
	connect(sock_fd, (struct sockaddr*)&serverAddr, sizeof(struct sockaddr_in));

	pthread_t tid;
	pthread_create(&tid, NULL, func, &sock_fd);

	while(1)
	{
		printf("data: ");
		char buf[1024] = "";
		scanf("%s", buf);
		send(sock_fd, buf, strlen(buf)+1, 0);
	}
	close(sock_fd);

	pthread_join(tid, NULL);

	return 0;
}

服务端

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <pthread.h>

void* func(void* arg)
{
	int newClientFd = *((int*)arg);
	while(1)
	{
		printf("data: ");
		char buf[1024] = "";
		scanf("%s", buf);
		send(newClientFd, buf, strlen(buf)+1, 0);
	}
}


int main(int argc, char const *argv[])
{

	// 买手机
	int sock_fd = socket(PF_INET, SOCK_STREAM, 0);
	if(-1 == sock_fd)
	{
		perror("socket");
		return -1;
	}

	// 绑定号码
	struct sockaddr_in ownAddr;
	ownAddr.sin_family=AF_INET;
	ownAddr.sin_port=htons(5000);//htons--->将主机端口号变成网络端口号 host to network short
	ownAddr.sin_addr. s_addr=inet_addr("192.168.1.153");//inet_addr将主机IP转换为网络IP
	bind(sock_fd, (struct sockaddr*)&ownAddr, sizeof(struct sockaddr_in));

	// 设置铃声
	listen(sock_fd, 5);

	// 等电话
	struct sockaddr_in clientAddr;
	int len = sizeof(struct sockaddr_in);
	int newClientFd = accept(sock_fd, (struct sockaddr*)&clientAddr, &len);
	if(-1 != newClientFd)
	{
		printf("已连接的客户端IP: %s 端口号: %d\n", inet_ntoa(ownAddr.sin_addr), ntohs(ownAddr.sin_port));
	}

	pthread_t tid;
	pthread_create(&tid, NULL, func, &newClientFd);

	while(1)
	{
		char buf[1024] = "";
		recv(newClientFd, buf, sizeof(buf), 0);
		printf("recv: %s\n", buf);
	}

	close(sock_fd);
	close(newClientFd);

	pthread_join(tid, NULL);
	return 0;
}