计算机网络(2)——TCP/UDP的编程流程

本次将计算机网络概述中所提到的知识点一一展开,作为个人学习记录和复习参考。
TCP编程流程
TCP特点:面向对象的,可靠的字节流服务。
明确我们TCP网络编程的上层协议如HTTP,FTP分别是浏览器方式和FTP传输文件的形式,这些都是具体的业务形式,目前不做考虑。
下层协议使用的就是TCP&IP&ARP/RARP。
TCP协议是基于C/S架构,故我们分为:
服务器端:socket bind listen accept(和谁绑定) recv/send close
客户端:socket connect(发起链接) recv/send close
#include <sys/types.h>//必要头文件
#include<sys/socket.h>
#include<arpa/inet.h>
#include<netinet/in.h>
以上是他们在编程时需要调用的函数
int socket(int domain,int type,int pro);//创建socket//返回-1出错,返回文件描述符正确//类比open
domain:使用的协议簇 AF_INET//地址族 PF_INET//协议族 宏替换的数值相同
type:具体的协议 TCP:SOCK_STREAM UDP:SOCK_DGRAM
pro:默认给0,比起协议簇更加具体的协议
在申请好连接用的套接字后,申请两个struct sockaddr_in ser,cli 并且memset清理内存一次,目前用不到全部数据,以免出错。再进行逐个赋值操作,详见后文

int bind(int sockfd,struct sockaddr *seraddr,int len);//绑定,通过IP和端口号就可以找到本程序,失败返回返回-1,成功返回0
bind相当于告知服务器所在的端口号和IP地址,进行绑定命名,可以从外部进行访问
这里一般会用res接受返回值,只作为判断成功与否的标志,后续不用
sockfd:文件描述符
seraddr: 服务器的IP地址的结构体地址
len:指定的服务器结构体长度
给出一个socket结构体,表示IP地址和端口号sockaddr_in
struct sockadder_in//socket专用地址
{
sa_family_t sin_family;//地址族
u_int16_t sin_port;//端口号
struct in_addr sin_addr;//IP地址
}
拓展:端口号是标识机器上运行的进程唯一端口
0-1024端口是系统预留,不允许普通用户使用,比如80 外部服务器专用端口与 21 FTP专用端口
1025-5000 网络服务预留
总计0-65535端口
struct in_addr
{
u_int32_t s_addr;
}
主机字节序:ARM,手机大端模式 Interx86也就是pc机,小端模式//高位存在高地址
网络字节序:在进行网络传输时都是使用大端模式,顺便一提,stack地址是从高到低,heap地址是从低到高,这和他们在4G虚拟空间的增长方向有关。

#include<netinet/in.h>
#include<arpa/inet.h>IP地址转化的函数头文件
主机网络字节序的转换
IP地址正好就是无符号类型的short数据(16位)转换数据有相关函数
in_addr_t inet_addr()
int inet_aton()
char* inet_ntoa()
在赋值端口号的时候需要转换数据类型。
他们的地址转化函数需要看看,一共有四种:
htonl 主机到网络 long int
htons 主机到网络 short int
ntohl 网络到主机 long int
ntohs 网络到主机 short int
bind出错的原因1.IP地址不对2.端口号被占用或没权限使用
输出的数据问题出错?最后一个buff[strlen(buff0-1)] = 0;的问题?

int listen(int sockfd,int size);//监听套接字
//启动监听//早期版本可能有多个客户同时发起链接,早期叫做监听队列
//现在分为两个部分:
已完成连接的队列//size+1就是指的是已经完成三次握手,但没有被应用程序处理的的队列,也就是最大监听队列的长度,超过则不再接受客户的连接,可以说是在内核区的一个缓存区,当被使用时,可以在队列中等待。
//正在进行三次握手的队列
正在完成连接的队列

int accept(int sockfd,struct sockaddr* cliaddr,int *len);//也会阻塞,等待链接,可以说这里是服务器等待客户端的请求,真正作用是从已经完成三次握手的链接中取出一个

cliaddr:来电显示,获取客户端IP地址,传入的socket类似于门铃的套接字
返回值返回的是维护本次连接的文件描述符,也就是说返回本次与客户端服务的真正套接字,和谁进行通讯功能,成功返回链接的文件标识符(也就是socket的一种),失败返回-1
在accept中最后一个参数为指针,在bind中最后一个参数为整数
下面的recv和send操作的fd是accept返回的fd
int recv(int fd//文件描述符,void *buff,size_t buffsize,int flag);//获取数据//会阻塞,成功返回获取的数据个数

flag:标记,其他和read很像,按照read的方式走
int send(int fd//文件描述符,void *buff,int datasize,int flag);//发送数据//给客户端发送数据//失败返回负值
其中flag一般都给0,不用。
每次连接后要一直传输数据,用while(1)循环,内部发送数据,while循环如果在accept之前,listen之后,那么就可以实现多客户访问服务器,每次对一个客户服务完应该close当前socket
close(int fd);//关闭本次链接,而不关闭门铃套接字(提供C/S链接的套接字),关闭的是accept返回的文件描述符,在全部结束时,可以关闭门铃套接字
以上为服务器端的代码实现。

客户端的代码实现
int socket(int domain,int type,int pro);//同服务器
int connect(int sockfd,(struct sockaddr*)seraddr,int len);//与哪一个服务器链接
返回:-1链接不成功
send(int cil,void * buff,int datasize,int flag);//客户端先发送请求,接受服务器的回复//
recv(int cil,void *buff,int buffsize,int flag);
close(int sockfd);
这个流程下来有缺点,无法同时响应多个客户端连接服务器,需要I/O复用才可响应多个客户端的连接

UDP编程流程
UDP的特点: 无连接 不可靠的 数据报服务
无连接:两个主机可以直接发送数据,不需要建立连接//发短信,不需要双方共同确认
不可靠:UDP不保证数据能够到达对端,不保证数据的完整性和正确性,乱序重复都不做处理。
数据报服务:数据是一段一段的,数据有截断区分。
UDP编程流程
和TCP有很大区别的收发函数:
int recvfrom(int sockfd,void * buff,size_t len,int flag,struct sockaddr*src_addr//获取到是从哪里来的数据的结构体,socklen * len);
因为是无连接的协议,所以每次传输数据必须携带来源方的端口和IP,也就是sockaddr结构体的内容,故sendto和recvfrom都需要填充sockaddr。

int sendto(int sockfd,void * buff,size_t len,int flag,struct sockaddr*dest_addr//获取到是从哪里来的数据的结构体,socklen len//最后这个是个整数,不是地址);

服务器端://一般来说是被动请求服务的主机,IP,端口固定
socket() bind() recvfrom() sendto()
服务器端的sendto()中//在最后一个len参数,是接受的长度客户端请求,最小值应该是cli本身的长度,也就是客户端的大小,当然可设置更大,但是没必要
sendto() close()
客户端://相反,主动请求服务的。
socket() bind() sendto() recvfrom() close()
客户端的recvfrom()//这里的后两个参数可以省略,因为在sockaddr中已经指明已经指明了要传送给的服务器名和长度
注意:在客户端编程时,需要判断argc是否大于3,因为无连接的情况,传输数据必须指定目标地址和端口
总是客户端先发起第一次数据
TCP在没有断开链接的时候,新开的客户端都无法联机(需要I/O复用才可以实现高并发处理链接)
而UDP因为没有连接,所以不管是谁发的,服务器都接受,只要客户端IP和端口写正确就行。

对于socket编程的一些总结
TCP在进行连接前Server需要绑定当前连接,也就是accept(cli)操作,在accept之前先申请门铃socket,和bind(ser),在bind前需要指明ser的内容,然后绑定本套接字和主机,使用完本次链接,应关闭accept所生成的实际连接的套接字
在需要输入(struct sockaddr*)&ser 之后紧接的是sizeof(ser) 而当出现(struct sockaddr*)&cli后,需要&len,这个len需要提前声明,是cli的长度,不声明会报错。

Client需要connect(&ser)操作,connect可以认为是与谁连接绑定,需要&ser,
在需要输入(struct sockaddr*)&ser 之后紧接的是sizeof(ser) ,一个整型,而当出现(struct sockaddr*)&cli后,需要&len,是一个指针,这个len需要提前声明,是cli的长度。

UDP在进行链接时不需要绑定链接,发送数据直接到网卡,接收数据直接从网卡读取,但是Server需要绑定端口IP,也就是bind(ser),在bind前需要指明ser的内容,接下来只要接收发送数据即可
但是UDP的发送和接受是sendto,recvfrom这两个的前四个参数和TCP一样,套接字,发送/接收的内容 发送/接收长度,flag值,最后多加两个参数,sendto是(struct sockaddr*)&ser/cli和sizeof(ser/cli), recvfrom是(struct sockaddr*)&cli/ser和&len
注意好后两个参数是给谁发的指针和谁收的指针就可以
另外在client中recvfrom的后两个参数可以忽略(貌似服务器的也可以忽略?服务器里的cli是由client实时传递过去的,,他不等待某一个具体的客户端,不能省略)
recvfrom最后取地址,sendto最后是取长度

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值