免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 5235 | 回复: 6
打印 上一主题 下一主题

[SCO UNIX] UNIX系统TCP/IP Socket接口的应用 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2003-02-14 14:35 |只看该作者 |倒序浏览
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 *)&amp;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*)&amp;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 *)&amp;cli_addr,&amp;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 *)&amp;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 *)&amp;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);

}

}

论坛徽章:
0
2 [报告]
发表于 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 *)&amp;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*)&amp;cliaddr,&amp;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

论坛徽章:
0
3 [报告]
发表于 2003-02-14 14:39 |只看该作者

UNIX系统TCP/IP Socket接口的应用

恩  不错!

论坛徽章:
0
4 [报告]
发表于 2011-03-09 21:35 |只看该作者
不错 认真学习

论坛徽章:
1
2015年辞旧岁徽章
日期:2015-03-03 16:54:15
5 [报告]
发表于 2011-03-23 11:08 |只看该作者

论坛徽章:
0
6 [报告]
发表于 2011-03-24 20:48 |只看该作者
不错。支持一下!

论坛徽章:
0
7 [报告]
发表于 2011-03-25 15:51 |只看该作者
挖坟?
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP