TCP下实现通讯

TCP下实现通讯

有了上节课的基础现在我们一起来用TCP/IP协议实现通讯

网络编程---传输层选择一个数据控制模式:TCP/IP      UDP/IP
TCP: 面向连接         可靠的                流式服务
UDP:无连接             不可靠的             数据报服务


TCP是面向连接的服务


三次握手创造链接:

客户端先给服务器发送syn,syn如果在传输过程中丢了(tcp会实现超时重传机制,会有一个计时器,服务器如果收到报文syn要发送一个ack),收到了syn/ack也会反馈一个ack证明链接建立了起来 然后就要进行收发数据,如果客户端和服务器同事给对方发送syn然后再给对方同时发送ack这就是四次握手,所以最少是三次握手。

四次挥手断开链接:

其实三次挥手也是有可能完成的不需要服务器处理数据第二个ack和第三个fin合在一起就行了。




大家注意这里的TIME_WAIT 状态很关键:他是出现在主动发起断开链接请求的一端
                                                         意义:1、保证可靠的终止TCP链接
                                                                    2、保证迟来的数据报能被识别并丢弃
一般存在时间为两分钟左右,然后释放掉


红线:客户端发起链接
绿线:服务器接收链接
蓝线:主动断开链接的过程
紫线:被动断开链接的过程



下来我们一起开一下编程中要用到的几个函数:
服务器:socket   bind   listen   accept   recv/send   close
客户端:socket   /*bind*/   connect   recv/send   close

1>>   int socket(int protofamily, int type, int prtotcol);//返回sockfd
socket函数对应于普通文件的打开操作。普通文件的打开操作返回一个文件描述字,而socket()用于创建一个socket描述符,它唯一标识一个socket。这个socket描述字跟文字一样,后续的操作都有用到它,把它作为参数,通过它来进行一些读写操作。
        protofamily::协议族,常用的协议有,AF_INET(IPV4)、AF_INET6(IPV6)
        type::指定socket类型。SOCK_STREAM(TCP)     SOCK_DGRAM(UDP)
        protocol::指定协议,这里我们通常用0

         当我们调用socket创建一个socket时,返回socket描述它存在于协议族(address family,AF_XXX)空间中,但没有一个具体的地址。如果想要就给它赋值一个地址,就必须调用bind()函数,否则就当调用connect()、listen()时系统会自动随机分配一个端口。


2>>   int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
正如上面所说bind()函数把一个地址族中的特定地址赋给socket。例如对应AF_INET、AF_INET6就是把一个ipv4或者ipv6地址和端口号组合赋给socket。
        socket:即socket描述字,它是通过socket()函数创建了,唯一标识一个socket。bind()函数就是将这个描述字绑定一个名字。
        addr:一个const struct sockaddr*指针,指向要绑定给socket的协议地址。这个地址结构根据地址创建socket是的地址协议不同而不同。
        struct sockaddr_in
{
         sa_family_t  sin_family;   //地址族      AF_INET
         u_int16_t   sin_port;  //端口号   网络字节序(大端模式)  PC机(小端模式)
         struct  in_addr   sin_addr; //IP地址
}

        struct in_addr
{
        u_init32_t      s_addr; //IP地址最终的表示
}



3>>listen()、conect()函数
如果作为一个服务器,在调用socket()、bind()之后就会调用listen()来监听这个socket,如果客户端这时调用connect()发出连接请求,服务器端就会接收到这个请求。

int  listen(int   sockfd,  int   backlog);
int  connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

listen函数的第一个参数即为要监听的socket描述字,第二个参数为相应socket可以排队的最大连接个数。socket()函数创建的socket默认是一个主动类型的,listen()函数将socket变为被动类型的,等待客户的连接请求。

connect函数的第一个参数即为客户端的socket描述字,第二参数为服务器的socket地址,第三个参数为socket地址的长度。客户端通过调用connect函数来建立与TCP服务器的连接。


4>>accept()函数
TCP服务器端依次调用 socket() bind() listen() 之后,就会监听指定的socket地址了。TCP客户端依次调用 socket() connect() 之后就向TCP服务器发送了一个连接请求。TCP服务器监听到这个请求之后,就会调用 accept() 函数取接收请求,这样连接就建立好了。之后就可以开始网络I/O操作了,即类同于普通文件的读写I/O操作。

int accept(int  sockfd, struct sockaddr *addr,socklen_t *addrlen);//返回connect_fd
参数sockfd
参数sockfd就是上面解释中的监听套接字,这个套接字用来监听一个端口,当有一个客户与服务器连接时,它使用这个一个端口号,而此时这个端口号正与这个套接字关联。当然客户不知道套接字这些细节,它只知道一个地址和一个端口号。
参数addr
这是一个结果参数,它用来接受一个返回值,这返回值指定客户端的地址,当然这个地址是通过某个地址结构来描述的,用户应该知道这一个什么样的地址结构。如果对客户的地址不感兴趣,那么可以把这个值设置为NULL。
参数len
如同大家所认为的,它也是结果的参数,用来接受上述addr的结构的大小的,它指明addr结构所占有的字节个数。同样的,它也可以被设置为NULL。

如果accept成功返回,则服务器与客户已经正确建立连接了,此时服务器通过accept返回的套接字来完成与客户的通信。



5>>send()、recv()函数

这就相当于之前文件中的read()/write()

ssize_t   send(int  sockfd, const void *buff,size_t len,  int flags);
ssize_t   recv(int sockfd,  void *buf,  size_t len,  int flags);


6>>close()函数
在服务器与客户端建立连接之后,会进行一些读写操作,完成了读写操作就要关闭相应的socket描述字,好比操作完打开的文件要调用fclose关闭打开的文件。
注意:close操作只是使相应socket描述字的引用计数-1,只有当引用计数为0的时候,才会触发TCP客户端向服务器发送终止连接请求。




代码如下:
服务器代码:
客户机代码:
运行结果


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值