TCP协议
TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议,而与TCP相对应的UDP协议则是无连接的、不可靠的协议(但传输效率比TCP高)。
在TCP/IP协议中,TCP协议提供可靠的连接服务,连接是通过三次握手进行初始化的:首先A发送信息给B,B在收到A的信息后给A发信息告诉A自己已经收到了信息,A收到信息后再给B发信息告诉B他已经知道B收到消息了。
TCP通过四次挥手来确保双端都断开了联系:
1. A先向B发送一个终止的信号并且进入状态1。
2.B收到A的终止信号后向A发一个信号表明自己已经收到了终止信号,收到B发出的信号的A进入状态2。
3.在B把数据传输完以后向A发送一个终止信号,请求关闭连接。
4.A在收到B的终止信号后向B发一个信号告诉B可以关机了,然后A等一段时间后关闭链接(处理B重发的数据),B在收到A的可以关闭连接的信号后,关闭连接。
Socket通信的基本流程
1.Socket函数
socket函数用于创建一个新的socket,socket函数用于客户端和服务器,单个程序最多可以打开1023个socket。
函数定义:
SOCKET socket(int af, int type, int protocol);
//af: 地址协议族 (ipv4和ipv6)
//type:传输协议类型(流式套接字和数据报套接字)
//protocol:使用具体的某个传输协议
ipv4是由四个字节组成,格式:xxx.xxx.xxx.xxx(0<x<255)每个x都是十进制数
ipv4 由16个字节组成,格式:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx每个x都是16进制的数
用法如下所示:
SOCKET fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
//使用ipv4,流式套接字,TCP协议
2.主机字节序和网络字节序
字节顺序是指占内存多于一位的字节类型的数据在内存中存放的顺序,一个32位整数由4个字节组成。储存这些字节有两个方法: 大端存储和小端存储。大端存储是指将高字节数据存储在起始地址,小端存储是将低字节数据储存到起始地址,如数据ox123456789
12 | 34 | 56 | 78 |
78 | 56 | 34 | 12 |
网络字节序是由TCP/IP中规定的一种数据表达格式,主机字节序是由CPU决定的。一般网络字节序是大端存储,主机字节序是小端存储,需要使用htons()函数进行转换。
htons(PORT)//把本地字节序转化成网络字节序
3.sokcet中的结构体
struct sockaddr
{
unsigned short sa_family; //地址类型,AF_xxx
char sa_data[14]; //14字节的端口和地址
};
struct sockaddr_in
{
short int sin_family; //地址类型
unsigned short int sin_port; //端口号
struct in_addr sin_addr; //地址
unsigned char sin_zero[8]; //为了保持与struct sockaddr一样的长度
};
struct in_addr
{
unsigned long s_addr; //地址
};
4.inet_ntoa函数
inet_ntoa函数将 (Ipv4) 因特网网络地址转换为因特网标准点分十进制格式的 ASCII 字符串。
struct sockaddr cy;
inet_ntoa(cy.sin_addr);
如果未发生错误,inet_ntoa将返回一个指向静态缓冲区的字符指针,该缓冲区包含标准 “.'” 表示法中的文本地址。否则,它将返回 NULL。
5.bind函数
bind函数将本地地址与套接字相关联。
bind(fd, &addr, sizeof(addr));
//第一个参数是标识未绑定套接字的描述符。
//第二个指向要分配给绑定套接字的本地地址的 sockaddr 结构的指针。
//第三个参数所指向的值的长度。
6.设置服务端socket的SO_REUSEADDR属性
服务端程序的端口释放后可能会处于TIME_WAIT状态,等待两分钟之后才能再被使用,SO_REUSEADDR是让端口释放后立即就可以被再次使用。
//设置SO_REUSEADDR选项。
int opt =1; unsigned int len = sizeof(opt);
setsockopt(listenfd,SoL_SOCKET,SO_REUSEADDR,&opt,len);
7.listen()、connect()、和accept()函数
(1)服务端在调用listen()之前,客户端不能向服务端发起连接请求的。
(2)服务端调用listen()函数后,服务端的socket开始监听客户端的连接。
(3)客户端调用connect()函数向服务端发起连接请求。
(4)在TCP底层,客户端和服务端握手后建立起通信通道,如果有多个客户端请求,在服务端就会形成一个已准备好的连接的队列(三次握手)。
(5)服务端调用accept()函数从队列中获取一个已准备好的连接,函数返回一个新的socket, 新的socket用于与客户端通信,listen的socket只负责监听客户端的连接请求。
8.send函数
send函数用于数据通过socket发给对端,如果出错send函数会返回-1。
send(int sockfd, const void *buf, size_t len,int flags);
//sockfd为己建立好连接的socket
//buf为需要发送数据内存的地址
//发送数据的长度
//填0就行
9.recv函数
recv函数用于接受对端socket发送过来的数据,失败是会返回-1,socket通道关闭返回0,如果对端的socket没有发送数据,recv函数就会一直等待,
recv(int sockfd, void *buf, size_t len, int flags);
//sockfd为已建立好连接的socket
//接收数据的内存地址
//接收数据的长度,不能超过buf的大小,否则会溢出
//填0就行