网络编程之tcp服务器/客户端模型

本文介绍了作者在学习网络编程时实现的一个TCP长连接服务器/客户端模型,该模型包括TCP长连接特性、UDP心跳机制,并可在命令行查看心跳次数和断开时间。通过对比TCP的可靠性、流量控制等特点与UDP的无序、不可靠特性,展示了两种协议的应用场景差异。
摘要由CSDN通过智能技术生成

前言

       刚接触网络编程,所以写个小demo玩玩,主要实现的功能:(1)tcp长连接(2)心跳采用UDP连接方式(3)使用命令行查看心跳发送次数,断开时间等。

一·概念的理解:

TCP长连接:tcp服务器与客户端建立连接后,进行一次读写后,连接并不主动断开,之后一直使用这个连接。即不需要发送完一次交互就close

TCP短连接:tcp服务器与客户端建立连接后,进行一次读写后,调用close将连接断开。

TCP特点:(1)字节流,无边界长度(2)面向连接(3)可靠,超时重传直到丢失的数据修复,接收端可以根据序号重排再给应用(4)TCP提供流量控制,通告窗口告知对端他一次能从对端接收多少数据,确保不发生缓冲区溢出。

UDP特点:(1)不可靠,但每个数据报都有长度,不提供序列号确认等(2)UDP可用一个套接字发送给多个地址(3)UDP不提供流量控制


二.流程

三:设计思想
1.由于要连接10个客户端端,所以最初的方法是为每一个连接单独开启一个进程或者线程的并发服务器。由于在真正项目中线程数有限,所以换用select IO复用模型进行TCP的数据交换。
2.单独为UDP的心跳开启一个线程。服务器端开启一个线程,根据不同客户端发过来的心跳不同来区别是哪个客户端(暂时不知道实际应用中是否每个客户端也会发送自己的ID)进行心跳计数和在线时长,掉线时长的记录。客户端开启一个线程来发送心跳。不使用setsockopt的SO_KEEPALIVE是因为要两个小时才会去检查。
3.命令行采用IO复用检查标准输入 stdin是否有数据,如果命令行字符串为showinfo则打印相关信息。
4.注册采用客户端发送自己的端口号(随便一个东西)给服务器,如果服务器收到,并且端口号正确就返回给客户端通知一下已经注册了。

四:需注意的知识点(随便列了几个,看过书的都应该知道)
1.TCP连接设置地址和端口号中服务器设置自己的通配地址和端口号并绑定,所以服务器最终建立的连接套接字的IP地址和端口号均为这里所设置。客户端一般不需要绑定,虽然建立连接时候也设置了要连接的IP地址和端口字段,那都是服务器的,内核会确定其源地址并分配临时端口。(通配地址要等建立连接后内核才会选择一个本地的IP地址,这就是为何netstat -a 命令后监听IP地址为“*”)

2.UDP服务同样,服务器端使用bind函数绑定ip和端口号,其端口号就是绑定的。客户端像服务器sendto时需要设置要发送的IP地址和端口字段,该端口并和这个UDPsocket没什么关系,其端口在sendto时才分配其端口。UDP协议无客户端与服务器,只要知道IP地址与端口号就可以sendto数据,不去管目标地址是否收到数据。

3.FD_ZERO()                 函数是把文件描述符集全部清空即置0;
    FD_SET()                   将sokefd指定的位置1
   select()                    该函数需要注意的地方是如果选择阻塞方式,当有文件描述符准备好后他才返回,即当有socket缓冲区数据超过可读的数据套接字低水位标记当前值就返回,返回后select会将此时没有数据准备好的位都置为0.这个就是为什么每次在select上面要重新使用FD_SET的原因也是一般并                           发服务器模型使用了allset的原因。
    FD_ISSET()         这个函数是检查经过select后依然为1(即有描述符已经准备就绪)的位。
    FD_CLR()                  这个函数用于把指定的位置0.
    例如 sockfd的值是第几位例如4,FD_SET是把第4位置1.

4.在bind前使用setsockopt()的SO_REUSEADDR选项,这样在服务器终止其进程后可以立即重新运行,否则会出现bind错误,这是由于服务器端处于TIME_WAIT状态要等4分钟时间(可能为1分钟)才会释放端口。

5.close过程:tcp连接建立时三次握手,终止需要四个分节。

当一段主动调用close的时候另一端执行被动关闭,如果read返回值为0,此端可以主动调用close,也可不用,内核会自动处理,全由个人习惯。这个过程对于在程序中出现异常打印时候去分析再好不过。

6.recv()和send()与read()和write()是多数情况为TCP使用,默认下都为阻塞模式,可以使用fcntl()函数设置为非阻塞,如果你使用了一个select函数下面的recv等函数最好用非阻塞的,否则程序会在那里傻等,此时其他sockfd的数据已经到了,而select执行不到。另UDP协议使用recvfrom()和sendto()函数居多。

7.并行服务器与fork函数
(1)父进程中调用fork之前打开的所有描述符在fork返回之后由子进程共享。
(2)在调用fork函数时父进程创建一个副本,这样每个副本都可以在另一个副本执行其他任务的同时处理各自的操作。
举例:tcp连接的时候调用socket()返回listenfd,此时listenfd的关联表项为1,调用accept()函数时返回connfd关联表项也为1,而调用fork后,创建副本,此时listenfd和connfd的关联表项都为2,当父进程或者子进程close()该文件描述符后并不发送FIN给对端,因为close()只有当描述符关联表项为0时才发送FIN关闭套接字,所有父进程调用close(connfd)关闭套接字子进程依然可以使用该套接字收发数据。
     
由于子进程不需要管listenfd所以close,此时并没关闭该描述符,同理数据处理由子进程完成,所以在父进程关闭connfd也是可以的,由于没关闭所以不影响子进程。

五感想
由于才接触网络编程导致一边学习一边写程序,感想可能对新手有些许帮助。
(1)像这种小的demo如果看UNIX网络编程的话,做下来应该不成问题,书上都有差不多的例子供参考,相关的知识点书上讲的都很全。由于在写程序碰到问题后去unix网络编程上去找相关的知识,导致前面每章有几节没看,在碰到有些问题的时候不知所措,定位不准。后来重新把没看的都看了,好多知识点都是写程序碰到的,自己想了想然后上网查的。所以书一定要看,而且还要仔细的看,仔细的理解,把每一段说明的逻辑都搞懂。
(2)技术是一步一个脚印的,不要怕自己有不懂的地方,有不懂的一定要弄清楚,否则越积累漏洞越大。如果你一个地方没弄明白后根本不知道自己的知识盲点在哪里,以后可能导致你再做某一个项目的时候浪费更多的时间也解决不掉。
(3)不要害怕问题,要有自信,多问自己为什么这样,这些参数是怎么回事,都代表服务器还是客户端的相关数据,没事多想想问题总会有更深刻的理解。我们的思维在推理这种逻辑时会异常兴奋,可以让我们注意力集中,在攻下一个问题后我们的自信也会随着增加一些。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值