1.2.2
bind函数绑定端口实例
下面的实例使用了bind函数在一个打开的socket上面绑定IP与端口。绑定的端口是5678,IP为INADDR_ANY,表示本地计算机的默认IP地址。
代码17-1 bind函数绑定端口:\源文件\17\17.19.c
#include
#include
#include
#include
#include
#define PORT 5678
main()
{
int sockfd,newsockfd, fd; struct sockaddr_in addr;
int addr_len = sizeof(struct sockaddr_in);
fd_set myreadfds;
char msgbuffer[256];
if ((sockfd = socket(AF_INET,SOCK_STREAM,0))<0) {
perror("socket");
exit(1);
}
else {
printf("socket created .\n");
printf("socked id: %d \n",sockfd);
}
bzero(&addr,sizeof(addr)); addr.sin_family =AF_INET;
addr.sin_port = htons(PORT); addr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(sockfd,&addr,sizeof(addr))<0) {
perror("connect");
exit(1);
}
else {
printf("connected.\n");
printf("local port:%d\n",PORT) ;
}
}
输入下面的命令,编译这个程序。
gcc 17.19.c
输入下面的命令,对这个程序添加可执行权限。
chmod +x a.out
输入下面的命令,运行这个程序。
./a.out
程序的运行结果如下所示。
socket created .
socked id: 3
connected.
local port:5678
1.3 监听与连接
所谓监听,指的是socket的端口处于等待状态,如果客户端有连接请求,这个端口会接受这个连接。连接指的是客户端向服务端发送一个通信申请,服务端会响应这个请求。本节将讲述socket的监听与连接操作。
1.3.1 等待监听函数listen
服务器必须等待客户端的连接请求,listen函数用于实现监听等待功能。这个函数的使用方法如下所示。
int listen(int s,int backlog);
在参数列表中,s表示已经建立的socket,backlog表示能同时处理的最大连接请求,如果超过这个数目,客户端将会接收到ECONNREFUSED拒绝连接的错误。需要注意的是,listen并未真正的接受连接,只是设置socket的状态为listen模式,真正接受客户端连接的是accept函数。通常情况下,listen函数会在socket,
bind函数之后调用,然后才会调用accept函数。
listen函数只适用SOCK_STREAM或SOCK_SEQPACKET的socket类型。如果socket为AF_INET则参数backlog最大值可设至128,即最多可以同时接受128个客户端的请求。
如果调用成功,则函数的返回值为0,失败返回-1。函数可能发生如下所示的错误,可以用errno来捕获发生的错误。
EBADF:参数sockfd不是一个合法的socket。
EACCESS:权限不足。
EOPNOTSUPP:指定的socket不支持listen模式。
在使用这个函数前,需要在程序的最前面包含下面的头文件。
#include
1.3.2 listen函数使用实例
本节将讲述一个listen函数使用实例。在程序中,先建立一个socket,然后用bind函数在这个socket上面绑定端口与IP,然后用listen函数设置这个socket进行监听。程序的代码如下所示。
代码17-2 listen函数监听端口:\源文件\17\17.20.c
#include
#include
#include
#include
#include
#define PORT 5678 #define MAX 10
main()
{
int sockfd,newsockfd,is_connected[MAX],fd; struct sockaddr_in addr;
int addr_len = sizeof(struct sockaddr_in);
fd_set myreadfds;
char msgbuffer[256];
char msg[] ="This is the message from server.Connected.\n";
if ((sockfd = socket(AF_INET,SOCK_STREAM,0))<0) {
perror("socket");
exit(1);
}
else {
printf("socket created .\n");
printf("socked id: %d \n",sockfd);
}
bzero(&addr,sizeof(addr)); addr.sin_family =AF_INET; addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(sockfd,&addr,sizeof(addr))<0) {
perror("connect");
exit(1);
}
else {
printf("connected.\n");
printf("local port:%d\n",PORT) ;
}
if(listen(sockfd,3)<0) {
perror("listen"); exit(1);
}
else {
printf("listenning......\n"); }
}
输入下面的命令,编译这个程序。
gcc 17.20.c
输入下面的命令,对这个程序添加可执行权限。
chmod +x a.out
输入下面的命令,运行这个程序。
./a.out
程序的运行结果如下所示。
socket created .
socked id: 3
connected.
local port:5678
listenning......
1.3.3 接受连接函数accept
服务器处于监听状态时,如果获得客户机的请求会将这个请求放在等待队列中,当系统空闲时将处理客户机的连接请求。接受连接请求的函数是accept,这个函数的使用方法如下所示。
int accept(int s,struct sockaddr * addr,int * addrlen);
在参数列表中,s表示处于监听状态的socket,addr是一个sockaddr结构体类型的指针,系统会把远程主机的这些信息保存到这个结构体指针上,addrlen表示sockaddr的内存长度,可以用sizeof函数来取得。
当accept函数接受一个连接时,会返回一个新的socket编号。以后的数据传输与读取就是通过这个新的socket编号来处理。原来参数中的socket可以继续使用。接受连接以后,远程主机的地址和端口信息将会保存到addr所指的结构体内。如果处理失败,返回值为-1。函数可能产生下面的错误,可以用error来捕获发生的错误。
EBADF:参数s不是一个合法的socket代码。
EFAULT:参数addr指针指向无法存取的内存空间。
ENOTSOCK 参数s为一文件描述词,而不是一个socket。
EOPNOTSUPP:指定的socket不是SOCK_STREAM。
EPERM:防火墙拒绝这一个连接。
ENOBUFS:系统的缓冲内存不足。
ENOMEM:核心内存不足。
在使用这个函数前,需要在程序中包含下面的头文件。
#include
#include
1.3.4
accept函数使用实例
本节将讲述一个accept函数使用实例。在程序中,先建立一个socket,然后用bind函数在这个socket函数上面绑定一个端口,然后使用listen函数使这个端口处于监听状态。当有连接请求时,accpet函数会产生一个新的socket,然后输出提示信息。程序的代码如下所示。
代码17-3
accept函数接受连接:\源文件\17\17.21.c
#include
#include
#include
#include
#include
#define PORT 5678
main()
{
int sockfd,newsockfd,fd;
struct sockaddr_in addr;
int addr_len = sizeof(struct sockaddr_in);
fd_set myreadfds;
char msgbuffer[256];
if ((sockfd = socket(AF_INET,SOCK_STREAM,0))<0)
{
perror("socket");
exit(1);
}
else
{
printf("socket created .\n");
printf("socked id: %d \n",sockfd);
}
bzero(&addr,sizeof(addr));
addr.sin_family =AF_INET;
addr.sin_port = htons(PORT);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
if(bind(sockfd,&addr,sizeof(addr))<0)
{
perror("connect");
exit(1);
}
else
{
printf("connected.\n");
printf("local port:%d\n",PORT) ;
}
if(listen(sockfd,3)<0)
{
perror("listen");
exit(1);
}
else
{
printf("listenning......\n");
}
if((newsockfd = accept (sockfd,&addr,&addr_len))<0)
{
perror("accept");
}
else
{
printf("accepted a new connecttion.\n");
}
}
输入下面的命令,编译这个程序。
gcc 17.21.c
输入下面的命令,对这个程序添加可执行权限。
chmod +x a.out
输入下面的命令,运行这个程序。
./a.out
程序的运行结果如下所示。
socket created .
socked id: 3
connected.
local port:5678
listenning......
结果表明,本地计算机的5678号端口处于监听状态。打开浏览器,在浏览器的地址栏中输入下面的网址,然后按“Enter”键。这样浏览器会请求连接本地计算机的5678号端口。
浏览器会显示无法打开这个网页。在终端中显示的结果如下所示。表明这个程序已经接受了这个连接,然后退出了程序。
accepted a new connecttion.
1.3.5 请求连接函数connet
所谓请求连接,指的是客户机向服务器发送信息时,需要发送一个连接请求。connect函数可以完成这项功能,这个函数的使用方法如下所示。
int connect (int sockfd,struct sockaddr * serv_addr,int addrlen);
在参数列表中,sockfd表示已经建立的socket,serv_addr是一个结构体指针,指向一个sockaddr结构体,这个结构体存储着远程服务器的IP与端口信息,Addrlen表示sockaddr结构体的内存长度,可以用sizeof函数来获取。sockaddr结构体的定义见前面节中的bind函数所述。
函数会将本地的socket连接到serv_addr所指定的服务器IP与端口。如果连接成功,返回值为0,连接失败则返回-1。函数可能发生下面的错误,可以用error来捕获发生的错误。
EBADF:参数sockfd 不是一个合法的socket。
EFAULT:参数serv_addr指针指向了一个无法读取的内存空间。
ENOTSOCK:参数sockfd是文件描述词,而不是一个正常的socket。
EISCONN:参数sockfd的socket已经处于连接状态。
ECONNREFUSED:连接要求被服务器拒绝。
ETIMEDOUT:需要的连接操作超过限定时间仍未得到响应。
ENETUNREACH:无法传送数据包至指定的主机。
EAFNOSUPPORT:sockaddr结构的sa_family不正确。
EALREADY:socket不能阻断,但是以前的连接操作还未完成。
如果需要使用这个函数,需要在程序的最前面包含下面的头文件。
#include
#include