Chinaunix
标题:
UNIX系统TCP/IP Socket接口的应用
[打印本页]
作者:
ccbxlb
时间:
2003-02-14 14:35
标题:
UNIX系统TCP/IP Socket接口的应用
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);
}
}
作者:
小马
时间:
2003-02-14 14:39
标题:
UNIX系统TCP/IP Socket接口的应用
已看过多谢
unix下编写socket程序的一般步骤
在unix下写socket程序可能是最方便,你只要掌握其一般步骤,就可以松的写出面向传输层的应用。
1、理解几个常用的socket函数
#include
#include
int socket(int domain,int type,int portocol);
domain指所使用的协议族(family)可以为AF_UNIX和AF_INET,一般只用AF_INET(指Internet)type指所用的传输类型,可以为SOCK_STERAM(面向连接的TCP),和SOCK_DGRAM(面向无连接的udp)
int bind(int s,const struct sockaddr *address,size_t address_len);
s为socket返回的文件描述符
address为协议族名称和其他信息
具体结构为struct sockaddr_in{
short sin_family;/*协议族
u_short sin_port;/*端口*/
struct in_addr sin_addr;/*地址*/
char sin_zero[8];
};
int listen(int s,int backlog);
backlog为容许的请求数目
int accept(int s,struct sockaddr *address,int *address_len);
这里的前两个参数同上
addres_len是要传递一个记有结构大小的地址
int connect(int s,struct sockaddr *address,size_t address_len);
这里的参数意义同bind
2.理解建立程序的一般调用过程
要建立一个处理连接的服务器端程序,首先要调用socket函数创建一个socket,返回一个文件句柄fd,使以后对它的操作就象对普通文件设备一样读写。
由于是服务器端必须对一个断口进行监听其他机器的请求,所以接下去调用bind函数,传入刚才的fd,定义好地址和端口,由于是要接受来自任何host的连接所以应讲sin_addr赋为INADDR_ANY,port为你所设定的端口。
注意:这里的地址和端口是网络字节顺序,所以要调用htonl,htons完成主机字节顺序
到网络字节的转变
接下来就是监听listen,调用accept接受来自客户端的请求,accpet返回连接后的文件描述符,你就可以用它进行收发信息(对应于read,write)这样的一个过程就是socket->;bind->;listen->;accpet->;Read,write
而对于客户端则是socket->;connect->;read,write
3.一个完整的程序
#include
#include
#include /*包含有htons等函数的头文件*/
#include
#include
void main()
{
int listenfd,clifd;
long pid;
struct sockaddr_in myaddr,cliaddr;
int ret;
int len;
listenfd=socket(AF_INET,SOCK_STREAM,0);
if (listenfd<0)
{
perror("socket error"
;
exit(-1);
}
myaddr.sin_family=AF_INET;
myaddr.sin_addr.s_addr=htonl(INADDR_ANY);
myaddr.sin_port=htons(888
;
ret=bind(listenfd,(struct sockaddr *)&myaddr,sizeof(myaddr));
if (ret<0)
{
perror("bind error"
;
exit(-1);
}
listen(listenfd,10);
len=sizeof(struct sockaddr);
while(1)
{
clifd=accept(listenfd,(struct sockaddr*)&cliaddr,&len);
/*注意accept的第三个参数也是地址*/
if(clifd==-1)
{
perror("accept error"
;
continue;
}
printf("connect from %s %d\n",inet_ntoa(cliaddr.sin_addr.s_addr),ntohs(cliaddr.sin_port));
switch(pid=fork())
{
case 0: /*子进程*/
close(listenfd);
;/*子进程进行其他的操作*/
close(clifd);
exit(0);
break;
case -1:
perror("fork error"
;
break;
default:/*父进程*/
close(clifd);
break;
}
}
}
4.程序说明
该程序的功能是监听8888端口的连接,对所有的对8888端口的连接显示出地址和对方的端口号该程序在sco unix下调试通过,在其他unix和linux平台请注意inet_ntoa,htons函数所应在的头文件的名称
同时该程序用到了并发的观点,因为accept,read,write均为阻塞(block)的函数,一旦进程block将不能处理其他请求,所以用主进程进行listen,由子进程进行负责对客户端传输数据.
你可以在同一台unix机器用telnet localhost 8888进行观察程序会输出connect from 127.0.0.1 xxxx
作者:
zengcarter
时间:
2003-02-14 14:39
标题:
UNIX系统TCP/IP Socket接口的应用
恩 不错!
作者:
lykxg
时间:
2011-03-09 21:35
不错 认真学习
作者:
snow888
时间:
2011-03-23 11:08
作者:
saintdragon
时间:
2011-03-24 20:48
不错。支持一下!
作者:
sunlan
时间:
2011-03-25 15:51
挖坟?
欢迎光临 Chinaunix (http://bbs.chinaunix.net/)
Powered by Discuz! X3.2