c语言socket编程常用函数2
By admin
- 4 minutes read - 753 wordssocket()函数
#include #include int socket(int domain, int type, int protocol); domain应该设置为”AF_INET”,和上面数据结构 struct sockaddr_in 中一样,或者其它的type 告诉内容是SOCK_STREAM还是SOCK_DGRAM 类型,或者其它的. protocol 设置为0.
socket()只是返回以后在系统调用中可能用到的socket描述符,或者错误的时候返回-1.全局变量errno中将存储返回的错误值.
===================== bind() 函数 一旦你有一个套接字,你可能要将套接字和机器上的一定的端口关联起来。 ( 如果你想用 listen() 来侦听一定端口的数据,这是必要一步 –MUD 告 诉你说用命令 “telnet x.y.z 6969” 。 ) 如果你只想用 connect() ,那么这个步骤没有必 要 。 但是无论如何,请继续读下去。
这里是系统调用 bind() 的大概:
#include #include int bind(int sockfd, struct sockaddr *my_addr, int addrlen); sockfd 是调用 socket()返回的文件描述符.
my_addr 是指向数据结构struct sockaddr 的指针,它保存你的地址(即端口和IP地址)信息. addrlen 设置为sizeof(struct sockaddr)
bind()在错误的时候返回是-1,并且设置全局变量errno 注意:在调用bind的时候,端口号不要小于1024.可选择1024-65535之间的端口号.
有时候根本不需要调用它,如果使用connect()来和远程机器进行通讯.就不再需要关心本地端口号,只需要简单的调用connect()就可以了. 它会检查套接字是否绑定端口,如果没有,它会自己绑定一个没有使用的本地端口.
========================== connect()程序 现在我们假设你是个 telnet 程序。你的用户命令你得到套接字的文件描述符。你听从命令调用了 socket() 。下一步,你的用户告诉你通过端口 23( 标准 telnet 端口 ) 连接到”132.241.5.10″ 。
你该怎么做呢 ? 幸运的是,你正在阅读connect()– 如何连接到远程主机这一章。你可 不想让你的用户失望。
这里是系统调用 connect() 的大概: #include #include int connect(int sockfd, struct sockaddr * serv_addr, int addrlen) sockfd 是系统调用 socket() 返回的套接字文件描述符. serv_addr 是保存着目的地端口和IP地址的数据结构 struct sockaddr addrlen 设置为 sizeof(struct sockaddr)
=========================== listen()函数 是换换内容得时候了。假如你不希望与远程的一个地址相连,或者说, 仅仅是将它踢开,那你就需要等待接入请求并且用各种方法处理它们。处 理过程分两步:首先,你听 –listen() ,然后,你接受 –accept() ( 请看下面的 内容 ) 。
除了要一点解释外,系统调用 listen 也相当简单。
int listen(int sockfd, int backlog); sockfd 是调用 socket() 返回的套接字文件描述符。 backlog 是在进入队列中允许的连接数目。什么意思呢 ? 进入的连接是在队列中一直等待直到你接受 (accept() 请看下面的文章) 连接。它们的数目限制于队列的允许。 大多数系统的允许数目是 20,你也可以设置为 5 到 10 。
和上面函数一样,发生错误时返回-1,并设置全局错误变量errno
你可能想象到了,在你调用 listen() 前你或者要调用 bind()或者让内 核随便选择一个端口。如果你想侦听进入的连接 ,那么系统调用的顺序可 能是这样的
socket(); bind(); listen();
accept()
============================== accept()函数 准备好了,系统调用 accept() 会有点古怪的地方的! 你可以想象发生 这样的事情:有人从很远的地方通过一个你在侦听 (listen()) 的端口连接 (connect()) 到你的机器。它的连接将加入到等待接受 (accept()) 的队列 中。你调用accept() 告诉它你有空闲的连接。它将返回一个新的套接字文 件描述符!这样你就有两个套接字了,原来的一个还在侦听你的那个端口, 新的在准备发送 (send())和接收( recv()) 数据。这就是这个过程!
函数是这样定义的:
#include int accpet(int sockfd, void *addr, int *addrlen); sockfd 同上是系统调用socket()返回的套接字描述符 addr 是指向局部的数据结构 sockaddr_in 的指针.这是要求接入的信息所要去的地方(你可以测定哪个地址在哪个端口呼叫).在它的地址传递给accept之前. addrlen 是个局部的整形变量,设置为sizeof(struct sockad,_in).accept将不会将多余的字节给addr,如果你放入的少些,那么化会通过改变addrlen的值反映出来.
========================= send()和recv()函数
这两个函数用于流式套接字或者数据报套接字的通讯。 如果你喜欢使 用无连接的数据报套接字,你应该看一看下面关于 sendto() 和 recvfrom() 的章节。 send() 是这样的:
int send(int sockfd, const void *msg, int len, int flags); sockfd 是你想发送数据的套接字描述符(调用socket()或者accept()返回) msg 是指向你想发送的数据的指针. len 是数据的长度 flags 设置为0就可以了
char *msg = “Beej was here!”; int len, bytes_sent; . . len = strlen(msg); bytes_sent = send(sockfd, msg, len, 0); . . .
send() 返回实际发送的数据的字节数 — 它可能小于你要求发送的数 目! 注意,有时候你告诉它要发送一堆数据可是 它不能处理成功。它只是 发送它可能发送的数据,然后希望你能够发送其它的数据。记住,如果 send() 返回的数据和len 不匹配,你就应该发送其它的数据。但是这里也有个好消息:如果你要发送的包很小 ( 小于大约 1K) ,它可能处理让数据一 次发送完。
最后要说得就是,它在错误的时候返回 – 1 , 并设置 errno 。
recv() 函数
int recv(int sockfd, void *buf, int len, unsigned int flags);
sockfd 是要读的套接字描述符 buf 是要读的信息的缓冲 len 是缓冲的最大长度 flags 可设置为0
recv()返回实际读入缓冲的数据的字节数 错误返回-1,并设置errno
===========================
sendto() 和 recvfrom()函数
“ 这很不错啊 ” ,你说, “ 但是你还没有讲无连接数据报套接字呢? ” 没问题,现在我们开始这个内容。
既然数据报套接字不是连接到远程主机的,那么在我们发送一个包之 前需要什么信息呢 ? 不错,是目标地址!看看下面的: int sendto(int sockfd, const void *msg, int len, unsigned int flags,const struct sockaddr *to, int tolen);
你已经看到了,除了另外的两个信息外,其余的和函数send() 是一样的。 to 是个指向数据结构 struct sockaddr的指针,它包含了目的地的 IP 地址和端口信息。 tolen 可以简单地设置为 sizeof(struct sockaddr) 。 和函数 send() 类似, sendto() 返回实际发送的字节数 ( 它也可能小于你想要发送的字节数!) ,或者在错误的时候返回 -1 。
int recvfrom(int sockfd, void *buf, int len, unsigned int flags,struct sockaddr *from, int *fromlen); 又一次,除了两个增加的参数外,这个函数和 recv() 也是一样的。 from 是一个指向局部数据结构 struct sockaddr 的指针,它的内容是源机器的 IP 地址和端口信息。 fromlen 是个 int 型的局部指针,它的初始值为 sizeof(struct sockaddr) 。函数调用返回后, fromlen 保存着实际储存在from 中的地址的长度。
recvfrom() 返回收到的字节长度,或者在发生错误后返回 -1 。
记住,如果你用 connect() 连接一个数据报套接字,你可以简单的调 用 send() 和 recv() 来满足你的要求。这个时候依然是数据报套接字,依 然使用 UDP ,系统套接字接口会为你自动加上了目标和源的信息。
close() 和 shutdown() 函数 你已经整天都在发送 (send()) 和接收 (recv()) 数据了,现在你准备关闭你的套接字描述符了。
这很简单,你可以使用一般的 Unix 文件描述符 的 close() 函数: close(sockfd); 它将防止套接字上更多的数据的读写。任何在另一端读写套接字的企图都将返回错误信息。 如果你想在如何关闭套接字上有多一点的控制,你可以使用函数 shutdown() 。它允许你将一定方向上的通讯或者双向的通讯 ( 就象 close() 一 样 ) 关闭,你可以使用:
int shutdown(int sockfd, int how); sockfd 是你想要关闭的套接字文件描述复。 how 的值是下面的其中之 一: 0 – 不允许接受 1 – 不允许发送 2 – 不允许发送和接受 ( 和 close() 一样 ) shutdown() 成功时返回 0 ,失败时返回 -1( 同时设置errno) 如果在无连接的数据报套接字中使用 shutdown() ,那么只不过是让 send() 和 recv() 不能使用 ( 记住你在数据报套接字中使用了 connect 后是可以使用它们的) 。
============================ getpeername() 函数 函数 getpeername() 告诉你在连接的流式套接字上谁在另外一边。函数是这样的:
#include int getpeername(int sockfd, struct sockaddr *addr, int *addrlen); sockfd 是连接的流式套接字的描述符。 addr 是一个指向结构 struct sockaddr ( 或者是struct sockaddr_in) 的指针,它保存着连接的另一边的信息。
addrlen 是一个 int 型的指针,它初始化为 sizeof(struct sockaddr) 。
函数在错误的时候返回 -1 ,设置相应的 errno。
一旦你获得它们的地址,你可以使用 inet_ntoa() 或者gethostbyaddr() 来打印或者获得更多的信息。但是你不能 得到它的帐号。 ( 如果它运行着愚 蠢的守护进程,这是可能的,但是它的讨论已经超出了本文的范围,请参 考 RFC-1413以获得更多的信息。)
============================
gethostname() 函数
甚至比 getpeername() 还简单的函数是gethostname() 。它返回你程 序所运行的机器的主机名字。
然后你可以使用 gethostbyname() 以获得你的机器的IP地址。
下面是定义: #include int gethostname(char *hostname, size_t size);
hostname 是一个字符数组指针,它将在函数返回时保存主机名。 size 是 hostname 数组的字节长度。
函数调用成功时返回 0 ,失败时返回 -1 ,并设置 errno 。
==========================
域名服务( DNS ) 如果你不知道 DNS 的意思,那么我告诉你,它代表域名服务 (Domain Name Service) 。它主要的功能是:你给它 一个容易记忆的某站点的地址, 它给你 IP 地址 ( 然后你就可以使用 bind(), connect(), sendto() 或者其它 函数 ) 。当一个人输入: $ telnet whitehouse.gov telnet 能知道它将连接 (connect()) 到 “198.137.240.100” 。 但是这是如何工作的呢 ? 你可以调用函数gethostbyname() : #include struct hostent *gethostbyname(const char *name); 很明白的是,它返回一个指向 struct hostent 的指针。这个数据结构是这样的:
struct hostent { char *h_name; char **h_aliases; int h_addrtype; int h_length; char **h_addr_list; };
#define h_addr h_addr_list[0] 这里是这个数据结构的详细资料: struct hostent: h_name – 地址的正式名称。 h_aliases – 空字节 – 地址的预备名称的指针。 h_addrtype – 地址类型 ; 通常是 AF_INET 。 h_length – 地址的比特长度。 h_addr_list – 零字节 – 主机网络地址指针。网络字节顺序。 h_addr – h_addr_list 中的第一地址。
gethostbyname() 成功时返回一个指向结构体 hostent 的指针,或者是个空 (NULL) 指针。 ( 但是和以前不同,不设置 errno , h_errno 设置错误信息。请看下面的 herror() 。)
但是如何使用呢 ? 有时候(我们可以从电脑手册中发现),向读者灌输信息是不够的。这个函数可不象它看上去那么难用。
这里是个例子:
#include #include #include #include #include #include
int main(int argc, char *argv[]) { struct hostent *h; if (argc != 2) { /* 检查命令行 */ fprintf(stderr,”usage: getip addressn”); exit(1); } if ((h=gethostbyname(argv[1])) == NULL) { /* 取得地址信息 */ herror(“gethostbyname”); exit(1); } printf(“Host name : %sn”, h->h_name); printf(“IP Address : %sn”,inet_ntoa(*((struct in_addr*)h->h_addr))); return 0; }
在使用 gethostbyname() 的时候,你不能用 perror() 打印错误信息 ( 因为 errno 没有使用 ),你应该调用 herror() 。
相当简单,你只是传递一个保存机器名的字符串 ( 例如”whitehouse.gov”) 给 gethostbyname(),然后从返回的数据结构 struct hostent 中获取信息。
唯一也许让人不解的是输出 IP 地址信息。 h->h_addr 是一个 char * , 但是 inet_ntoa() 需要的是 struct in_addr 。因此,我转换 h->h_addr 成 struct in_addr * ,然后得到数据 。