send ,recv 函数使用来在 client/server 对等端发送和接收数据的
通过前面的学习我们已经知道:
当 Server 端从请求缓冲队列中获取一个连接请求并将其 accept(接收)
之后,server 的运行系统会为其分配一块用来执行请求操作的缓冲空间。
而这块缓冲空间是通过server端调用 accept 函数的返回值---- 一个新的套接字描述符来指向的。
当Client 端向 Server 端发送请求信息之后,connect 方法成功执行返回 0 的时候,
系统会为其接收来自 Server 端的返回信息也分配一块缓冲空间,而这块缓冲空间是通过
Client 端一开始通过 socket 方法返回的套接字描述符来唯一指定的。
这个地方我理解起来用了一些时间,原因是,我一直认为的是: client server 的缓冲空间是二者共享的,
所以他们的套接字描述符应该是相同的,在仔细思考全部流程之后,这种想法显然是错误的。
大体的思路是这样的,
1.首先知道,套接字描述符是用来标定系统为当前的进程划分的一块缓冲空间的,类似于文件描述符,不过二者有些区别;
2.其次应该知道的是,这块缓冲空间并不是一开始就被系统划分给进程的;
3. 对于 server 端而言,划分系统缓冲空间的时刻是: 当server 决定接收来自 client 的连接请求,
即 accept 方法成功执行,返回一个 > 0 的整数(也就是新的套接字描述符),
系统才会为其分配缓冲空间,自然, 这块缓冲空间是通过 accept 新的套接字描述符来指定的 ;
4. 对于 client 端而言,划分系统缓冲空间的时刻是: 当 client 端执行 connect 函数正确的时候,
connect 函数正确执行,说明此 client 端的连接请求已经被 server 端接收,剩下的就需要系统为 client 划分缓冲空间,
用来接收来自于 server 端的返回结果。 这个时候,系统才会为其分配缓冲空间,
而该缓冲空间使用 client 一开始创建套接字的 socket 函数的返回值标定即可。
值得记下的一点便是,send 是将内存中的数据放入到缓冲区中,
而 recv 是将缓冲区中的数据提取到内存中,而内存便是我们使用各种变量和数据类型的地方。
send 参数便是普通变量参数; 而 recv 参数是
值-结果 参数, 值-结果参数常用作函数的返回值,
有的是双向的,有的是单向的
双向指的是:该参数传入的值会参与到函数的计算中,同时会将函数的运行结果赋给该参数作为函数的返回值。
单向指的是:该参数传入的数值是什么并不重要,重要的是该参数在被传入函数中,函数执行之后,该参数中的数值。
如果将函数理解为一口井,那么单纯的 send 参数便是向井中投入个苹果或是鸭梨这种实体类型。
而 值-结果 参数便是向井中投入一个装东西的空桶,桶中原本是什么并不重要,重要的是桶提上来之后,
里面装着的是什么,当然这个描述是针对单向而言的。
双向的话,就是桶里放个苹果,然后提上来一桶水。(请原谅我这土傻俗的比喻,谢谢)
send 发送数据函数 API 描述
#include
#include
ssize_t send ( int s , const void *msg , size_t len , int flags ) ;
参数:
1. s 套接字描述符
如果是 client 端调用 send 方法的话,s 便是 connect 正确执行之后,首次调用 socket 函数返回的套接字描述符。
如果是 server 端调用 send 方法的话, s 便是 accept 正确执行之后的返回的新的套接字描述符
2. msg 是要从内存 ----> 放入缓冲区中的数据,通常都是将字符串指针强制转为 void *得到的
3. len 是 msg 的长度
4. flags 控制选项,对应一系列系统定义的宏,用来指定以何种方式来发送缓冲区的数据。
常用的取值为 0 ,在特殊一点就是带外数据 (flags= MSG_OOB) 其中 OOB = out of band,
带外数据一般用于紧急命令的传送,希望通信的对等端优先接收和处理的信息。
返回值:
如果返回值为负数,说明将内存中的数据移动到缓冲区中操作失败。
如果返回值为 >=0 的数值,说明移动成功,返回的数值便是将内存中的多少个字节
移动到了缓冲区中。而且这种情况仅仅说明数据移动成功,并不能表明成功发送缓冲区中
的数据到对等端 。
recv 接收数据函数 API 描述
#include
#include
ssize_t recv ( int s , void *buf , size_t len , int flags ) ;
参数:
1. s 套接字描述符,如果在 client 端调用 recv 函数,则是 connect 成功执行之后的,首次调用 socket 函数的返回值、
如果在 server 端调用 recv 函数,则是 accept 成功执行之后返回的新的套接字描述符
2. buf : 指向内存用于存放来自缓冲空间的指针 ,也就是接收来自缓冲区的数据,并将该数据存放到有 buf 指针指向的空间中
3. len : buf 指向的内存空间的大小
4. flags : 控制选项,对应一系列宏变量,这些宏变量用来限制或是指定接收数据的类型
flags = 0 接收所有数据
flags = MSG_OBB 仅仅接收带外数据
flags = MSG_PEEK : 只查看数据而不读出数据
flags = MSG_WAITWALL : 为了减少从缓冲区频繁少量的读取数据到内存操作,
只在 buf 指向的空间中存满数据的核实后,才返回。
示例代码
client 端代码
-
//clientTest.cpp
-
//g++ clientTest.cpp -o client
-
-
#include <stdio.h>
-
#include <string.h> // memset
-
-
#include <sys/types.h> // AF_INET ,SOCK_STREAM
-
#include <sys/socket.h> // socket, connect
-
#include <netinet/in.h> // sockaddr_in
-
#include <arpa/inet.h> // inet_aton
-
#include <unistd.h> // close
-
-
#define SERVER_PORT 1027
-
#define SERVER_IP "10.0.2.15"
-
-
-
int getSocketDone ()
-
{
-
int ret = socket (AF_INET , SOCK_STREAM , 0 ) ;
-
if ( ret < 0 )
-
perror ("socket") ;
-
return ret ;
-
}
-
-
int getConnectDone ( int sock_fd )
-
{
-
struct sockaddr_in serv_addr ;
-
int ret ;
-
-
memset ( &serv_addr, 0 , sizeof(serv_addr) ) ;
-
serv_addr.sin_family = AF_INET ;
-
serv_addr.sin_port = htons (SERVER_PORT) ;
-
ret = inet_aton ( SERVER_IP , &serv_addr.sin_addr ) ;
-
-
if ( ret < 0 )
-
perror ("inet_aton") ;
-
ret = connect ( sock_fd , (struct sockaddr*)(&serv_addr) ,
-
sizeof( struct sockaddr_in )) ;
-
if ( ret < 0 )
-
perror ("connect") ;
-
-
return ret ;
-
}
-
-
int setSendMessage ( int sock_fd , const char *msg , int length )
-
{
-
int ret = send ( sock_fd , msg , length, 0 ) ;
-
-
if ( ret < 0 )
-
perror ("send") ;
-
-
return ret ;
-
}
-
-
int getRecvMessage ( int sock_fd , char *msg )
-
{
-
int ret = recv ( sock_fd , msg ,sizeof(msg) , 0 ) ;
-
-
if( ret < 0 )
-
perror ( "send" ) ;
-
-
return ret ;
-
}
-
-
int main ( int c , char ** v )
-
{
-
int sock_fd , ret , len;
-
char msg [1024] ;
-
-
sock_fd = getSocketDone () ;
-
-
if ( sock_fd > 0 )
-
printf ("success get socket descriptor : %d \n" , sock_fd) ;
-
else
-
goto error ;
-
-
if ( !getConnectDone ( sock_fd ) )
-
printf ("success connect \n") ;
-
else
-
goto error ;
-
-
printf("in put message send to server \n") ;
-
scanf ("%s" , msg) ;
-
len = strlen ( msg ) ;
-
-
-
if ( (ret = setSendMessage ( sock_fd , msg , len ) )> 0 )
-
printf("success send %d bytes message to server \n" , ret ) ;
-
-
else
-
{
-
perror ("send error ") ;
-
goto error ;
-
}
-
memset ( msg , 0 , sizeof ( msg ) ) ;
-
len = sizeof ( msg ) ;
-
-
printf ("waiting response from server \n") ;
-
while ( (ret = getRecvMessage ( sock_fd ,msg )) <= 0 );
-
-
-
printf ("success receive %d bytes message from server \n %s \n",
-
ret , msg ) ;
-
goto success ;
-
-
error :
-
printf ("failed in main \n") ;
-
goto success ;
-
success:
-
return 0 ;
-
}
// Server 端代码
-
//serverTest.cpp
-
// g++ serverTest.cpp -o server
-
-
#include <stdio.h>
-
#include <string.h> // memset
-
-
#include <sys/types.h> // AF_INET , SOCK_STREAM
-
#include <sys/socket.h> // socket , bind , connect , accept
-
#include <netinet/in.h> // sockaddr_in
-
#include <unistd.h>
-
-
#define SERVER_PORT 1027
-
#define LISTEN_Q_lENGTH 10
-
-
int getSocketDone ()
-
{
-
return socket(AF_INET , SOCK_STREAM, 0 ) ;
-
}
-
-
int getBindDone (int sock_fd )
-
{
-
struct sockaddr_in serv_addr ;
-
int ret ;
-
-
memset ( &serv_addr , 0 , sizeof( struct sockaddr_in )) ;
-
serv_addr.sin_family = AF_INET ;
-
serv_addr.sin_port = htons (SERVER_PORT) ;
-
serv_addr.sin_addr.s_addr = htonl ( INADDR_ANY ) ;
-
-
ret = bind ( sock_fd , (struct sockaddr*)(&serv_addr) ,
-
sizeof(struct sockaddr_in ) ) ;
-
return ret ;
-
}
-
-
int getListenDone ( int sock_fd )
-
{
-
return listen (sock_fd , LISTEN_Q_lENGTH ) ;
-
}
-
-
int getAcceptDone ( int sock_fd_queue , struct sockaddr_in *client_addr ,
-
int *client_len )
-
{
-
int sock_fd_conn;
-
-
*client_len = sizeof (struct sockaddr_in ) ;
-
sock_fd_conn = accept ( sock_fd_queue , (struct sockaddr*)client_addr ,
-
(socklen_t*) client_len ) ;
-
-
return sock_fd_conn ;
-
}
-
-
int getRecvMessage ( int sock_fd , char *msg , int len )
-
{
-
int ret = recv ( sock_fd , msg , len , 0 ) ;
-
return ret ;
-
}
-
-
int setSendMessage ( int sock_fd , const char *msg , int len )
-
{
-
int ret = send ( sock_fd , msg , strlen(msg) , 0 ) ;
-
return ret ;
-
}
-
-
-
int main ( int c , char **v )
-
{
-
int sockfd_queue , sockfd_conn , ret , client_len , msg_len ;
-
struct sockaddr_in client_addr ;
-
char msg[1024] ;
-
-
sockfd_queue = getSocketDone () ;
-
if ( sockfd_queue > 0 )
-
printf ("socket success\n") ;
-
else
-
{
-
perror ("socket error") ;
-
goto error ;
-
}
-
-
if ( !getBindDone ( sockfd_queue ) )
-
{
-
printf ("bind success\n") ;
-
}
-
else
-
{
-
perror ( "bind error ") ;
-
goto error ;
-
}
-
-
if ( !getListenDone ( sockfd_queue ) )
-
{
-
printf ("listen success");
-
}
-
else
-
{
-
perror ("listen error") ;
-
goto error ;
-
}
-
-
if ( (sockfd_conn = getAcceptDone ( sockfd_queue , &client_addr , &client_len ) ) > 0 )
-
{
-
printf ("accept success new sockfd_conn :%d \n" , sockfd_conn) ;
-
}
-
else
-
{
-
perror ("accept error") ;
-
goto error ;
-
}
-
-
if ( (ret = getRecvMessage(sockfd_conn , msg , sizeof(msg)) ) > 0)
-
{
-
printf ("recv success receive %d bytes \n" , ret) ;
-
printf ("message contents : %s\n" , msg ) ;
-
}
-
-
else
-
{
-
perror ("recv error ") ;
-
goto error ;
-
}
-
-
// send response to client
-
memset ( msg , 0 , sizeof(msg) ) ;
-
printf ("input response to client \n") ;
-
scanf("%s" , msg) ;
-
-
if ( (ret = setSendMessage ( sockfd_conn , msg , strlen(msg))) > 0 )
-
{
-
printf ("success send %d bytes message\n" , ret ) ;
-
goto success ;
-
}
-
-
else
-
{
-
perror ("server send error ") ;
-
goto error ;
-
}
-
-
error :
-
perror ("error exit ") ;
-
goto success ;
-
success :
-
return 0 ;
-
}
运行顺序
首先运行 server(以阻塞的方式运行)
socket success
bind success
listen successaccept success new sockfd_conn :4
然后打开一个新的 terminal 运行 client ,在client中输入想要发送给 server 端的信息
success get socket descriptor : 3
success connect
in put message send to server
[request_from_client] (此为用户输入的信息)
success send 21 bytes message to server
waiting response from server
切换到 server 运行的 terminal 看到发送过来的 client,在server 运行的terminal 端输入想要回复 client 端的信息
recv success receive 21 bytes
message contents : [request_from_client]
input response to client
[response_from_server] (此为用户输入的信息)
success send 22 bytes message
然后切换到 client 运行的 terminal 看到来自 server 端的回复信息
success receive 8 bytes message from server
[respons
(是的,你没有看错,这个地方是因为没有对 client 端的 recv 方法进行处理,
只要读到数据便返回,相应正确的处理应该是通过一个循环反复读取,直到将缓冲区中的数据全部读取到
msg 对应的内存空间中的时候,才返回, 即通过 while 循环, 循环条件是 recv (...) >=0
一旦将全部输入读入, 便会有 recv(...) <0 , 具体的实现在后面的博文中在进行介绍 )
这个地方 Unix 系统编程中的 “重启库” 中的源码有着详细的描述
client/server 端通信结束
阅读(7005) | 评论(0) | 转发(0) |