网络编程01

网络编程




一、网络编程大纲

1、计算机网络体系结构模型、网络编程专业术语(socket/IP/端口号)、通信时序图
2、传输层协议: TCP协议/UDP协议。(重点)
3、组播、广播、多进程并发服务器、多线程并发服务器,
4、网络编程IO模型:/阻塞IO /非阻塞IO(非阻塞IO轮训服务器) /多路复用(select多路复用服务器)
拓展知识点:API云接口,http协议

二、进程的通信方式

1、 管道
无名管道(只能作用亲缘关系(父子进程)) – pipe()
有名管道(任意两个进程) — mkfifo()
2、信号
发送信号 – kill()
捕捉信号 – signal()
3、IPC对象
消息队列—接收特征类型的数据 – ftok() msgget() msgsnd() msgrcv() msgctl()
共享内存 --> 双方进程可以同时对一片内存进行读写 shmget() shmat() shmdt() shmctl()
信号量 --> 不属于通信方式,只是一种互斥的量 semget() semop() semctl()

特点: 只能在同一台主机上内部通信,不能跨平台

三、套接字编程

1、特点

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

2、协议

1)概念
应用的角度出发,在不同的主机之间通信,双方都必须遵循的同一种规则。协议可理解为“规则”,是数据传输和数据的解释的规则。
假设,A、B双方欲传输文件。规定:
第一次,传输文件名,接收方接收到文件名,应答OK给传输方;
第二次,发送文件的尺寸,接收方接收到该数据再次应答一个OK;
第三次,传输文件内容。同样,接收方接收数据完成后应答OK表示文件内容接收成功。
由此,无论A、B之间传递何种文件,都是通过三次数据传输来完成。A、B之间形成了一个最简单的数据传输规则。双方都按此规则发送、接收数据。A、B之间达成的这个相互遵守的规则即为协议。
这种仅在A、B之间被遵守的协议称之为原始协议。当此协议被更多的人采用,不断的增加、改进、维护、完善。最终形成一个稳定的、完整的文件传输协议,被广泛应用于各种文件传输过程中。该协议就成为一个标准协议。最早的ftp协议就是由此衍生而来。
TCP协议注重数据的传输。http协议着重于数据的解释。
2)常见的协议
传输层 常见协议有TCP/UDP协议。
应用层 常见的协议有HTTP协议,FTP协议。
网络层 常见协议有IP协议、ICMP协议、IGMP协议。
网络接口层 常见协议有ARP协议、RARP协议。
TCP传输控制协议(Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议。
UDP用户数据报协议(User Datagram Protocol)是OSI参考模型中一种无连接的传输层协议,提供面向事务的简单不可靠信息传送服务。
HTTP超文本传输协议(Hyper Text Transfer Protocol)是互联网上应用最为广泛的一种网络协议。
FTP文件传输协议(File Transfer Protocol)
IP协议是因特网互联协议(Internet Protocol)
ICMP协议是Internet控制报文协议(Internet Control Message Protocol)它是TCP/IP协议族的一个子协议,用于在IP主机、路由器之间传递控制消息。
IGMP协议是 Internet 组管理协议(Internet Group Management Protocol),是因特网协议家族中的一个组播协议。该协议运行在主机和组播路由器之间。
ARP协议是正向地址解析协议(Address Resolution Protocol),通过已知的IP,寻找对应主机的MAC地址。
RARP是反向地址转换协议,通过MAC地址确定IP地址

3、历史

ARPAnet(阿帕网):
1)历史:
1958年美国总统艾森豪威尔向美国国会提出建立DARPA (Defense Advanced Research Project Agency),即国防部高级研究计划署,简称ARPA。1968年6月DARPA提出“资源共享计算机网络”(Resource Sharing Computer Networks),目的在于让DARPA的所有电脑互连起来,这个网络就叫做ARPAnet。
2)使用的协议:网络控制协议(Network Control Protoco,NCP)
3) 缺点:不能互联不同类型的计算机 和 不同类型的操作系统,同时也没有纠错功能
Internet(因特网):
1)由于ARPAnet网络的局限性,引入了TCP/IP协议。TCP/IP是Internet上所有网络和主机之间进行交流的标准连接协议。通常所说的TCP/IP协议实际上包含了大量的协议和应用,且由多个独立定义的协议组合在一起,因此,更确切地说,应该称之为TCP/IP协议簇。
TCP/IP协议: 传输控制协议/因特网互联协议
TCP协议: 用于检测网络中传输差错
IP协议: 负责不同的网络之间的通信
通俗讲: TCP负责发送传输问题,一旦有问题发出信号,要求重新传输,直到数据安全到达对方为止。
IP给每一台联网设备规定一个地址

四、网络应用程序设计模式

1、C/S模式

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

2、B/S模式

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

3、优缺点

对于C/S模式来说,其优点明显。客户端位于目标主机上可以保证性能,将数据缓存至客户端本地,从而提高数据传输效率。且,一般来说客户端和服务器程序由一个开发团队创作,所以他们之间所采用的协议相对灵活。可以在标准协议的基础上根据需求裁剪及定制。例如,腾讯公司所采用的通信协议,即为ftp协议的修改剪裁版。
因此,传统的网络应用程序及较大型的网络应用程序都首选C/S模式进行开发。如,知名的网络游戏魔兽世界。3D画面,数据量庞大,使用C/S模式可以提前在本地进行大量数据的缓存处理,从而提高观感。
C/S模式的缺点也较突出。由于客户端和服务器都需要有一个开发团队来完成开发。工作量将成倍提升,开发周期较长。另外,从用户角度出发,需要将客户端安插至用户主机上,对用户主机的安全性构成威胁。这也是很多用户不愿使用C/S模式应用程序的重要原因。
B/S模式相比C/S模式而言,由于它没有独立的客户端,使用标准浏览器作为客户端,其工作开发量较小。只需开发服务器端即可。另外由于其采用浏览器显示数据,因此移植性非常好,不受平台限制。如早期的偷菜游戏,在各个平台上都可以完美运行。
B/S模式的缺点也较明显。由于使用第三方浏览器,因此网络应用支持受限。另外,没有客户端放到对方主机上,缓存数据不尽如人意,从而传输数据量受到限制。应用的观感大打折扣。第三,必须与浏览器一样,采用标准http协议进行通信,协议选择不灵活。
因此在开发过程中,模式的选择由上述各自的特点决定。根据实际需求选择应用程序设计模式

五、计算机网络体系结构模型

1、概念

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

2、作用

封装数据和解析数据

3、分类

OSI(Open System Interconnection开放系统互联)参考模型
TCP/IP参考模型
在这里插入图片描述

4、OSI参考模型(七层模型)

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

口诀:物数网传会表应
注意:OSI模型非常臃肿,处理数据效率非常低,这个模型已经被TCP/IP协议模型所取代

5、TCP/IP协议模型(四层)

TCP/IP网络协议栈分为应用层(Application)、传输层(Transport)、网络层(Network)和链路层(Link)四层。如下图所示:

在这里插入图片描述
一般在应用开发过程中,讨论最多的是TCP/IP模型。链路层 也可以称之为 网络接口层

六、TCP/IP网络模型通信过程

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

在这里插入图片描述

2、数据包封装

传输层及其以下的机制由内核提供,应用层由用户进程提供(后面将介绍如何使用socket API编写应用程序),应用程序对通讯数据的含义进行解释,而传输层及其以下处理通讯的细节,将数据从一台计算机通过一定的路径发送到另一台计算机。应用层数据通过协议栈发到网络上时,每层协议都要加上一个数据首部(header),称为封装(Encapsulation),如下图所示:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
不同的协议层对数据包有不同的称谓,在传输层叫做段(segment),在网络层叫做数据报(datagram),在链路层叫做帧(frame)。数据封装成帧后发到传输介质上,到达目的主机后每层协议再剥掉相应的首部,最后将应用层数据交给应用程序处理

3、以太网帧格式

1、以太网的帧格式如下所示:
在这里插入图片描述
其中的源地址和目的地址是指网卡的硬件地址(也叫MAC地址),长度是48位(6个字节),是在网卡出厂时固化的。可在shell中使用ifconfig命令查看,“HWaddr 00:15:F2:14:9E:3F”部分就是硬件地址。协议字段有三种值,分别对应IP、ARP、RARP。帧尾是CRC校验码。以太网帧中的数据长度规定最小46字节,最大1500字节,ARP和RARP数据包的长度不够46字节,要在后面补填充位。最大值1500称为以太网的最大传输单元(MTU),不同的网络类型有不同的MTU,如果一个数据包从以太网路由到拨号链路上,数据包长度大于拨号链路的MTU,则需要对数据包进行分片(fragmentation)。ifconfig命令输出中也有“MTU:1500”。注意,MTU这个概念指数据帧中有效载荷的最大长度,不包括帧头长度

4、ARP数据报格式

在网络通讯时,源主机的应用程序知道目的主机的IP地址和端口号,却不知道目的主机的硬件地址,而数据包首先是被网卡接收到再去处理上层协议的,如果接收到的数据包的硬件地址与本机不符,则直接丢弃。因此在通讯前必须获得目的主机的硬件地址。ARP协议就起到这个作用。源主机发出ARP请求,询问“IP地址是192.168.0.1的主机的硬件地址是多少”,并将这个请求广播到本地网段(以太网帧首部的硬件地址填FF:FF:FF:FF:FF:FF表示广播),目的主机接收到广播的ARP请求,发现其中的IP地址与本机相符,则发送一个ARP应答数据包给源主机,将自己的硬件地址填写在应答包中。
每台主机都维护一个ARP缓存表,可以用arp -a命令查看。缓存表中的表项有过期时间(一般为20分钟),如果20分钟内没有再次使用某个表项,则该表项失效,下次还要发ARP请求来获得目的主机的硬件地址。

ARP数据报的格式如下所示:
在这里插入图片描述
源MAC地址、目的MAC地址在以太网首部和ARP请求中各出现一次,对于链路层为以太网的情况是多余的,但如果链路层是其它类型的网络则有可能是必要的。硬件类型指链路层网络类型,1为以太网,协议类型指要转换的地址类型,0x0800为IP地址,后面两个地址长度对于以太网地址和IP地址分别为6和4(字节),op字段为1表示ARP请求,op字段为2表示ARP应答

5、IP数据报格式

在这里插入图片描述
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数据段格式

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

七、传输层协议

TCP协议(打电话)

1、概念:
用来检测网络传输中差错的传输控制协议 transmission control protocol。 是一种面向连接的传输层协议,它能提供高可靠性通信(即数据无误、数据无丢失、数据无失序、数据无重复到达的通信)
2、适用场合:
1)、对传输质量要求较高,以及传输大量数据的通信
2)、在需要可靠数据传输的场合,通常使用TCP协议
3)、QQ等即时通讯软件的用户登录账户管理相关的功能,通常采用TCP协议

UDP协议(信)

1、概念
UDP(User Datagram Protocol)用户数据报协议。是不可靠的无连接的协议。在数据发送前,因为不需要进行连接,所以可以进行高效率的数据传输。
2、适用场合:
1)发送小尺寸数据
2)适用于广播/组播式通信
3) QQ等即时通讯软件的点对点文本通讯以及音视频通讯 常采用 UDP协议
4)网络多媒体服务中通常采用UDP方式进行实时数据传输

八、网络编程中几个重要概念

1、socket --> 套接字

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

在这里插入图片描述
在网络通信中,套接字一定是成对出现的。一端的发送缓冲区对应对端的接收缓冲区。我们使用同一个文件描述符指向发送缓冲区和接收缓冲区。
TCP/IP协议最早在BSD UNIX上实现,为TCP/IP协议设计的应用层编程接口称为socket API
在这里插入图片描述
3)特点

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

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

套接字:     int sockfd = socket(TCP协议);   --> 得到TCP协议套接字
            int sockfd = socket(UDP协议);   --> 得到UDP协议套接字
普通文件:  int fd = open(普通文件);   --> fd就是文件描述符

2、IP地址

1)概念
用来标识网络中不同的主机。通信必须要具有一个IP地址
2)分类
IPV4地址—32位 IPV6地址 —128位
3)如何表示
常常以点分制"192.168.8.108"
4)数据包中都必须要包含目的IP地址,源IP地址。路由器依赖于此信息作为数据选择路由

3、端口号(16位)

标识同一台主机内 不同的应用程序

 网络
主机1  -----------------------------------主机2        

qq                    qq
微信                        微信
......
          Jack.c       -->      Rose.c        
IP地址    192.168.8.100          192.168.8.108       --->  双方必须在相同的局域网
端口号        50001                 50001            --->  双方都必须使用相同的端口号


端口号占用:
    1)系统占用端口号: 1 - 1023  (用户不能再次使用端口号进行通信)
    2)可用端口:  1024 - 65535

4、字节序

1)概念
一个多字节存储单位的低地址存储数据的高有效位 还是 低有效 位,说白了,也就是数据在计算机内存中以什么样的方式存储
2)分类
小端字节序:数据的低有效位 存储 在 内存中的 低地址
大端字节序:数据的低有效位 存储 在 内存中的 高地址
3)为了避免不同类别主机之间在数据交换时由于对于字节序的不同而导致的差错,引入了网络字节序(大端)。也就是统一规定所有主机通过网络发送数据包时转为大端序,也就是网络字节序。
内存地址: 低地址 -------------------------> 高地址

  "hello"
x86平台     ----->   ARM 

小端字节序:  低字节存放在低地址               1   2   3   4
大端字节序:  高字节存放在低地址               4   3   2   1
 
本地字节序:  取决于主机本身  
网络字节序:  一定是大端字节序

大端还是小端是由CPU架构+系统来决定。

总结一下: 无论本身是大端还是小端,都需要把自身的字节序转换为网络字节序,即大端;才能在网络中传输数据。大端还是小端是由CPU架构+系统来决定

九、TCP通信

1、通信过程

在这里插入图片描述

2、客户端API

1、建立套接字 socket

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int socket(int domain, int type, int protocol);

函数作用
建立套接字,返回套接字文件描述符
函数参数
domain : 你要选择 哪一种 地址族
PF_INET / AF_INET Ipv4 网络协议 (都可以用)
PF_INET6/AF_INET6 Ipv6 网络协议
type: 你 要选择 哪一种 协议
SOCK_STREAM 选择TCP ----流式 套接字
SOCK_DGRAM 选择UDP ----数据报套接字
protocol : 传0表示使用默认协议
返回值
成功返回 套接字文件描述符 sockfd
失败返回 -1
2、发起连接 -拨打电话

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

函数参数
sockfd --> 套接字文件描述符
addr —对方的IP地址 和 端口号
addrlen —地址的长度 sizeof(struct sockaddr_in )
返回值:
成功:0
失败:-1

struct sockaddr      ---旧的结构体
{
    unsigned short int sa_family;   ---IP地址   端口号  、  协议
    unsigned char sa_data[14];
};
IPV4结构体
struct sockaddr_in

在这里插入图片描述

struct sockaddr_in   serverAddr;
serverAddr.sin_family = PF_INET ;
serverAddr.sin_port =   htons (5000)  ;  //    host   to    network   short
serverAddr.sin_addr.s_addr =  inet_addr("192.168.1.120");   //主机 IP地址  ---》网络IP地址    inet_addr
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
char *inet_ntoa(struct in_addr in);   //将网络IP 转成  主机IP

参数:
cp —》主机IP
返回值 :
返回 网络IP

3、发送数据-------write send

#include <sys/types.h>
#include <sys/socket.h>
ssize_t send(int sockfd, const void *buf, size_t len, int flags);

作用
用于网络中发送数据
参数
sockfd: 套接字文件描述符
buf : 你要发送的数据
len : 你要发送数据的大小 ,以 字节为单位
flags : 一般默认为 0
返回值
成功返回发送的字节数
失败返回 -1

4、关闭文件–close

3、服务器端API

1、绑定 bind

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

作用:
绑定自己的IP地址 和 端口号
参数
sockfd : 套接字文件描述符
addr : 自己的IP地址和端口号
addrlen : 地址的大小长度
返回值
成功 0
失败 -1
2、设置铃声

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int listen(int sockfd, int backlog);

作用
创建 一个 未连接 队列 ,同时最多连接的客户端 总数 backlog
参数
sockfd : 套接字文件描述符
backlog :同时 最多支持连接上来的客户端总数
返回值
成功返回 0
失败返回 -1
说明
典型的服务器程序可以同时服务于多个客户端,当有客户端发起连接时,服务器调用的accept()返回并接受这个连接,如果有大量的客户端发起连接而服务器来不及处理,尚未accept的客户端就处于连接等待状态,listen()声明sockfd处于监听状态,并且最多允许有backlog个客户端处于连接待状态,如果接收到更多的连接请求就忽略
3、等待客户端的连接 --accept

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

作用
等待客户端的连接
参数
sockfd: 套接字文件描述符
addr : 连接上来的客户端的IP地址和端口号
addrlen : 长度
//如果客户端连接上来了,可以获取到连接上来的客户端的IP地址和端口号
返回值:
成功返回 已连接的套接字文件描述符
说明:指针地址的写法
socklen_t addrlen1 = sizeof(struct sockaddr_in);
scoket_client = accept(scoket_fd, (struct sockaddr *)&client_addr, &addrlen1);

4、接收数据 recv

#include <sys/types.h>
#include <sys/socket.h>
ssize_t recv(int sockfd, void *buf, size_t len, int flags);

参数
sockfd: 已连接的套接字文件描述符 connectfd
buf : 接收到的数据存储到这里
len : 接收数据的大小
flags : 一般设置为 0
返回值:
成功返回 接收到的字节数
失败返回 -1
返回 0 表示 客户端断开连接

代码说明:
服务器代码:

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

/*
    服务端
*/

#define SERVER_IP "192.168.1.67"
#define SERVER_PORT 60000

int main(int argc, char **argv)
{
    // 建立套接字---socket
    int scoket_fd;
    // SOCK_STREAM--TCP的协议
    scoket_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (scoket_fd < 0)
    {
        perror("socket fail");
        return -1;
    }

    // 所以设置端口号可以复用,这两条语句放在 绑定bind 之前
    int optval = 1;
    setsockopt(scoket_fd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));

    int ret;
    // 填充服务器的ip和端口号----新结构体
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;                   // Ipv4 网络协议
    server_addr.sin_port = htons(SERVER_PORT);          // 本机端口转换为网络端口host to net short
    server_addr.sin_addr.s_addr = inet_addr(SERVER_IP); // 本机ip转换为网络ip
    socklen_t addrlen = sizeof(struct sockaddr_in);

    // 绑定本机ip和端口号(必须要有)---bind
    ret = bind(scoket_fd, (struct sockaddr *)&server_addr, addrlen);
    if (ret < 0)
    {
        perror("bind fail");
        return -1;
    }
    printf("bind success[%s][%d]\n",SERVER_IP,SERVER_PORT);

    // 监听---listen
    ret = listen(scoket_fd, 20); // //20表示同时连接上来的客户端的最大数量
    if (ret < 0)
    {
        perror("listen fail");
        return -1;
    }

    // 接收客户端的链接---accept
    int scoket_client; // 服务器给客户端发送数据的套接字,不是scoket_fd
    struct sockaddr_in client_addr; //用来接收客户端的ip和端口
    socklen_t addrlen1 = sizeof(struct sockaddr_in);
    scoket_client = accept(scoket_fd, (struct sockaddr *)&client_addr, &addrlen1);
    if (scoket_client < 0)
    {
        perror("accept fail");
        return -1;
    }
    char *ip = inet_ntoa(client_addr.sin_addr) ;//将网络ip转换为本机ip
    int port = ntohs(client_addr.sin_port); //将网络端口转本机端口
    printf("新的客户端上线:[%s][%d]\n",ip,port);

    // 与客户端之间交互数据---send/recv
    char buf[1024] = {0};
    ret = recv(scoket_client, buf, sizeof(buf), 0);
    printf("[%s][%d]recv buf:%s ret:%d\n", ip,port,buf, ret);

    // 关闭套接字---close
    close(scoket_fd);

    return 0;
}

客户端代码:

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

/*
    客户端
*/
// #define SERVER_IP "192.168.1.67" //ubuntu的服务器的ip
#define SERVER_IP "192.168.1.2" //windows的服务器的ip
#define SERVER_PORT 60000

int main(int argc, char **argv)
{
    // 建立套接字---socket
    int scoket_fd;
    // SOCK_STREAM--TCP的协议
    scoket_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (scoket_fd < 0)
    {
        perror("socket fail");
        return -1;
    }

    //绑定本机ip和端口---客户端可有可无

    int ret;
    // 填充服务器的ip和端口号----新结构体
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;                   // Ipv4 网络协议
    server_addr.sin_port = htons(SERVER_PORT);          // 本机端口转换为网络端口host to net short
    server_addr.sin_addr.s_addr = inet_addr(SERVER_IP); // 本机ip转换为网络ip
    socklen_t addrlen = sizeof(struct sockaddr_in);

    // 连接服务器---connect
    // 注意:要将新结构体的指针类型转换为旧结构体的指针类型
    ret = connect(scoket_fd, (struct sockaddr *)&server_addr, addrlen);
    if (ret < 0)
    {
        printf("connect server fail\n");
        return -1;
    }

    // 与服务器之间交互数据---send/recv
    ret = send(scoket_fd, "hao hai yo", strlen("hao hai yo"), 0);
    printf("send ret:%d\n", ret);

    // 关闭套接字---close
    close(scoket_fd);

    return 0;
}

总结

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值