网络通讯TCP和UDP的编程流程
来源:互联网 发布:抽奖算法 奖池 c 编辑:程序博客网 时间:2024/06/06 02:38
我们介绍过进程间通讯的方式有:管道、信号量、共享内存、消息队列,但这些通讯方式都是在本地上通讯的,那么在现实生活中还有一种通信方式,不同主机上的两个进程之间的通讯,是通过socket套接字来完成的。
比如,生活中聊天软件QQ,你和好友之间的聊天,就是网络通讯。你是客户端,好友也是客户端,那么你们之间能够通讯是因为中间还有个服务器在工作。
服务器:提供数据的为服务器
客户端:获取数据的为客户端
在计算机中要做到有条不紊的交换数据,就必须遵守一些实现约定好的规则。这些规则明确规定了所交换的数据的格式以及有关的同步问题。
所以我们有了网络协议,网络协议有TCP/IP UDP/IP
协议的选择: TCP 的特点: 面向连接 可靠传输 流式服务
UDP的特点: 无连接 不可靠 数据报服务
这里我们主要说说TCP和UDP的编程流程
TCP的编程流程:
server(服务器): socket bind listen accept recv/send close
client (客户端): socket /*bind*/ connect recv/send close
1、服务器端:
(1)创建socket
socket ,在linux下一切都是文件,socket就是可读、可写、可控制、可关闭的文件描述符。
#include<sys/types.h>
#include<sys/socket.h>
int socket(int domain, int type, int protocol)
domain 参数:系统使用的是哪个底层协议族。TCP/IP协议族,参数设置为PF_INET
UNIX本地域 ,参数设置为PF_UNIX
type参数:指定服务类型 ,服务类型就是流服务或者数据报服务,TCP协议使用流服务SOCK_STREAM,
UDP使用数据报服务SOCK_DGRAM.
protocol参数:是在前面爱那个参数构成的协议下,再选择一个具体的协议。我们一般设置为0,表示使用默 认协议。
函数成功返回一个socket文件描述符,失败返回-1.
(2)bind (绑定)
创建socket时,我们给它指定了地址族,但未指定使用该地址族中的哪个具体socket地址。
将一个socket 与socket地址绑定称为给socket命名,在服务器程序中我们需要命名socket,因为只有命名后服务器才能知道该如何连接它。客户端通常不需要命名,采用匿名方式,使用操作系统自动分配的socket地址。
#include<sys/types.h>
#include<sys/socket.h>
int bind (int sockfd, const struct sockaddr* my_addr, int addrlen)
sockfd就是socket函数的返回值;
参数2: sockaddr:指定IP地址和端口号
它是个结构体: struct sockaddr_in
{
sa_family_t sin_family;//地址族,AF_INET
u_int16_t sin_port;//端口号,要用网络字节序表示
struct in_addr sin_addr;//IPV4 地址结构体
}
struct in_addr
{
u_int32_t s_addr;}//IPV4地址,要用网络字节序来表示
参数3:addlen指出该socket地址的长度,也就是struct sockaddr_in结构体的长度。
成功返回0,失败返回-1,并设置errno。
(3)listen(监听)
socket被命名之后,还不能马上被接受客户连接,我们需要使用如下系统调用来创建一个监听队列以存放待处理的客户连接。
#include<sys/socket.h>
int listen(int socket ,int size);
sockfd就是socket函数的返回值
size 参数提示内核监听队列的最大长度,如果监听队列的长度超过size,服务器将不受理新的客户连接,客户端也将收到ECONNREFUSED错误信息
成功返回0,失败返回-1,并设置errno。
(4)accept(接受连接)
系统调用从listen队列中接受一个连接:
#include<sys/types.h>
#include<sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, int *addrlen)
accept成功时返回一个新的连接socket,该socket唯一地标识了被接受的这个连接,服务器可通过读写该socket来与被接受连接对应的客户端通讯。
recv( socket,void *buff,int size,int flag)
send(socket,void* buff,int size)
(6) close(关闭连接)
#include<unistd.h>
int close(int fd);
fd参数是待关闭的socket,不过,close系统调用并非总是立即关闭一个连接,而是将 fd的引用计数减一,只有当fd的引用计数为0时,才真正关闭连接。
如果无论如何都要立即终止连接(而不是将fd的引用计数减一),可以使用shutdown系统调用
#include<sys/socket.h>
int shutdown(int sockfd,int howto)
howto参数决定了shutdown的行为: 1、SHUT_RD 关闭读
2、SHUT_WR 关闭写
3、SHUT_RDWR 同时关闭读和写
2、客户端
socket /*bind*/ connect recv/send close
发起请求的是客户端,那么客户端是不需要监听来创建一个等待处理连接的队列的,也没有accept了。
所以 1、创建socket与前面服务器的是一样的。
2、客户端通常不需要命名,采用匿名方式,使用操作系统自动分配的socket地址。所以bind我们可以添加,也可以不添加。
3、connect(发起连接)
#include<sys/types.h>
#include<sys/socket.h>
int connect(int sockfd, const sockaddr *serv_addr, int addrlen)
serv_addr 是指服务器监听的socket地址,addrlen指定这个地址的长度。
connect 成功时返回0,一旦成功连接,sockfd就唯一标识了这个连接,客户端就可以通过读写sockfd来与服务器通信。
失败返回-1,并设置errno。
两种最常见的errno:
1、ECONNREFUSED,目标端口不存在,连接被拒绝。
2、ETIMEDOUT,连接超时。
4、recv/send close 同服务器一样。
二、UDP的编程流程:
服务器:socket bind recvfrom/sendto close
客户端:socket sendto/recvfrom close
因为UDP的特点是无连接的,所以就没有监听和接受或发起连接的过程了。
前面已经说过socket 和 bind ,那么就不再说了,创建socket的时候协议要变成SOCK_DGRAM;
#include<sys/types.h>#include<sys/socket.h>int recv(int sockfd,void *buf,int len,int flags)int send(int sockfd,const void *buf,int len,int flags)
#include<stdio.h>#include<unistd.h>#include<assert.h>#include<string.h>#include<stdlib.h>#include<sys/types.h>#include<sys/socket.h>#include<arpa/inet.h>void main(){int sockfd=socket(AF_INET,SOCK_DGRAM,0); assert(sockfd!=-1); struct sockaddr_in cli, ser; ser.sin_family=AF_INET;ser.sin_port=htons(5000);ser.sin_addr.s_addr=inet_addr("127.0.0.1");int res=bind(sockfd,(struct sockaddr*)&ser,sizeof(ser)); assert(res!=-1);while(1){ char buff[128]={0}; int len=sizeof(cli); int recv=recvfrom(sockfd,buff,127,0,(struct sockaddr*)&cli,&len); assert(recv!=-1); printf("addr::%s,port::%d\n",inet_ntoa(cli.sin_addr),ntohs(cli.sin_port)); printf("%s\n",buff); sendto(sockfd,"OK",2,0,(struct sockaddr*)&cli,len); } close(sockfd);}
#include<stdio.h>#include<unistd.h>#include<assert.h>#include<string.h>#include<stdlib.h>#include<sys/types.h>#include<sys/socket.h>#include<arpa/inet.h>void main(){int sockfd=socket(AF_INET,SOCK_DGRAM,0); assert(sockfd!=-1); struct sockaddr_in cli, ser; ser.sin_family=AF_INET;ser.sin_port=htons(5000);ser.sin_addr.s_addr=inet_addr("127.0.0.1");char buff[128]={0}; int len=sizeof(ser); sendto(sockfd,"hello world",12,0,(struct sockaddr*)&ser,len);int recv=recvfrom(sockfd,buff,127,0,NULL,NULL);printf("%s\n",buff);}
- 网络通讯TCP和UDP的编程流程
- TCP和UDP网络通讯的区别及实现方式
- TCP和UDP网络通讯的区别及实现方式
- TCP和UDP网络通讯的区别及实现方式
- 17、TCP和UDP网络通讯的区别及实现方式
- TCP和UDP网络通讯的区别及实现方式
- TCP和UDP网络通讯的区别及实现方式
- 【黑马程序员】Java基础12:UDP和TCP的网络通讯
- 网络通讯选择TCP或UDP的指导方针
- 网络通讯TCP/UDP
- 网络通讯方式 ------ TCP、UDP
- TCP UDP协议网络通讯
- UDP网络通讯编程
- TCP和UDP套接字编程基本流程
- 详解网络通讯Socket、TCP、UDP
- TCP和UDP编程
- Android 网络通讯 socket tcp/ip udp http之间的关系
- 基于socket的TCP和UDP编程
- SMTP抓包分析
- Web 开发的安全核对清单简要
- 都是服务器多IP惹的祸
- 关于jQuery中的attr和prop
- C/C++
- 网络通讯TCP和UDP的编程流程
- AGC 011
- 99乘法表
- Libcurl 表单提交模式(POST 模式)
- 简单排序算法:冒泡排序(交换排序)
- 计算机视觉识别简史:从 AlexNet、ResNet 到 Mask RCNN
- Cordova插件(语音识别--科大讯飞)
- 商业web 漏洞扫描神器———AWVS篇基础
- mysql启动命令