目录
网络:由若干结点和连接这些结点的链路组成,网络中的结点可以是计算机,交换机、 路由器等设备。
网络设备有:交换机、路由器、集线器
传输介质有:双绞线、同轴电缆、光纤

一个简单的网络示意图
把多个网络连接起来就构成了互联网。目前最大的互联网就是我们常说的因特网。

IP 地址就是给因特网上的每一个主机(或路由器)的每一个接口分配的一个在全世界 范围内唯一的标识符。IP 地址因其特殊的结构使我们可以在因特网上很方便地进行寻址。
IP地址的目的:资源共享、信息交互
IP 地址有分 IPV4 和 IPV6 两种类别格式,IPV4 是类似”A.B.C.D”的格式,它是 32 位 的,用“.”分成四个段,每个段是 8 个位(值为 0-255),用 10 进制表示。IPV6 地址是 128 位,格式类似”XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX:XXXX”,用“:“分成 8 个 段,每个段 16 个位,用 4 个 16 进制数表示。


查看ip命令—— windows:ipconfig ;Linux:ifconfig
127.0.0.1 自己电脑的ip,用于测试
在局域网中,硬件地址又称为物理地址或者 MAC 地址,长度 48 位,是固化在计算机适配器的 ROM 中的地址。因此假定连接在局域网上的一台计算机的适配器坏了而我们更换了 一个新的适配器,那么这台计算机的局域网的“地址”也就改变了,虽然这台计算机的地理 位置没有发生变化。其次,当我们把一个笔记本从一个城市带到另一个城市时,虽然地理位置改变了,但是电脑在局域网中的“地址”仍然不变。由此可见,局域网上某个主机的“地址”根本不能告诉我们这台主机位于什么地方。在网络上方便的寻找某个主机,还得依靠 ip 地址进行。
一个ip地址表示一台主机。主机运行一台进程,需要和其他主机联网通信,这就依赖于端口号。
端口可以理解为应用程序代号,是软件层次的,用于表示一个进程/应用程序,目的是方便查找,可以实现不同主机之间的通信。
ip+port->进程 ,想要实现进程间通信需要:自己的ip+端口与对方的ip+端口
网络协议就是一组网络规则的集合,是我们共同遵守的约定或标准。
常见的协议:
(面试会考:
分层的目的?哪些层?各层次功能?

OSI 的 7 层模型与 tcp/ip 协议族体系 4 层结构
物理层的主要任务就是确定与传输媒体的接口相关的一些特性,如机械特性、电气特性、物理特性和过程特性
主要功能是:通过各种控制协议,将有差错的物理信道变为无差错的、能可靠传输数据帧的数据链路。 在计算机网络中由于各种干扰的存在,物理链路是不可靠的。因此,这一层的主要功能 是在物理层提供的比特流的基础上,通过差错控制,使有差错的物理线路变为无差错的数据链路,即提供可靠的通过物理介质传输数据的方法。 该层通常又被分为介质访问控制(MAC)和逻辑链路控制(LLC)两个子层。
TCP/IP 协议体系结构中,数据链路层的功能描述为实现网卡接口的网络驱动程序,以 处理数据在物理媒介上的传输,不同的物理网络具有不同的电气特性,网络驱动程序隐藏了 这些细节,为上层协议提供了一个统一的接口。这一层主要关注的三个基本问题是:封装成帧,透明传输和差错检测。
网络层实现数据包的选路和转发。广域网或者说互联网通常使用众多分级的路由器来连接分散的主机或者局域网,因此,通信的两台主机一般不是直接相连的,而是通过多个中间 结点(路由器)连接的。网络层的任务就是选择这些中间结点,以确定两台主机之间的通信路径。同时,网络层对上层协议隐藏了网络拓扑连接的细节,使得在传输层和网络应用程序看来,通信的双方是直接相连的。
网络层最核心的协议是 IP 协议(Internet Protocol,因特网协议)。IP 协议根据数据包的目的 IP地址来决定何如投递它。如果数据包不能直接发送给目标主机,那么 IP 协议就是为它寻找一个合适的吓一跳路由器,并将数据包交付给该路由器来转发。多次重复这一过程,数据包最终到达目标主机,或者由于发送失败而被丢弃。可见,IP 协议使用逐跳的方式确定通 信路径。
网络层另外一个重要的协议是 ICMP 协议(因特网控制报文协议)。它是 IP 协议的重要 补充,主要用于检测网络连接。
IP 协议为上层协议提供无状态、无连接、不可靠的服务。
无状态是指通信双方不同步传输数据的状态信息,因此所有 IP 数据报的发送、传输和 接收都是相互独立、没有上下文关系的。这种服务最大的缺点是无法处理乱序和重复的 IP 数据报。虽然 IP 数据报头部提供了一个标识字段用以唯一标识一个 IP 数据报,但它是被用 来处理 IP 分片和重组的,而不是用来指示接收顺序的。无状态的优点是简单、高效。无须 为保持通信状态而分配一些内核资源,也无须再每次通信时携带状态信息。
无连接是指 IP 通信双方都不长久地维持对方的任何信息。这样,上层协议每次发送数 据的时候,都必须明确指定对方的 IP 地址。 不可靠是指 IP 协议不能保证 IP 数据报准确地到达接收端,它只是承诺尽最大努力。
IPV4 头部结构如下:

传输层为两台主机上的应用程序提供端到端的通信。与网络层使用的逐跳通信的方式不 同,传输层只关心通信的起始端和目的端,而不在乎数据包的中转过程。
传输层协议主要有三个:TCP 协议、UDP 协议和 SCTP 协议为应用层提供可靠的、面向连接的和基于流的服务。
TCP 协 议使用超时重传、确认应答等方式来确保数据包被正确的发送至目的端,因此 TCP 服务是 可靠的。使用 TCP 协议通信的双方必须先建立 TCP 连接,并在内核中为该连接维持一些必 要的数据结构,比如连接状态,读写缓冲区等。当通信结束时,双方必须关闭连接以释放这 些内核数据。TCP 服务是基于流的,基于流的数据没有边界(长度)限制,它源源不断地从 通信地一端流入另一端。发送端可以逐个字节地向数据流中写入数据,接收端可以逐个字节地将它们读出。
UDP 协议(用户数据报协议)则与 TCP 协议完全相反,它为应用层提供不可靠、无连接、基于数据报地服务。“不可靠”意味着 UDP 协议无法保证数据从发送端正确地传送到目 的端。如果数据在中途丢失,或者目的端通过数据校验发现数据错误而将其丢弃,则 UDP 协议只是简单地通知应用程序发送失败。因此,如果要使 UDP 协议可靠,那么应用程序通 常要自己处理数据确认、超时重传等逻辑。UDP 是无连接的,即通信双发不保持一个长久 的联系,因此应用程序每次发送数据都要明确指定 接收端的地址。基于数据报的服务,是相 对基于流的服务而言的。每次 UDP 数据报都有一个长度,接收端必须以该长度为最小单位 将其所有内容一次性读出,否则数据将被截断。
SCTP 协议(流控制传输协议)是一种相对较新的传输层协议,它是为了再因特网上传 输电话信号而设计的。这里暂时不讨论 SCTP 协议。
负责处理应用程序的逻辑。




TCP:面向连接,通讯前进行三次握手建立连接,是可靠的流式服务。
三次握手完成,链接才建立,才能send发送数据
i:32位序号
ACK:确认信息:i+1

四次挥手之后,链接断开。执行close之后,才会发送FIN

四次挥手也可以是三次挥手:在客户端close()后,服务器端立马收到也close,此时就是三次挥手
如下图,应用程序 A 要将数据”hello” 传给网络上另外一台主机上的应用程序 B。

- uint32_t htonl(uint32_t hostlong); // 长整型的主机字节序转网络字节序
- uint32_t ntohl(uint32_t netlong); // 长整型的网络字节序转主机字节序
- uint16_t htons(uint16_t hostshort); // 短整形的主机字节序转网络字节序
- uint16_t ntohs(uint16_t netshort); // 短整型的网络字节序转主机字节序
应用程序编程接口也称为系统调用接口,是应用程序直接调用操作系统的功能为自己服务的机制。大多数操作系统都是通过应用程序编程接口来支持应用程序运行的。
通过应用程序编程接口调用操作系统功能被称为系统调用。虽然系统调用与一般程序中的函数调用非常相似,只不过系统调用是将控制权传递给操作系统。当某个应用进程启动系统调用时,控制权就从应用程序传递给系统调用接口。
套接字编程接口给出了应用程序能够调用的一组函数——socket函数,每个函数完成一种与协议软件交互的一种操作,比如,用来创建套接字的socket函数,用来发送数据的end函数,用来和网络上接受数据的receive函数等。应用程序通过调用这些函数就可以达到利用网络进行通信的目的。
简单的说,套接字使用用程序调用网络协议进行通信的接口,具有通过网络收发数据的能力。
操作系统提供三种不同的套接字:
socket 网络编程接口中表示 socket 地址的是结构体 sockaddr,其定义如下:
- #include
- struct sockaddr
- {
- sa_family_t sa_family;
- char sa_data[14];
- };ket.h>
sa_family 成员是地址族类型(sa_family_t)的变量。地址族类型通常与协议族类型对 应。常见的协议族和对应的地址族如下图所示:

TCP/IP 协议族有 sockaddr_in 和 sockaddr_in6 两个专用 socket 地址结构体,它们分 别用于 IPV4 和 IPV6:
- /*
- sin_family: 地址族 AF_INET
- sin_port: 端口号,需要用网络字节序表示
- sin_addr: IPV4 地址结构:s_addr 以网络字节序表示 IPV4 地址
- */
-
- struct in_addr
- {
- u_int32_t s_addr;
- };
-
- struct sockaddr_in //bind的第二个参数类型
- {
- sa_family_t sin_family;//用于存放代表不同协议族地址的代码,internet协议族代码为AF_INET
- u_int16_t sin_port;//用于存放地址通信使用的16位端口号,以网络字节顺序表示
- struct in_addr sin_addr;//存放网络字节顺序表示的32位IP地址
- //将ip地址设置为INADDR_ANY表示将该套接字的IP地址由系统自己指定
- //如果希望系统自动分配端口号,就将port定义为0即可
- };
-
- struct in6_addr
- {
- unsigned char sa_addr[16]; // IPV6 地址,要用网络字节序表示
- };
-
- struct sockaddr_in6
- {
- sa_family_t sin6_family; // 地址族:AF_INET6
- u_inet16_t sin6_port; // 端口号:用网络字节序表示
- u_int32_t sin6_flowinfo; // 流信息,应设置为 0
- struct in6_addr sin6_addr; // IPV6 地址结构体
- u_int32_t sin6_scope_id; // scope ID,尚处于试验阶段
- };
通常,人们习惯用点分十进制字符串表示 IPV4 地址,但编程中我们需要先把它们转化 为整数方能使用,下面函数可用于点分十进制字符串表示的 IPV4 地址和网络字节序整数表 示的 IPV4 地址之间的转换:
- #include
- in_addr_t inet_addr(const char *cp); //字符串表示的 IPV4 地址转化为网络字节序
- int inet_aton(const char*cp,struct in_addr*inp);
- char* inet_ntoa(struct in_addr in); // IPV4 地址的网络字节序转化为字符串表示
inet_addr
作用:将点分十进制字符串表示的IP地址转换为32位无符号长整形数
inet_aton
将点分十进制的Ip地址转换为32位无符号长整形,并存放在参数inp指向的in_addr结构变量中。
inet_ntoa
将一个包含在inet_addr结构变量中的长整型ip地址转换为点分十进制形式
头文件
- 1. #include
- 2. #include
socket
int socket(int domain, int type, int protocol);
注意,服务器程序和客户程序创建套接字的用处不同,服务器创建的套接字是监听套接字,并不用于数据收发。客户端创建的套接字则有两个用途:一是向服务器发送连接建立请求,二是用于进行客户端的数据收发。
bind
socket()函数在创建套接字时并没有为创建的套接字分配地址,因此,服务器软件在创建了监听套接字后,还必须为它分配一个地址,该地址为套接字指定需要监听的端口号和IP地址。
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
listen
int listen(int sockfd, int backlog);
建立连接
服务器在将监听套接字设置为监听模式后,便可用accept函数接收客户端的连接请求,客户端则在创建套接字后调用connect函数发起连接建立过程,如下“三次握手”建立连接的过程:

accept
accpet函数只适用于面向连接的套接字,而且与listen一样只能由服务器端调用。
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
connect
int connect(int sockfd, const struct sockaddr *serv_addr, socklen_t addrlen);
int n=recv(c,buff,127,0);//期望接受的个数:127
if(n<=0)//n=0:对方关闭,不需要再阻塞,n=-1,返回错误
n=0是唯一判断对方关闭连接的条件
TCP 数据读写
recv()读取 sockfd 上的数据,buff 和 len 参数分别指定读缓冲区的位置和大小
send()往 socket 上写入数据,buff 和 len 参数分别指定写缓冲区的位置和数据长
- ssize_t recv(int sockfd, void *buff, size_t len, int flags);
- ssize_t send(int sockfd, const void *buff, size_t len, int flags);
UDP 数据读写
- ssize_t recvfrom(int sockfd, void *buff, size_t len, int flags,struct sockaddr* src_addr, socklen_t *addrlen);
- ssize_t sendto(int sockfd, void *buff, size_t len, int flags,struct sockaddr* dest_addr, socklen_t addrlen);
close
int close(int sockfd);
TCP 提供的是面向连接的、可靠的、字节流服务。TCP 的服务器端和客户端编程流程如 下:

listen()第二个参数n,在linux系统是已完成三次握手队列的长度。但实际能够存放的连接是n+1个。unix:已完成+未完成的。



- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- //服务器
- int main()
- {
- //创建套接字
- int sockfd=socket(AF_INET,SOCK_STREAM,0);//tcp:STREAM流协议
- if(sockfd==-1)
- {
- exit(0);
- }
- struct sockaddr_in saddr,caddr;//saddr服务器地址,caddr:客户端
-
- memset(&saddr,0,sizeof(saddr));//清空
- saddr.sin_family=AF_INET;
- saddr.sin_port=htons(6000);//端口,1024以内是知名端口,尽量自己选择较大的端口
- saddr.sin_addr.s_addr=inet_addr("127.0.0.1");//点分十进制转换为32位无符号长整形
-
- int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
- if(res==-1)
- {
- printf("bind failed\n");
- exit(0);
- }
- res=listen(sockfd,5);
- if(res==-1)
- {
- exit(0);
- }
- while(1)
- {
- socklen_t len=sizeof(caddr);
- int c=accept(sockfd,(struct sockaddr*)&caddr,&len);
- //sockfd:监听套接字;c:连接套接字
- if(c<0)
- {
- continue;
- }
- printf("accept c=%d\n",c);
-
- char buff[128]={0};
- int n=recv(c,buff,127,0);
- printf("recv(%d):%s\n",n,buff);
- send(c,"ok",2,0);
- close(c);
- }
- }
TCP 客户端代码 TCPClient.c:
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- //客户端
- int main()
- {
- //创建套接字
- int sockfd=socket(AF_INET,SOCK_STREAM,0);//tcp:STREAM流协议
- if(sockfd==-1)
- {
- exit(0);
- }
- struct sockaddr_in saddr;//服务器地址
- //清空
- memset(&saddr,0,sizeof(saddr));
- saddr.sin_family=AF_INET;
- saddr.sin_port=htons(6000);//端口
- saddr.sin_addr.s_addr=inet_addr("127.0.0.1");//点分十进制转换为32位无符号长整形
-
- int res=connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));//连接服务器
- if(res==-1)
- {
- printf("connect failed\n");
- exit(0);
- }
- printf("input:\n");
- char buff[128]={0};
- fgets(buff,128,stdin);
- send(sockfd,buff,strlen(buff)-1,0);
- memset(buff,0,128);
- recv(sockfd,buff,127,0);
- printf("buff=%s\n",buff);
- close(sockfd);
- exit(0);
- }
运行结构:

服务器端代码修改:
- while(1)
- {
- struct sockaddr_in caddr;
- socklen_t len=sizeof(caddr);
- int c=accept(sockfd,(struct sockaddr*)&caddr,&len);
- if(c<0)
- {
- continue;
- }
- printf("accept c=%d\n",c);
- //循环
- while(1)
- {
- char buff[128]={0};
- int n=recv(c,buff,127,0);//期望接受的个数:127
- if(n<=0)//n=0:对方关闭,不需要再阻塞,n=-1,返回错误
- //n=0是唯一判断对方关闭连接的条件
- {
- break;
- }
- //收到数据,打印出来并回复对象
- printf("recv(%d):%s\n",n,buff);
- send(c,"ok",2,0);
- close(c);
- }
- }
代码说明:
- int n=recv(c,buff,127,0);//期望接受的个数:127
- if(n<=0)//n=0:对方关闭,不需要再阻塞,n=-1,返回错误
- {...}
n=0是唯一判断对方关闭连接的条件
客户端代码修改:
- while(1)
- {
- printf("input:\n");
- char buff[128]={0};
- fgets(buff,128,stdin);
- if(strncmp(buff,"end",3)==0)
- {
- break;
- }
- send(sockfd,buff,strlen(buff)-1,0);
- memset(buff,0,128);
- recv(sockfd,buff,127,0);
- printf("buff=%s\n",buff);
- }
- close(sockfd);
- exit(0);
运行结果:

一个思考:
问:如果接收recv一次只读一个字符,客户端send一次发送了个"hello",请问服务器端接受情况为?
答:recv输出是h一次,e一次,l一次,l一次,o一次。服务器端一次发给客户端5个ok:"okokokokok",但客户端可能会只收到一个"ok",因为在接收缓冲区中存着没发过来,下次recv发数据后,就会收到上次没收到的的4个"ok"。同样,实际客户端每次发送的数据(假设“123”和“456”,他们都会缓冲区内放着123456,读走多少拿多少,剩下的就在缓冲区内等着。
问:如何看到发送缓冲区还有多少数据为发送?(面试会问)
答:命令netstat -natp
上面程序没法实现实现同时处理两个以上的客户端,必须一个客户端退出close之后,另一个客户端的连接才能被服务器accept连接上。用线程就可以解决该问题,实现多客户端同时连接服务区。
服务器端修改代码如下:
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- //服务器
- struct ArgNode
- {
- int c;
- };
-
- int socket_init()
- {
- //创建套接字
- int sockfd=socket(AF_INET,SOCK_STREAM,0);//tcp:STREAM流协议
- if(sockfd==-1)
- {
- exit(0);
- }
- struct sockaddr_in saddr,caddr;//saddr服务器地址,caddr:客户端
-
- memset(&saddr,0,sizeof(saddr));//清空
- saddr.sin_family=AF_INET;
- saddr.sin_port=htons(6000);//端口
- saddr.sin_addr.s_addr=inet_addr("127.0.0.1");//点分十进制转换为无符号整形
-
- int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
- if(res==-1)
- {
- printf("bind failed\n");
- exit(0);
- }
- res=listen(sockfd,5);
- if(res==-1)
- {
- exit(0);
- }
- return sockfd;
- }
- void *fun(void* arg)
- {
- struct ArgNode*p=(struct ArgNode*)arg;
- int c=p->c;
- while(1)
- {
- char buff[128]={0};
- int n=recv(c,buff,127,0);
- if(n<=0)
- {
- break;
- }
- printf("buff(%d):%s",c,buff);
- send(c,"ok",2,0);
- }
- close(c);
- printf("cli close\n");
- }
- int main()
- {
- int sockfd=socket_init();
- if(sockfd==-1)
- {
- exit(0);
- }
-
- while(1)
- {
- struct sockaddr_in caddr;
- socklen_t len=sizeof(caddr);
- int c=accept(sockfd,(struct sockaddr*)&caddr,&len);
- if(c<0)
- {
- continue;
- }
- printf("accept c=%d\n",c);
- //启动线程
- pthread_t id;
- struct ArgNode * p=(struct ArgNode*)malloc(sizeof(struct ArgNode));
- p->c=c;
- pthread_create(&id,NULL,fun,(void*)p);
- }
- close(sockfd);
- }
代码:
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- //服务器
- int socket_init()
- {
- //创建套接字
- int sockfd=socket(AF_INET,SOCK_STREAM,0);//tcp:STREAM流协议
- if(sockfd==-1)
- {
- exit(0);
- }
- struct sockaddr_in saddr,caddr;//saddr服务器地址,caddr:客户端
-
- memset(&saddr,0,sizeof(saddr));//清空
- saddr.sin_family=AF_INET;
- saddr.sin_port=htons(6000);//端口
- saddr.sin_addr.s_addr=inet_addr("127.0.0.1");//点分十进制转换为无符号整形
-
- int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
- if(res==-1)
- {
- printf("bind failed\n");
- exit(0);
- }
- res=listen(sockfd,5);
- if(res==-1)
- {
- exit(0);
- }
- return sockfd;
- }
-
- void recv_data(int c)
- {
- while(1)
- {
- char buff[128]={0};
- int n=recv(c,buff,127,0);
- if(n<=0)
- {
- break;
- }
- printf("buff=%s\n",buff);
- send(c,"ok",2,0);
- }
- close(c);
- }
-
- int main()
- {
- int sockfd=socket_init();
- if(sockfd==-1)
- {
- exit(0);
- }
-
- while(1)
- {
- struct sockaddr_in caddr;
- socklen_t len=sizeof(caddr);
- int c=accept(sockfd,(struct sockaddr*)&caddr,&len);
- if(c<0)
- {
- continue;
- }
- pid_t pid=fork();
- int socket_init();
- if(pid==-1)
- {
- close(c);
- continue;
- }
- if(pid==0)
- {
- recv_data(c);
- exit(0);
- }
- close(c);
- }
- }
运行结果:

注意,引入子进程,一定要注意处理僵死进程的问题,如下,通过输入“ps -ef | grep fork”命令查看当前进程情况:

其中可看出,当一个客户端与服务器主动输入“end”或者ctrl+c断开连接时,服务器会产生一个僵死进程,下面利用信号来解决该问题:
- #include
- #include
- void wait_child(int sig)
- {
- wait(NULL);
- }
- int main()
- {
- signal(SIGCHLD,wait_child);
- int sockfd=socket_init();
- ....
- }
signal(SIGCHLD,SIG_IGN);
服务器:创建套接字
接收客户端数据
UDP是:无连接 不可靠(发出去就不管了,成功与否不保障)效率高,实时性好

tcp和udp特点:
适用不同场景,互补
拿发送字节举例:
客户端发送"hello",服务器设置每次只收1字节时,会收到 'h',下次发"abc"收到'a',不会对上次没发完的数据进行保存,因为目的地址不确定是否一样。
代码:
服务器:
- #include
- #include
- #include
- #include
-
- //服务器
- int main()
- {
- //创建套接字
- int sockfd=socket(AF_INET,SOCK_DGRAM,0);//tcp:STREAM流协议
- if(sockfd==-1)
- {
- exit(0);
- }
- struct sockaddr_in saddr,caddr;//saddr服务器地址,caddr:客户端
-
- memset(&saddr,0,sizeof(saddr));//清空
- saddr.sin_family=AF_INET;
- saddr.sin_port=htons(6000);//端口,1024以内是知名端口,尽量自己选择较大的端口
- saddr.sin_addr.s_addr=inet_addr("127.0.0.1");//点分十进制转换为32位无符号长整形
-
- int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
- /*if(res==-1)
- {
- printf("bind failed\n");
- exit(0);
- }
- res=listen(sockfd,5);
- if(res==-1)
- {
- exit(0);
- }*/
- while(1)
- {
- int len=sizeof(caddr);
- /* int c=accept(sockfd,(struct sockaddr*)&caddr,&len);
- if(c<0)
- {
- continue;
- }
- printf("accept c=%d\n",c);*/
-
- char buff[128]={0};
- int n=recvfrom(sockfd,buff,127,0,(struct sockaddr*)&caddr,&len);
- printf("ip:%s,buff=%s\n",inet_ntoa(caddr.sin_addr),buff);
- sendto(sockfd,"ok",2,0,(struct sockaddr*)&caddr,sizeof(caddr));
- // close(c);
- }
- }
客户端:
- #include
- #include
- #include
- #include
- #include
- #include
- #include
-
- //客户端
- int main()
- {
- //创建套接字
- int sockfd=socket(AF_INET,SOCK_DGRAM,0);//tcp:STREAM流协议
- if(sockfd==-1)
- {
- exit(0);
- }
- struct sockaddr_in saddr;//服务器地址
- //清空
- memset(&saddr,0,sizeof(saddr));
- saddr.sin_family=AF_INET;
- saddr.sin_port=htons(6000);//端口
- saddr.sin_addr.s_addr=inet_addr("127.0.0.1");//点分十进制转换为
-
- while(1)
- {
- printf("input:\n");
- char buff[128]={0};
- fgets(buff,128,stdin);
- if(strncmp(buff,"end",3)==0)
- {
- break;
- }
- sendto(sockfd,buff,strlen(buff)-1,0,(struct sockaddr*)&saddr,sizeof(saddr));
- memset(buff,0,128);
- int len=sizeof(saddr);
- recvfrom(sockfd,buff,127,0,(struct sockaddr*)&saddr,&len);
- printf("buff=%s\n",buff);
- }
- close(sockfd);
- exit(0);
- }
运行结果:
