- 论坛徽章:
- 0
|
UNIX系统TCP/IP Socket接口的应用
Socket接口是TCP/IP网络的API(应用程序接口)。它定义了许多函数和例程,程序员可以用它开发TCP/IP网络上的应用程序,可以在CLIENT/SERVER模型中制作自己的应用程序通信接口。今天,Socket接口是TCP/IP网络最为通用的API之一。
TCP/IP协议组可以实现面向连接和无连接两种网络通信。面向连接的协议在两个连接端点之间建立一条虚电路,两个端点之间的链路可以看作是直接的点到点的连接。两个端点只有在建立连接后才能传输数据。在面向连接的网络通信中,数据按字节流形式传输。无连接协议在传输报文前,不用建立连接。无连接协议每个报文包含一个完整,精确的传送地址。在无连接网络通讯中,数据按数据包形式流动。网络通信包含两台主机或两个进程,网络对话的每一端称为一个端点。可将Socket看作网络通信的一个端点,当使用Socket接口对网络通信编程时,Socket是网络通信过程中端点的抽象表示。程序在网络对话的每端都需要先建立一个Socket,即为一个Socket数据结构分配存储空间。
图1,图2是面向连接的和无连接的Socket使用综述图。
图1 面向连接协议时socket的使用
图2 无连接协议时socket的使用
一.系统调用说明:??
有关消息队列的系统调用均需下列头文件:
#include <sys/types.h>;
#include <sys/socket.h>;
#include <arpa/inet.h>;
#include <netinet/in.h>;
#include <fcntl.h>;
1。socket 调用
建立一个socket,即为一个socket数据结构分配存储空间。并初步定义socket的属性。调用格式如下:
int s, domain, type, protocol;
s = socket (domain, type, protocol);
domain:协议族和地址族,确定socket使用的一组协议。与三个符号常数有关:
PF_INET 表示互联网协议族,一般选用此参数。
PF_VNIX 表示VNIX内部协议族。
PF_NS 表示Xerox网络服务协议族。
type:通信类型,与两个符号常数有关:
SOCK_STREAM 以字节流形式通信,面向连接的协议使用这种形式。
SOCK_DGRAM 数据以独立的数据包形式流动,无连接协议使用这种形式。
protocol:指明此socket请求使用的协议,可以使用相关符号常数来表示:
IPPROTO_TCP 表示TCP协议。
IPPROTO_UDP 表示UDP协议。
s:如调用成功,返回socket描述符。否则返回-1。
2。connect调用
启动和远地主机的直接连接,只有面向连接的客户程序使用此函数。调用格式如下:
int s, namelen;
struct sockaddr *name;
int connect (s, name, namelen);
s: socket句柄,是socket函数返回的socket描述符。
name: 远地socket地址,是一个指向特定socket地址结构的指针。此结构保存了一个地址族,一个协议端口和一个网络主机地址。
namelen: 地址长度,简单地告诉socket执行体远地socket地址结构(第二个参数)按字节的大小。
返回值: 连接成功返回0,不成功返回-1。
3。bind调用
配置一个socket的本地地址(包括主机地址和协议端口)。指明socket将使用本地的哪一个协议端口进行数据传送。面向连接的服务器程序,无连接的服务器程序,无连接的客户程序使用此函数。调用格式如下:
int s, namelen;
struct sockaddr *name;
int bind (s, name, namelen);
s: socket句柄,socket函数返回的socket描述符。
name: 本地socket地址,是指向特定socket地址结构的指针。指明用于通信的本地协议端口。
namelen: 本地socket地址结构的长度。
返回值: 调用成功返回0,不成功返回-1。
4。listen调用
建立一个输入数据队列,将到达本地的客户服务请求保存在此队列上,直到程序处理它们。面向连接的服务器程序使用此函数。调用格式如下:
int s, backlog;
int listen (s, backlog);
s: socket句柄,socket函数返回的socket描述符。
backlog: 定义最大队列长度,一般可定义为5。如果一个客户服务请求到来时,输入队列已满,此socket将拒绝连接请求客户程序将受到一个出错信息。
返回值: 调用成功返回0,不成功返回-1。
5。accept调用
使服务器进程进入睡眠状态,等待客户进程的连接请求。收到连接请求后将建立一个新的socket,并将新socket和客户进程连接起来。即将请求连接的客户进程的地址配置到新的socket中去,而初始socket仍具有任意网络地址,因此它可继续接收进来的服务请求。以后发送和接收数据都使用这个新建的socket。面向连接的服务器进程使用此函数。调用格式如下:
int s, addrlen;
struct sockaddr *addr;
int accept (s, addr, addrlen);
s: socket句柄,socket函数返回的socket描述符。在此socket的基础上建立一个新socket 。
addr: 用于存放客户程序socket地址。
addrlen: 客户程序socket地址长度。
返回值: 调用成功返回新建socket的描述符,否则返回-1。
6。write,writev,send,sendto,sendmsg调用
Socket API提供这五个函数来发送数据。在SCO UNIX环境中支持其中三个:面向连接的协议使用write,send两个函数。在面向连接的协议中,程序使用connect函数配置一个面向连接的socket,此socket包含远地主机的地址信息,因此,在传送数据时不需再指明目的地址。无连接的协议使用sendto,函数,在函数调用中需指明目的地址。调用格式如下:
int s, buflen, flags, namelen;
char buf [n];
struct sockaddr *name;
int write (s, buf, buflen);
int send (s, buf, buflen, flags);
int sendto (s, buf, buflen, flags, name, namelen);
s: socket句柄,accept函数返回的socket描述符。
buf: 指向一个包含传输信息的数据缓冲区。
buflen: 指明传送数据缓冲区的大小。
flags: 传输控制标志,可将数据设置为优先级较高的带外数据传送
name: 远地socket地址。
namelen: 远地socket地址长度。
返回值: 调用成功返回发送数据的长度(以字节为单位),否则返回-1。
7.read,readv,reav,recvfrom,recvmsg调用 socket API提供五个与传送数据函数对应的接收函数:read,readv,reav,recvfrom和recvmsg。SCO UNIX环境中支持其中两个:面向连接的协议使用read函数,它与write函数对应。无连接的协议使用recvfrom,它与sendto函数相对应,需在参数中指明地址。调用格式如下:
int s, buflen;
char buf [n];
int read (s, buf, buflen);
int recvfrom (s, buf, buflen, flags, name, namelen);
s: socket句柄,accept函数返回的socket描述符。
buf: 指向一个包含传输信息的数据缓冲区。
buflen: 指明传送数据缓冲区的大小。
flags: 传输控制标志,可将数据设置为优先级较高的带外数据传送。
name: 远地socket地址。
namelen: 远地socket地址长度。
返回值: 调用成功返回接收数据的长度(以字节为单位),否则返回 -1。
二.示例程序
示例程序可在SCO UNIX环境下编译,运行,分为服务器程序和客户程序,均采用面向连接的协议。可在网络或单机上运行(将程序中定义的IP地址根据情况修改)。与/usr/lib/libsocket.a一起编译。运行时先启动server进程,再启动client进程,按提示输入需传送的字符,即可看到传送结果。
服务器程序:
#include <fcntl.h>;
#include <netinet/in.h>;
#include <arpa/inet.h>;
#include <sys/types.h>;
#include <sys/socket.h>;
#define SERV_TCP_PORT 6000 //定义服务器的PORT端口
int sockfd,newsockfd;
err_dump(char *string)
{
printf("%s\n",string);
perror("system error message:" ;
close(sockfd);
close(newsockfd);
}
main()
{
int client,n;
char packet[80],*HELLO="hello",*cli_addr_str;
ushort cli_port;
struct sockaddr_in serv_addr,cli_addr;
if ((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
printf("socket error.\n" ; //建立socket
bzero((char *)&serv_addr,sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(SERV_TCP_PORT);
//配置地址结构serv_addr
if(bind(sockfd,(struct sockaddr*)&serv_addr,sizeof(serv_addr))<0)
//指明socket的数据传送端口地址
{err_dump("Server:bind error." ;exit(1);}
listen(sockfd,5); //建立数据输入队列
for (;
{
strcpy(packet,"\0" ;
client=sizeof(cli_addr);
newsockfd=accept(sockfd,(struct sockaddr *)&cli_addr,&client);
//等待客户进程的连接请求
if (newsockfd<0)
{err_dump("Server:accept error." ;exit(1);}
n=read(newsockfd,packet,80); //读入客户进程传送的数据
packet[n]='\0';
cli_addr_str=inet_ntoa(cli_addr.sin_addr.s_addr);
cli_port=ntohs(cli_addr.sin_port); //转换地址格式,用于显示
printf("packet from remote ip address:%s.\nremote port:%d.\n",\
cli_addr_str,cli_port);
printf("packet length:%d bytes.\n",n);
printf("packet=%s.\n",packet);
write(newsockfd,HELLO,6); //发送回应信息
close(newsockfd);
}
}
客户程序:
#include <fcntl.h>;
#include <netinet/in.h>;
#include <sys/types.h>;
#include <sys/socket.h>;
#define HOST_ADDR "134.162.33.50"
#define HOST_PORT 6000
int sockfd;
err_dump(char *string)
{
printf("%s\n",string);
perror("system error message:" ;
close(sockfd);
}
main()
{
char packet[80],bpacket[80],recepack[6],quit[]={'q','\0'};
struct sockaddr_in serv_addr;
int sockfd,connectfd,bindfd,writefd,n;
bzero((char *)&serv_addr,sizeof(serv_addr));
serv_addr.sin_family = PF_INET;
serv_addr.sin_addr.s_addr = inet_addr(HOST_ADDR);
serv_addr.sin_port = htons(HOST_PORT);
for(;
{
printf("input string to send or 'q' to quit.\n" ;
gets(packet);
if (strcmp(packet,quit)==0)
exit(0);
if ((sockfd = socket(PF_INET,SOCK_STREAM,0))<0) //建立socket
{err_dump("socket error." ;exit(1);}
if ((connectfd = connect(sockfd,(struct sockaddr *)&serv_addr,\
sizeof(serv_addr)))<0) //连接服务器进程
{err_dump("connect error.");exit(1);}
if ((writefd=write(sockfd,packet,strlen(packet)))<0)//发送信息
{err_dump("write error.");exit(1);}
printf ("sockfd=%d,bindfd=%d,connectfd=%d,writefd=%d.\n",\
sockfd,bindfd,connectfd,writefd);
strcpy (recepack,'\0');
read(sockfd,recepack,80); //读回应信息
n = strlen(recepack);
printf("read len=%d.\n",n);
printf("packet from server:%s.\n",recepack);
close(sockfd);
}
} |
|