TCP 套接字函数

本文深入解析TCP协议在服务器与客户端通信中的应用,包括如何使用socket创建连接,服务器端监听与响应客户端连接请求,以及客户端如何发起连接并进行数据交换。重点介绍了connect、listen、accept、send、recv等核心函数的使用方法和作用,帮助开发者掌握TCP通信的完整流程。
摘要由CSDN通过智能技术生成

TCP(Transmission Control Protocol)为应用程序提供可靠的数据传输服务,它是网络编程中使用最广泛的一种(传输层)协议。

使用TCP通信的两个应用程序,一个作为服务器,另一个作为客户端。

下图是设计TCP程序的基本流程。

 

服务器端会有两个socket(并发服务器会有多个),其中一个是侦听socket(图中srv),等待客户来建立连接。另一个是接收到客户端连接请求而建立的新的socket,用于与客户端交互(图中clt)。

服务器启动时首先创建侦听socket srv,绑定本地地址,并在srv上侦听,调用accept后阻塞,直到有客户建立连接。accept返回一个新的socket clt,与客户端之间的交互全部通过clt完成。

当有一方关闭了连接,clt的任务完成,需要closesocket释放占用的资源。socket srv可以继续为其他的客户提供服务,通常不会关闭。

客户端先创建一个socket,调用connect与服务器建立连接。在connect的参数中指明要连接的服务器地址和端口,调用connect会导致TCP协议的三次握手。connect返回之后,三次握手完成,双方可以通过send和recv进行通信。

connect

int connect( SOCKET s,  const struct sockaddr FAR* name,  int namelen);

connect函数用于建立客户端与服务器端的连接。客户端调用connect发起主动连接,TCP协议开始三次握手过程,三次握手完成,connect返回成功,双方就可以在这个连接上交换数据了。

参数s是建立连接的socket句柄,name包含服务器地址和端口号。此函数成功返回0,失败返回SOCKET_ERROR,可以调用WSAGetLastError()获取具体错误码。如果错误码为WSAETIMEDOUT,代表超时。客户端连续发送SYN,但一直收不到服务器的确认ACK。标准实现的超时时间是75秒。

如果是阻塞socket,返回值立即指明了connect的成功或者失败。对于非阻塞socket,如果连接不能立即完成,connect返回SOCKET_ERROR,错误码为WSAEWOULDBLOCK,应用程序可以通过下面的情况来判断什么时候连接成功:(1)通过select函数,如果连接成功,socket将是可写的;(2)如果应用程序使用了WSAAsyncSelect,并且指明对连接事件感兴趣,当连接完成时会收到FD_CONNECT通知。

listen

int listen(SOCKET s, int backlog);

listen函数由服务器端使用,告诉系统在这个socket上接受连接请求。参数backlog称为“待处理连接的最大数目”,设置为SOMAXCONN,底层协议会把backlog设置为一个最大的“合理”值。

此函数成功返回0,失败返回SOCKET_ERROR,可以调用WSAGetLastError()获取具体错误码。

accept

SOCKET accept(SOCKET s, struct sockaddr* addr, int* addrlen);

accept函数用于接受客户端连接请求,仅由服务器端使用,紧跟在listen调用之后。如果连接队列为空,默认阻塞调用线程。

参数s是侦听socket,addr用于接收连接客户端的地址,addrlen在传入时是缓冲区addr的长度,返回后包含了地址addr的实际长度。

accept成功将返回一个已连接socket,与客户端之间的交互全部通过该socket完成。失败则返回INVALID_SOCKET。

服务器端函数accept与客户端connect相对应。客户端调用connect时,向服务器发送SYN,服务器收到SYN,为进入的连接创建新的socket,把它添加到未完成连接队列中,向客户端发送SYN+ACK。客户端收到SYN和ACK,向服务器发送ACK,connect函数返回,表明成功建立连接。ACK一旦到达服务器就完成了三次握手,服务器把未完成连接转移到已完成连接队列中,socket的状态也从SYN_RCVD转变到ESTABLISHED,并唤醒服务线程,accept返回。

send

int send( SOCKET s,  const char FAR* buf,  int len,  int flags);

函数send在一个已连接的socket上向对方发送数据,对于tcp协议,发送的数据量大小没有限制。send成功返回发送数据的长度,可能小于参数len的长度,失败返回SOCKET_ERROR,可以调用WSAGetLastError()获取具体错误码。

参数s是已连接的socket,buf包含要发送的数据,len是buf中数据的长度,参数flags影响send函数的行为。flags可以设置为0,或者是MSG_DONTROUTE(不检查路由表,目的主机在直接相连的网络上),或者是MSG_OOB(发送带外数据),或者是它们的组合。

recv

int recv( SOCKET s,  char FAR* buf,  int len,  int flags);

recv从已连接套接字s接收数据。buf是用于接收数据的缓冲区,len是buf的长度。flags可以设置为0,或者是MSG_PEEK(查看是否有输入的数据可以读取,如果有数据会被复制到recv提供的缓冲区,但并不把数据从输入队列中删除),或者是MSG_OOB(接收带外数据),或者是它们的组合。

当套接字s上没有数据时,默认recv会阻塞调用线程,等待数据到达。非阻塞时,返回SOCKET_ERROR,错误码为WSAEWOULDBLOCK,程序可以调用select或者WSAAsyncSelect来确定什么时候有数据到达。

recv调用成功时,返回接收的字节数。当返回0时,对于面向连接的socket,表示对方正常关闭了这个连接。发生错误时,返回SOCKET_ERROR,可以调用WSAGetLastError()获取具体错误码。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值