c语言socket编程常用函数2

socket()函数

#include <sys/types.h>
#include <sys/socket.h>

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 <sys/types.h>
#include <sys/socket.h>

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 <sys/types.h>
#include <sys/socket.h>

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 <sys/socket.h>
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 <unistd.h>
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 <netdb.h>
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 <netdb.h>
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 <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>

int main(int argc, char *argv[])
{
struct hostent *h;
if (argc != 2) { /* 检查命令行 */
fprintf(stderr,”usage: getip address\n”);
exit(1);
}
if ((h=gethostbyname(argv[1])) == NULL) { /* 取得地址信息 */
herror(“gethostbyname”);
exit(1);
}
printf(“Host name : %s\n”, h->h_name);
printf(“IP Address : %s\n”,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 * ,然后得到数据 。

2 thoughts on “c语言socket编程常用函数2

  1. Pingback: Intelligent Automation

  2. Pingback: beasiswa universitas malaysia

Leave a Reply