前沿拓展:
本文主要给大家分享网络七层概念之网络编程socket,前边的章节已经给大家讲述了链路层、物理层、网络层、应用层、传输层等,欢迎学习嵌入式网络编程的朋友关注、转载和发表评论!
(绝对的好文,建议先收藏和转载!)
*/ bind(sockfd, (struct sockaddr *)&my_addr,
sizeof(struct sockaddr)); listen(sockfd, BACKLOG);
sin_size = sizeof(struct sockaddr_in);
new_fd = accept(sockfd, &their_addr, &sin_size);
……
……
}
注意:我们使用了套接字描述符 new_fd 用来进行所有的 send() 和 recv() 调用。如果你只想获
得一个单独的连接,那么你可以将原来的 sock_fd 关掉(调用 close()),这样的话就可以阻止以后的连接了。
在面向连接的通信中客户机要做如下一些事:
· 调用 socket() 函数创建一个套接字。
· 调用 connect() 函数试图连接服务。
· 如果连接成功调用 write() 函数请求数据,调用 read() 函数接收引入的应答。
8.5.6 send()和 recv()函数
这两个函数是最基本的,通过连接的套接字流进行通讯的函数。
如果你想使用无连接的用户数据报的话,请参考下面的 sendto() 和 recvfrom() 函数。
send() 函数的声明:
#include <sys/types.h>
#include <sys/socket.h>
int send(int sockfd, const void *msg, int len, int flags);
send 的参数含义如下:
· sockfd 是代表你与远程程序连接的套接字描述符。
· msg 是一个指针,指向你想发送的信息的地址。
· len 是你想发送信息的长度。
· flags 发送标记。一般都设为 0(你可以查看 send 的 man pages 来获得其他的参数值并且明白各个参数所代表的含义)。
下面看看有关 send() 函数的代码片段: char *msg = ” Hello! World!”;
int len, bytes_sent;
……
……
len = strlen(msg);
bytes_sent = send(sockfd, msg, len, 0);
……
……
……
send()函数在调用后会返回它真正发送数据的长度。
注意:send() 所发送的数据可能少于你给它的参数所指定的长度!
因为如果你给 send() 的参数中包含的数据的长度远远大于 send() 所能一次发送的数据,则
send() 函数只发送它所能发送的最大数据长度,第二它相信你会把剩下的数据再次调用它来进行第二次发送。
所以,记住如果 send() 函数的返回值小于 len 的话,则你需要再次发送剩下的数据。幸运的是, 如果包足够小(小于 1K),那么 send() 一般都会一次发送光的。
像上面的函数一样,send() 函数如果发生错误,则返回 – 1,错误代码存储在全局变量 errno中。
下面我们来看看 recv() 函数。
函数 recv() 调用在许多方面都和 send() 很相似,下面是 recv() 函数的声明:
#include <sys/types.h>
#include <sys/socket.h>
int recv(int sockfd, void *buf, int len, unsigned int flags);
recv()的参数含义如下:
· sockfd 是你要读取数据的套接字描述符。
· buf 是一个指针,指向你能存储数据的内存缓存区域。
· len 是缓存区的最大尺寸。
· flags 是 recv() 函数的一个标志, 一般都为 0(具体的其他数值和含义请参考 recv() 的man pages)。
recv() 返回它所真正收到的数据的长度。(也就是存到 buf 中数据的长度)。如果返回 –1 则代表发生了错误(比如网络以外中断、对方关闭了套接字连接等),全局变量 errno 里面存储了错误代码。
很简单,不是吗?现在你已经可以使用套接字连接进行网络发送数据和接受数据了!
Ya! 你现在已经成为了一个 Linux 下的网络程序员了!
8.5.7 sendto()和 recvfrom()函数
这两个函数是进行无连接的 UDP 通讯时使用的。使用这两个函数,则数据会在没有建立过任何连接的网络上传输。因为数据报套接字无法对远程主机进行连接,想想我们在发送数据前需要知道些什么 呢?
对了!是远程主机的 IP 地址和端口!
下面是 sendto()函数和 recvfrom()函数的声明:
#include <sys/types.h>
#include <sys/socket.h>
int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);
和你所看到的一样,这个函数和 send()函数基本一致。
· sockfd 是代表你与远程程序连接的套接字描述符。
· msg 是一个指针,指向你想发送的信息的地址。
· len 是你想发送信息的长度。
· flags 发送标记。一般都设为 0。 (你可以查看 send 的 man pages 来获得其他的参数值并且明白各个参数所代表的含义)
· to 是一个指向 struct sockaddr 结构的指针,里面包含了远程主机的
IP 地址和端口数据。
· tolen 只是指出了 struct sockaddr 在内存中的大小sizeof(struct sockaddr)。
和 send() 一样,sendto() 返回它所真正发送的字节数(当然也和 send() 一样,它所真正发送的字节数可能小于你所给它的数据的字节数)。 当它发生错误的时候,也是返回– 1,同时全局变量
errno 存储了错误代码。
同样的,recvfrom() 函数和 recv() 函数也基本一致。
recvfrom() 的声明为:
#include <sys/types.h>
#include <sys/socket.h>
int recvfrom(int sockfd, void *buf, int len, unsigned int flags struct sockaddr *from, int *fromlen);
其参数含义如下:
· sockfd 是你要读取数据的套接字描述符。
· buf 是一个指针,指向你能存储数据的内存缓存区域。
· len 是缓存区的最大尺寸。
· flags 是 recvfrom() 函数的一个标志, 一般都为 0 (具体的其他数值和含义请参考 recvfrom() 的 man pages)。
· from 是一个本地指针,指向一个 struct sockaddr 的结构(里面存有源 IP 地址和端口数)。
· fromlen 是一个指向一个 int 型数据的指针,它的大小应该是sizeof(struct sockaddr).当函数返回的时候,
formlen 指向的数据是 form 指向的 struct sockaddr 的实际大小。
recvfrom() 返回它接收到的字节数,如果发生了错误,它就返回 – 1,全局变量 errno 存储了错误代码。
如果一个信息大得缓冲区都放不下,那么附加信息将被砍掉。该调用可以立即返回,也可以**的 等待。这取决于你把 flags 设置成什么类型。你甚至可以设置超时(timeout)值。
在说明书(man pages)中可以找到 recvfrom 的更多信息。
注意:如果你使用 cnnect() 连接到了一个数据报套接字的服务器程序上,那么你就可以使用
send() 和 recv() 函数来传输你的数据。不要以为你在使用一个流式的套接字,你所使用的仍然是一个用户数据报的套接字,只不过套接字界面在 send() 和 recv()的时候自动帮助你加上了目标地址, 目标端口的信息。
8.5.8 close()和 shutdown()函数
程序进行网络传输完毕后,你需要关闭这个套接字描述符所表示的连接。实现这个非常简单,只需 要使用标准的关闭文件的函数:close()。
使 用 方 法 : close(sockfd);
执行 close()之后,套接字将不会在允许进行读**作和写**作。任何有关对套接字描述符进行读和写的**作都会接收到一个错误。
如果你想对网络套接字的关闭进行进一步的**作的话,你可以使用函数 shutdown()。它允许你进行单向的关闭**作,或是全部禁止掉。
shutdown()的声明为:
#include <sys/socket.h>
int shutdown(int sockfd, int how); 它的参数含义如下:
· sockfd 是一个你所想关闭的套接字描述符。
· how 可以取下面的值。0 表示不允许以后数据的接收**;1 表示不允许以后数据的发送**作;
2 表示和 close() 一样,不允许以后的任何**作(包括接收,发送数据)。
shutdown() 如果执行成功将返回 0,如果在调用过程中发生了错误,它将返回–1,全局变量 errno中存储了错误代码。
如果你在一个未连接的数据报套接字上使用 shutdown() 函数(还记得可以对数据报套接字 UDP 进行 connect() **作吗?),它将什么也不做。
8.5.9 setsockopt()和 getsockopt()函数
Linux 所提供的 socket 库含有一个错误(bug)。此错误表现为你不能为一个套接字重新启用同一个端口号,即使在你正常关闭该套接字以后。例如,比方说,你编写一个服务器在一个套接字上等待的 程序。服务器打开套接字并在其上侦听是没有问题的。无论如何,总有一些原因(不管是正常还是非正 常的结束程序)使你的程序需要重新启动。然而重启动后你就不能把它绑定在原来那个端口上了。从
bind() 系统调用返回的错误代码总是报告说你试图连接的端口已经被别的进程所绑定。
问题就是 Linux 内核在一个绑定套接字的进程结束后从不把端口标记为未用。在大多数
Linux/UNIX 系统中,端口可以被一个进程重复使用,甚至可以被其它进程使用。
在 Linux 中绕开这个问题的办法是,当套接字已经打开但尚未有连接的时候用 setsockopt() 系统调用在其上设定选项(options)。而 getsockopt() 可以从给定的套接字取得选项。
这里是这些调用的语法:
#include<sys/types.h>
#include<sys/socket.h>
int getsockopt(int sockfd, int level, int name,
char *value, int *optlen); int setsockopt(int sockfd, int level, int name,
char *value, int *optlen); 下面是两个调用的参数说明:
· sockfd 必须是一个已打开的套接字。
· level 是函数所使用的协议标准(protocol level) (TCP/IP 协议使用
PPROTO_TCP,套接字标准的选项实用 SOL_SOCKET)。
· name 选项在套接字说明书中(man page)有详细说明。
· value 指向为 getsockopt()函数所获取的值,setsockopt()函数所设置的值的地址。
· optlen 指针指向一个整数,该整数包含参数以字节计算的长度。
现在我们再回到 Linux 的错误上来.当你打开一个套接字时必须同时用下面的代码段来调用
setsockopt() 函数:
/* 设定参数数值 */
opt = 1; len = sizeof(opt);
/* 设置套接字属性 */ setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,&len);
setsockopt()函数还有很多其他用法,请参考帮助页(man pages)。
8.5.10 getpeername() 函数
这个函数可以取得一个已经连接上的套接字的远程信息(比如 IP 地址和端口),告诉你在远程和你连接的到底是谁。
它的声明为:
#include <sys/socket.h>
int getpeername(int sockfd, struct sockaddr *addr, int *addrlen); 下面是参数说明:
· sockfd 是你想取得远程信息的那个套接字描述符。
· addr 是一个指向 struct sockaddr (或是 struct sockaddr_in)的指针。
· addrlen 是一个指向 int 的指针,应该赋于 sizeof(struct sockaddr)的大小。
如果在函数执行过程中出现了错误,函数将返回 –1,并且错误代码储存在全局变量 errno 中。当你拥有了远程连接用户的 IP 地址,你就可以使用 inet_ntoa() 或 gethostbyaddr() 来输出
信息或是做进一步的处理。
8.5.11 gethostname()函数
gethostname() 函数可以取得本地主机的信息.它比 getpeername() 要容易使用一些。
它返回正在执行它的计算机的名字。返回的这个名字可以被 gethostbyname() 函数使用,由此可以得到本地主机的 IP 地址。
下面是它的声明:
#include <unistd.h>
int gethostname(char *hostname, size_t size); 参数说明如下:
· hostname 是一个指向字符数组的指针,当函数返回的时候,它里面的数据就是本地的主机的名字。
· size 是 hostname 指向的数组的长度。
函数如果成功执行,它返回 0,如果出现错误,则返回–1,全局变量 errno 中存储着错误代码。
拓展知识:
原创文章,作者:九贤生活小编,如若转载,请注明出处:http://www.wangguangwei.com/49869.html