网络传输----TCP

问题描述

        TCP协议是UDP协议的改进版,TCP协议可以保证传输的可靠性,TCP协议的优势如下:

        1、TCP协议提供可靠的传输服务(不会出现错误、乱码、丢失、误码、重复)

        2、面向连接的传输方式

        3、一条TCP只能进行两个端点一对一通信,通信双方固定,就是连接的两个点。如果想实现多机则需要创建多条TCP。

        4、面向字节流传输(数据只有先后之分,也就是一个发数据一个收数据错开,不可能同时收发数据)半双工模式

        5、可靠传输:有拥塞控制、流量控制、超时重传。

        上面是一些TCP协议的优点,下面来介绍以下TCP通信如何确保数据的可靠的,它的传输形式又是怎么样的。

TCP协议传输数据的组成是        TCP头+用户数据      

  

 上图就是TPC的头,由很多部分构成:

源端口  16bit        发送数据方的端口号(也就是自己的),标识发送该TCP报文的应用程序

目标端口 16bit      接收方的端口号(发送到那的),标识接收该TCP报文的应用程序

序号        32bit       该TCP报文的数据的第一个字节的序号

确认号    32bit        这是接收收到数据,返回的内容〈TCP报文〉,标识接收到多少内容,如确认号为n,准确接收到n-1个字节

数据偏移:4bit        以四字节为单位,当前这个TCP报文距离整个TCP报文开始有多远(看TCP头有多大,向后偏移就多少)

保留:6bit        无用

确认ACK 1bit   只有确认ACK为1时,才表示确认号有效

同步:1bit        在建立连接时可以让客户端和服务端同步,只有在建立的时候才用

终止:1bit        在断开连接、释放TCP连接时需要将这位为1,平时为0

复位:1bit        复位TCP连接使用,

窗口:发送TCP报文对应接收方的窗口大小

校验和:用于校验信息是否正确

TCP通信

        TCP通信为了保证传输可靠,必须在通信双方间建立连接,只有建立了连接之后TCP通信才是可靠通信,且确定唯一的通信双方 。所以TCP要进行通信的双方操作就存在着区别,一个进程应该主动的去进行连接,另一个进程应该被动等待连接请求,然后建立连接进行通信,客户端的通信流程和服务端的通信流程不相同。

TCP通信客户端和服务端操作流程:

1.客户端

2.服务端

也就是如下操作流程,上面是案例描述:

在这里需要学习一些新的API函数:

1、请求建立连接(TCP协议属于面向连接方式进行通信,这样才提供可靠性)

#include <sys/types.h> /* See NOTES */ 
#include <sys/socket.h>

使用当前进程的套接字与某个服务端建立连接 建立连接:与服务端建立连接,把套接字与服务端建立连接

int connect(int socket, const struct sockaddr *addr,socklen_t addrlen);

参数1:客户端套接字,表示用客户端哪个套接字(ip、port)与服务端建立连接, 进行通信。

参数2:客户端套接字要与哪个服务端建立建立,结构体中是服务端的ip、prot。

参数3:结构体大小(参数2的结构体)

成功返回0    失败返回-1

2、监听套接字

服务端监视自己的ip、port,看是否有客户端连接 等待客户端的连接请求,只要做监视操作,就让内核创建一个队列,把所有的客户端连接请求都放在队列 去(作为等待,等待接受),之后如果也有客户端连接请求,也会放入等待队列.

int listen(int sockfd, int backlog);

参数1:要监视的套接字,服务端的网络套接字

参数2:等待客户端个数,也就是允许最多连接多少个。好确定队列的大小

成功返回0    失败返回-1

3、接收客户端连接

从之前listen监听等待的客户端连接中,取出第一个,进行接受,进行通信,如果在刚才listen监视队 列中没有客户端连接请求,就会阻塞等待,一定要取一个客户端连接请求进行通信.

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

参数1:监视的套接字

参数2:客户端的ip、port

参数3:结构体大小

成功:返回客户端进行通信的套接字文件描述符,之前listen的套接字被用来做监视作用
失败:返回-1

示例:

        客户端

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

int main()
{
    int sockfd = socket(AF_INET,SOCK_STREAM,0)

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(8888);
    addr.sin_addr.a_addr = inet_addr("xxx.xxx.xxx.xxx");    自己的网络地址
    
    bind(sockfd,(struct sockaddr *)&addr,sizeof(addr));    将自己ip、port绑定到sockfd上
    
    struct sockaddr_in serveraddr;    要连接的服务端信息
    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(1111);
    serveraddr.sin_addr.a_addr = inet_addr("xxx.xxx.xxx.xxx");    要连接的的网络地址

    //sockfd就是当前进程的套接字(有当前进程网络信息),创建连接,失败打印
    if( connect(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr)) < 0 )
	{
		perror("connect error\n");
		return -1;
	}
    
    char buf[20];
    while(1)
    {
        //要进行做的事情
        read(sockfd,buf,20);    //读取服务端传过来的数据
        write(sockfd,buf,20);   //往服务端写内容
    }
}

服务端

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

int main()
{
    int sockfd = socket(AF_INET,SOCK_STREAM,0);

    struct sockaddr_in addr;//网络信息结构体    //服务端的网络信息
	addr.sin_family = AF_INET;
	addr.sin_port = htons(9999);
	addr.sin_addr.s_addr = inet_addr("xxx.xxx.xxx.xxx");

    bind(sockfd,(struct sockaddr *)&addr,sizeof(addr));

    listen(sockfd,10);//不会阻塞,告诉内核,要监视这个ip、port,把客户端的连接请求存放等待
   
    //接受客户端连接请求
    struct sockaddr_in addr_in;
	socklen_t len;
	int fd = accept(sockfd,(struct sockaddr *)&addr_in,&len);    //fd是接收客户端的文件描述符

    char buf[20];
	while(1)
	{
		int num = read(fd,buf,20);
		printf("server : %s\n",buf);	
		write(fd,"ok",3);
	}
	return 0;
}

配置好这些后就可以进行。有些函数是在UDP里面进行学习的。

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)