你好,欢迎来到电脑编程技巧与维护杂志社! 杂志社简介广告服务读者反馈编程社区  
合订本订阅
 
 
您的位置:杂志经典 / 编程语言
用Delphi开发Windows系统与Linux进程间通信软件
 

一、问题提出

  在Delphi的工具模板的Internet页上,提供了开发Internet应用程序的构件,其中TClinetSocketTServerSocket这两个构件用于开发基于客户/服务器(Client/Server)的应用软件,但是,仅使用这两个构件开发的软件,只能在Windwows系统中使用,为了能在Windows系统与Linux系统之间建立通信,必须在Linux系统中另外开发一个进程。

二、实现方法

  Linux拥有POSIX.1标准库函数,利用socket()bind()listen()这几个库函数可以非常方便地实现服务器/客户机模型。由于在Windows环境下的网络应用系统编程接口Windows Socket支持TCP/IP通信,因此,很容易利用SocketWindows系统与Linux系统之间建立通信。

  下面用一个例子说明实现方法。

  在Linux系统建立一个服务器进程,完成以下工作:

    1、打开一个已知的监听端口。

    2、在监听端口上监听客户机的连接请求,当有一客户机请求连接时建立连接线路并返回通信文件描述符。

    3、父进程创建一子进程,父进程关闭通信文件描述符并继续监听端口上的客户机的连接请求。

    4、子进程通过通信文件描述符与客户机进行通信:首先读取客户机请求,然后根据不同请求发送不同内容,通信结束后终止子进程并关闭通信文件描述符。

  在Windows系统建立一个客户机程序,完成以下工作:

    1、输入试卷代号及服务器名。

    2、进行连接,连接失败给出错误信息。

    3、连接成功后,向服务器发送请求信息,并接收服务器信息。

    4、在客户端程序窗口显示服务器信息,并终止连接。

三、服务器程序

  在Linux中设计网络程序,以下函数是核心库函数,它们的用法如下:

1socket()

调用方式:

#include <sys/types.h>

#include <sys/socket.h>

int socket(int domain,int type,int protocol);

简要说明:

  该函数为通信创建一个端口,正常调用将返回一个文件描述符,错误调用将返回-1并设置errnodomain参数有两种选择:AF_UNIXAF_INET,其中AF_INETInternet通信协议。type参数也有两种选择:SOCK_STREAM用于TCPSOCK_DGRAM用于UDPprotocol参数通常为0

2bind()

调用方式:

#include <sys/types.h>

#include <sys/socket.h>

int bind(int s,const struct sockaddr *address,size_t address_len);

简要说明:

   该函数把socket返回的套接口端口与网络上的物理位置相关联。bind正常调用返回0,出错返回-1。此函数有三个参数:其中ssocket调用返回的文件描述符,*address设置了与网络上的物理位置相关的信息,它的类型是struct sockaddr,但在Internet上它是struct sockaddr_in。在socket.hstruct sockaddr_in定义为:

struct sockaddr_in{

    short sin_family;

    u_short sin_port;

    struct in_addr sin_addr;

    char sin_zero[8];

};

sin_family一般为AF_INETsin_port为端口号,sin_addr将置为INADDR_ANY。这三个值设置完成后*address参数才有意义。在编写代码时,应先设置*address参数内部各成员变量的值,再调用bind

3listen()

调用方式:

#include <sys/types.h>

#include <sys/socket.h>

int listen(int s,int backlog);

简要说明:

  该函数使socket端口能够接受从客户机来的连接请求,正常调用返回0,出错返回-1s参数为socket产生的文件描述符,backlog为所能接受客户机的最大数目。socketbindlisten三个函数的综合调用最终在服务器上产生一个能接受客户机请求的监听文件描述符s

4accept()

调用方式:

#include <sys/types.h>

#include <sys/socket.h>

int accept(int s,struct sockaddr *address,int *address_len);

简要说明:

  当有客户机发出连接请求时,此函数初始化这个连接。正常调用返回与客户机通信的通信文件描述符,出错返回-1。参数ssocket调用返回的文件描述符,address将用来存储客户机的信息,此信息由accept填入,当与客户机连接时,客户机的地址与端口将填到此处。address_len是客户机地址长度的字节数,也由accept填入。

5htons()

调用方式:

#include <netinet/in.h>

uint16_t hotns(uint16_t host16bitvalue);

简要说明:

  将16位整数的主机字节序转换成网络字节序。在网络编程中常用于16位端口号的转换。

6fork()

调用方式:

#include <sys/types.h>

#include <unistd.h>

pid_t fork(void);

简要说明:

  fork的作用是拷贝父进程的内存映象来创建子进程,两个进程将接着fork后的指令继续执行。 事实上它返回两个进程控制号,对于父进程它返回子进程的进程ID,对于子进程它返回0

可用下边的代码调用fork

pid_t childpid;

if((childpid=fork())==-1){

   perror("The fork failed");

   exit(1);

   }

else if(childpid==0){

   调用子进程;

   }

else if(childpid>0){

   调用父进程;

   }

 

服务器程序代码如下:

 

//server.c源程序

#include <sys/types.h>

#include <sys/socket.h>

#include <unistd.h>

#include <stdio.h>

#include <netinet/in.h>

#include <netinet/tcp.h>

#include <stdlib.h>

#include <string.h>

#include <errno.h>

int main(void)

{

int listenfd,communfd,n;

struct sockaddr_in servaddr;

pid_t childpid;

char recieve[1024],buf[1024];

if((listenfd=socket(AF_INET,SOCK_STREAM,0))==-1)

{

   puts("Create socket error!\n");

   exit(1);

}

servaddr.sin_family=AF_INET;

servaddr.sin_addr.s_addr=INADDR_ANY;

servaddr.sin_port=htons(6623); //必须与客户机程序中设置的端口号一致

if(bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr))==-1)

{

   puts("Bind error!\n");

   exit(1);

}

if(listen(listenfd,200)==-1)

{

   puts("Listen error!\n");

   exit(1);

}

 

while(communfd=accept(listenfd,(struct sockaddr *)NULL,NULL))

{

   if((childpid=fork())==-1)

   {

      puts("Fork error!");

      exit(1);

   }

   else if(childpid==0)

   {

      n=read(communfd,recieve,1024);

      if((n==1)&&(recieve[0]>='A'&&recieve[0]<='C'))

      {

         if(recieve[0]=='A')

         {

            strcpy(buf,"你好!A");

         }

         if(recieve[0]=='B')

         {

            strcpy(buf,"你好!B");

         }

         if(recieve[0]=='C')

         {

            strcpy(buf,"你好!C");

         }

         write(communfd,buf,strlen(buf));

      }

      close(communfd);

      break;

   }

   else if(childpid>0)

   {

      close(communfd);

   }

}

exit(0);

}

 

 

四、客户机程序设计

  新建工程(Client),对主窗体的属性设置如下:

    AutoSize:=False 不能改变大小

    Borderlcons.biMaximize:=False 不能最大化

    Position:=poDesktopCenter 运行后窗口处在桌面中央

  把表1中列出的构件加到主窗体中。

  对Memo1属性设置如下:

    ReadOnly:=True 数据只读

  对ClientSocket1的属性设置如下:

  Port:=6623,每个Socket对应一个端口号,必须与服务器程序中设置的端口号一致。

  其它使用缺省设置。

ClientSocket1的响应事件:

  OnConnect事件

根据用户提供的试卷代号(Edit1.Text)发送连接请求数据。

 

           表1 客户端窗体中的构件

构件

Name

Caption

说明

Tbutton

Button1

连接

 

Tbutton

Button2

退出

 

Tmemo

Memo1

 

信息显示窗口

TclientSocket

ClientSocket1

 

Socket插口

Tlabel

Label1

试卷代号:

 

Tedit

Edit1

 

 

Tlabel

Label2

服务器名:

 

Tedit

Edit2

 

 

 

  OnRead事件

  接收下服务器端数据,并在信息窗口中显示,终止与服务器的连接,OnError事件

显示错误信息。

  OnDisconnect事件

  显示服务器关闭信息。

Button1OnClick事件

  如果试卷代号,服务器名为空,则显示错误信息,否则设置ClientSocket1.HostEdit2.Text(服务器名),激活ClientSocket1

Button2OnClick事件

  显示关闭信息,根据用户的选择关闭客户机程序。

客户机程序清单如下:

unit main;

interface

uses

  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,

  ScktComp, StdCtrls, ExtCtrls;

type

  TForm1 = class(TForm)

    Button1: TButton;

    Button2: TButton;

    ClientSocket1: TClientSocket;

    Edit2: TEdit;

    Edit1: TEdit;

    Label1: TLabel;

    Label2: TLabel;

    Memo1: TMemo;

    procedure Button1Click(Sender: TObject);

    procedure ClientSocket1Connect(Sender: TObject;

      Socket: TCustomWinSocket);

    procedure Button2Click(Sender: TObject);

    procedure ClientSocket1Read(Sender: TObject; Socket: TCustomWinSocket);

    procedure ClientSocket1Disconnect(Sender: TObject;

      Socket: TCustomWinSocket);

    procedure ClientSocket1Error(Sender: TObject; Socket: TCustomWinSocket;

      ErrorEvent: TErrorEvent; var ErrorCode: Integer);

    private

    { Private declarations }

  public

    { Public declarations }

  end;

var

  Form1: TForm1;

  key:integer=0;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);

begin

   Edit1.Text:=Trim(Edit1.Text);

   Edit2.Text:=Trim(Edit2.Text);

   if (Length(Edit1.Text)=1) and (Length(Edit2.Text)> 0) then

      begin

         with ClientSocket1 do

            begin

               Host := Edit2.Text;

               Active := True;

            end;

      end

   else

      MessageBox(0,'输入项内容有误!','错误信息窗口',MB_OK);

end;

procedure TForm1.ClientSocket1Connect(Sender: TObject;

  Socket: TCustomWinSocket);

begin

   Socket.Sendtext(Edit1.Text);

end;

procedure TForm1.Button2Click(Sender: TObject);

begin

   if (MessageBox(0,'确定要退出系统吗?','退出窗口',MB_YESNO)=6) then

      Close;

end;

procedure TForm1.ClientSocket1Read(Sender: TObject;

  Socket: TCustomWinSocket);

var

   sa:string;

begin

   sa:=Socket.ReceiveText;

   memo1.Lines.Add(sa);

   key:=1;

   ClientSocket1.Active:=false;

end;

procedure TForm1.ClientSocket1Disconnect(Sender: TObject;

  Socket: TCustomWinSocket);

begin

   if key=0 then

      begin

         MessageBox(0,'无法连接服务器!网上学习系统将关闭!','错误信息窗口',MB_OK);

        // Close;

      end;

   key:=0;

end;

procedure TForm1.ClientSocket1Error(Sender: TObject;

  Socket: TCustomWinSocket; ErrorEvent: TErrorEvent;

  var ErrorCode: Integer);

begin

   MessageBox(0,'   错误:无法连接服务器!','信息窗口',MB_OK);Errorcode:=0;

end;

end.

五、程序运行

  在Linux中用gcc编译服务器源程序并取名为server,再运行,命令如下:

    gcc -o server server.c

    server &

  在Windows中编译好Client,该程序必须在装有Windows系统并配有TCP/IP协议的联网计算机上运行,并确定可与Linux系统联网,运行Client程序时应注意服务器名的输入,如本地机没有设定IP地址,而使用DHCP获得,则在输入服务器名时不能用服务器的IP地址,只能用服务器的计算机名。该方法已在Delphi 4 Client/Server Suiteredhat 6.2上编译运行通过。

 

 

  推荐精品文章

·2024年12月目录 
·2024年11月目录 
·2024年10月目录 
·2024年9月目录 
·2024年8月目录 
·2024年7月目录 
·2024年6月目录 
·2024年5月目录 
·2024年4月目录 
·2024年3月目录 
·2024年2月目录 
·2024年1月目录
·2023年12月目录
·2023年11月目录

  联系方式
TEL:010-82561037
Fax: 010-82561614
QQ: 100164630
Mail:gaojian@comprg.com.cn

  友情链接
 
Copyright 2001-2010, www.comprg.com.cn, All Rights Reserved
京ICP备14022230号-1,电话/传真:010-82561037 82561614 ,Mail:gaojian@comprg.com.cn
地址:北京市海淀区远大路20号宝蓝大厦E座704,邮编:100089