那位讲讲WinSock编程的一些基本知识(100分)

  • 主题发起人 主题发起人 yukaikai
  • 开始时间 开始时间
Y

yukaikai

Unregistered / Unconfirmed
GUEST, unregistred user!
好难的感觉。

光是TSockAddr这些,我就不明白啊。
哪位能不能说说一些基本的知识。
 
找本书好好看呀,这个问题范围太广太大,不是谁一两句话就能说清的
 
WinSock介绍


作者:闻怡洋


文章来源:http://home.kzinfo.net/getc/myjc/20001022011.htm
上一篇 下一篇

--------------------------------------------------------------------------------
 Windows下网络编程的规范-Windows Sockets是Windows下得到广泛应用的、开放的、支持多种协议的
网络编程接口。从1991年的1.0版到1995年的2.0.8版,经过不断完善并在Intel、Microsoft、Sun、SGI、
Informix、Novell等公司的全力支持下,已成为Windows网络编程的事实上的标准。

Windows Sockets规范以U.C. Berkeley大学BSD UNIX中流行的Socket接口为范例定义了一套Micosoft
Windows下网络编程接口。它不仅包含了人们所熟悉的Berkeley Socket风格的库函数;也包含了一组针对
Windows的扩展库函数,以使程序员能充分地利用Windows消息驱动机制进行编程。Windows Sockets规范
本意在于提供给应用程序开发者一套简单的API,并让各家网络软件供应商共同遵守。此外,在一个特定
版本Windows的基础上,Windows Sockets也定义了一个二进制接口(ABI),以此来保证应用Windows
Sockets API的应用程序能够在任何网络软件供应商的符合Windows Sockets协议的实现上工作。因此这份
规范定义了应用程序开发者能够使用,并且网络软件供应商能够实现的一套库函数调用和相关语义。遵守
这套Windows Sockets规范的网络软件,我们称之为Windows Sockets兼容的,而Windows Sockets兼容实
现的提供者,我们称之为Windows Sockets提供者。一个网络软件供应商必须百分之百地实现Windows
Sockets规范才能做到现Windows Sockets兼容。任何能够与Windows Sockets兼容实现协同工作的应用程
序就被认为是具有Windows Sockets接口。我们称这种应用程序为Windows Sockets应用程序。Windows
Sockets规范定义并记录了如何使用API与Internet协议族(IPS,通常我们指的是TCP/IP)连接,尤其要
指出的是所有的Windows Sockets实现都支持流套接口和数据报套接口.应用程序调用Windows Sockets的
API实现相互之间的通讯。Windows Sockets又利用下层的网络通讯协议功能和操作系统调用实现实际的通
讯工作。它们之间的关系如图

通信的基础是套接口(Socket),一个套接口是通讯的一端。在这一端上你可以找到与其对应的一个名
字。一个正在被使用的套接口都有它的类型和与其相关的进程。套接口存在于通讯域中。通讯域是为了处
理一般的线程通过套接口通讯而引进的一种抽象概念。套接口通常和同一个域中的套接口交换数据(数据
交换也可能穿越域的界限,但这时一定要执行某种解释程序)。Windows Sockets规范支持单一的通讯
域,即Internet域。各种进程使用这个域互相之间用Internet协议族来进行通讯(Windows Sockets 1.1
以上的版本支持其他的域,例如Windows Sockets 2)。套接口可以根据通讯性质分类;这种性质对于用
户是可见的。应用程序一般仅在同一类的套接口间通讯。不过只要底层的通讯协议允许,不同类型的套接
口间也照样可以通讯。用户目前可以使用两种套接口,即流套接口和数据报套接口。流套接口提供了双向
的,有序的,无重复并且无记录边界的数据流服务。数据报套接口支持双向的数据流,但并不保证是可
靠,有序,无重复的。也就是说,一个从数据报套接口接收信息的进程有可能发现信息重复了,或者和发
出时的顺序不同。数据报套接口的一个重要特点是它保留了记录边界。对于这一特点,数据报套接口采用
了与现在许多包交换网络(例如以太网)非常类似的模型。

一个在建立分布式应用时最常用的范例便是客户机/服务器模型。在这种方案中客户应用程序向服务器程
序请求服务。这种方式隐含了在建立客户机/服务器间通讯时的非对称性。客户机/服务器模型工作时要求
有一套为客户机和服务器所共识的惯例来保证服务能够被提供(或被接受)。这一套惯例包含了一套协
议。它必须在通讯的两头都被实现。根据不同的实际情况,协议可能是对称的或是非对称的。在对称的协
议中,每一方都有可能扮演主从角色;在非对称协议中,一方被不可改变地认为是主机,而另一方则是从
机。一个对称协议的例子是Internet中用于终端仿真的TELNET。而非对称协议的例子是Internet中的
FTP。无论具体的协议是对称的或是非对称的,当服务被提供时必然存在"客户进程"和"服务进程"。一个
服务程序通常在一个众所周知的地址监听对服务的请求,也就是说,服务进程一直处于休眠状态,直到一
个客户对这个服务的地址提出了连接请求。在这个时刻,服务程序被"惊醒"并且为客户提供服务-对客户
的请求作出适当的反应。这一请求/相应的过程可以简单的用图表示。虽然基于连接的服务是设计客户机/
服务器应用程序时的标准,但有些服务也是可以通过数据报套接口提供的。

数据报套接口可以用来向许多系统支持的网络发送广播数据包。要实现这种功能,网络本身必须支持广播
功能,因为系统软件并不提供对广播功能的任何模拟。广播信息将会给网络造成极重的负担,因为它们要
求网络上的每台主机都为它们服务,所以发送广播数据包的能力被限制于那些用显式标记了允许广播的套
接口中。广播通常是为了如下两个原因而使用的:1. 一个应用程序希望在本地网络中找到一个资源,而
应用程序对该资源的地址又没有任何先验的知识。2. 一些重要的功能,例如路由要求把它们的信息发送
给所有可以找到的邻机。被广播信息的目的地址取决于这一信息将在何种网络上广播。Internet域中支持
一个速记地址用于广播-INADDR_BROADCAST。由于使用广播以前必须捆绑一个数据报套接口,所以所有收
到的广播消息都带有发送者的地址和端口。

Intel处理器的字节顺序是和DEC VAX处理器的字节顺序一致的。因此它与68000型处理器以及Internet的
顺序是不同的,所以用户在使用时要特别小心以保证正确的顺序。任何从Windows Sockets函数对IP地址
和端口号的引用和传送给Windows Sockets函数的IP地址和端口号均是按照网络顺序组织的,这也包括了
sockaddr_in结构这一数据类型中的IP地址域和端口域(但不包括sin_family域)。考虑到一个应用程序
通常用与"时间"服务对应的端口来和服务器连接,而服务器提供某种机制来通知用户使用另一端口。因此
getservbyname()函数返回的端口号已经是网络顺序了,可以直接用来组成一个地址,而不需要进行转
换。然而如果用户输入一个数,而且指定使用这一端口号,应用程序则必须在使用它建立地址以前,把它
从主机顺序转换成网络顺序(使用htons()函数)。相应地,如果应用程序希望显示包含于某一地址中的
端口号(例如从getpeername()函数中返回的),这一端口号就必须在被显示前从网络顺序转换到主机顺
序(使用ntohs()函数)。由于Intel处理器和Internet的字节顺序是不同的,上述的转换是无法避免的,
应用程序的编写者应该使用作为Windows Sockets API一部分的标准的转换函数,而不要使用自己的转换
函数代码。因为将来的Windows Sockets实现有可能在主机字节顺序与网络字节顺序相同的机器上运行。
因此只有使用标准的转换函数的应用程序是可移植的。

在MFC中MS为套接口提供了相应的类CAsyncSocket和CSocket,CAsyncSocket提供基于异步通信的套接口封
装功能,CSocket则是由CAsyncSocket派生,提供更加高层次的功能,例如可以将套接口上发送和接收的
数据和一个文件对象(CSocketFile)关联起来,通过读写文件来达到发送和接收数据的目的,此外
CSocket提供的通信为同步通信,数据未接收到或是未发送完之前调用不会返回。此外通过MFC类开发者可
以不考虑网络字节顺序和忽略掉更多的通信细节。

在一次网络通信/连接中有以下几个参数需要被设置:本地IP地址 - 本地端口号 - 对方端口号 - 对方IP
地址。左边两部分称为一个半关联,当与右边两部分建立连接后就称为一个全关联。在这个全关联的套接
口上可以双向的交换数据。如果是使用无连接的通信则只需要建立一个半关联,在发送和接收时指明另一
半的参数就可以了,所以可以说无连接的通信是将数据发送到另一台主机的指定端口。此外不论是有连接
还是无连接的通信都不需要双方的端口号相同。

在创建CAsyncSocket对象时通过调用
BOOL CAsyncSocket::Create( UINT nSocketPort = 0, int nSocketType = SOCK_STREAM, long lEvent
= FD_READ | FD_WRITE | FD_OOB | FD_ACCEPT | FD_CONNECT | FD_CLOSE, LPCTSTR lpszSocketAddress
= NULL )通过指明lEvent所包含的标记来确定需要异步处理的事件,对于指明的相关事件的相关函数调用
都不需要等待完成后才返回,函数会马上返回然后在完成任务后发送事件通知,并利用重载以下成员函数
来处理各种网络事件: 标记 事件 需要重载的函数
FD_READ 有数据到达时发生 void OnReceive( int nErrorCode );
FD_WRITE 有数据发送时产生 void OnSend( int nErrorCode );
FD_OOB 收到外带数据时发生 void OnOutOfBandData( int nErrorCode );
FD_ACCEPT 作为服务端等待连接成功时发生 void OnAccept( int nErrorCode );
FD_CONNECT 作为客户端连接成功时发生 void OnConnect( int nErrorCode );
FD_CLOSE 套接口关闭时发生 void OnClose( int nErrorCode );
我们看到重载的函数中都有一个参数nErrorCode,为零则表示正常完成,非零则表示错误。通过int
CAsyncSocket::GetLastError()可以得到错误值。


下面我们看看套接口类所提供的一些功能,通过这些功能我们可以方便的建立网络连接和发送数据。

BOOL CAsyncSocket::Create( UINT nSocketPort = 0, int nSocketType = SOCK_STREAM, long lEvent
= FD_READ | FD_WRITE | FD_OOB | FD_ACCEPT | FD_CONNECT | FD_CLOSE, LPCTSTR lpszSocketAddress
= NULL );用于创建一个本地套接口,其中nSocketPort为使用的端口号,为零则表示由系统自动选择,通
常在客户端都使用这个选择。nSocketType为使用的协议族,SOCK_STREAM表明使用有连接的服务,
SOCK_DGRAM表明使用无连接的数据报服务。lpszSocketAddress为本地的IP地址,可以使用点分法表示如
10.1.1.3。
BOOL CAsyncSocket::Bind( UINT nSocketPort, LPCTSTR lpszSocketAddress = NULL )作为等待连接方
时产生一个网络半关联,或者是使用UDP协议时产生一个网络半关联。
BOOL CAsyncSocket::Listen( int nConnectionBacklog = 5 )作为等待连接方时指明同时可以接受的连
接数,请注意不是总共可以接受的连接数。
BOOL CAsyncSocket::Accept( CAsyncSocket& rConnectedSocket, SOCKADDR* lpSockAddr = NULL, int*
lpSockAddrLen = NULL )作为等待连接方将等待连接建立,当连接建立后一个新的套接口将被创建,该套
接口将会被用于通信。
BOOL CAsyncSocket::Connect( LPCTSTR lpszHostAddress, UINT nHostPort );作为连接方发起与等待连
接方的连接,需要指明对方的IP地址和端口号。
void CAsyncSocket::Close( );关闭套接口。
int CAsyncSocket::Send( const void* lpBuf, int nBufLen, int nFlags = 0 )
int CAsyncSocket::Receive( void* lpBuf, int nBufLen, int nFlags = 0 );在建立连接后发送和接收
数据,nFlags为标记位,双方需要指明相同的标记。
int CAsyncSocket::SendTo( const void* lpBuf, int nBufLen, UINT nHostPort, LPCTSTR
lpszHostAddress = NULL, int nFlags = 0 )
int CAsyncSocket::ReceiveFrom( void* lpBuf, int nBufLen, CString& rSocketAddress, UINT&
rSocketPort, int nFlags = 0 );对于无连接通信发送和接收数据,需要指明对方的IP地址和端口号,
nFlags为标记位,双方需要指明相同的标记。
我们可以看到大多数的函数都返回一个布尔值表明是否成功。如果发生错误可以通过int
CAsyncSocket::GetLastError()得到错误值。

由于CSocket由CAsyncSocket派生所以拥有CAsyncSocket的所有功能,此外你可以通过BOOL
CSocket::Create( UINT nSocketPort = 0, int nSocketType = SOCK_STREAM, LPCTSTR
lpszSocketAddress = NULL )来创建套接口,这样创建的套接口没有办法异步处理事件,所有的调用都必
需完成后才会返回。

在上面的介绍中我们看到MFC提供的套接口类屏蔽了大多数的细节,我们只需要做很少的工作就可以开发
出利用网络进行通信的软件。

 
利用Delphi编写Socket通信程序


作者:不详


文章来源:yesky
上一篇 下一篇

--------------------------------------------------------------------------------
一、Delphi与Socket

  计算机网络是由一系列网络通信协议组成的,其中的核心协议是传输层的TCP/IP和UDP协
议。TCP是面向连接的,通信双方保持一条通路,好比目前的电话线,使用telnet登陆BBS,用
的就是TCP协议;UDP是无连接的,通信双方都不保持对方的状态,浏览器访问Internet时使用
的HTTP协议就是基于UDP协议的。TCP和UDP协议都非常复杂,尤其是TCP协议,为了保证网络传
输的正确性和有效性,必须进行一系列复杂的纠错和排序等处理。

  Socket是建立在传输层协议(主要是TCP和UDP)上的一种套接字规范,最初是由美国加州
Berkley大学提出,它定义两台计算机间进行通信的规范(也是一种编程规范),如果说两台计
算机是利用一个“通道“进行通信,那么这个“通道“的两端就是两个套接字。套接字屏蔽了
底层通信软件和具体操作系统的差异,使得任何两台安装了TCP协议软件和实现了套接字规范的
计算机之间的通信成为可能。

  微软的Windows Socket规范(简称winsock)对Berkley的套接字规范进行了扩展,利用标准
的Socket的方法,可以同任何平台上的Socket进行通信;利用其扩展,可以更有效地实现在
Windows平台上计算机间的通信。在Delphi中,其底层的Socket也应该是Windows的
Socket。Socket减轻了编写计算机间通信软件的难度,但总的说来还是相当复杂的(这一点在
后面具体会讲到);Inprise在Delphi中对Windows Socket进行了有效的封装,使得用户可以很
方便地编写网络通信程序。下面我们实例解读在Delphi中如何利用Socket编写通信程序。

  二、利用Delphi编写Socket通信程序。

  下面是一个简单的Socket通信程序,其中客户机和服务机是同一个程序,当客户机(服务
器)在一个memo1中输入一段文字然后敲入回车,该段文字就可以显示在服务器(客户机)的
memo2中,反之亦成立。具体步骤如下:

  1、新建一个form,任意命名,不妨设之为chatForm;放上一个MainMenu(在Standard栏
中),建立ListenItem、ConnectItem、Disconnect和Exit菜单项;在从Internet栏中选择
TServerSocket、TClientSocket添加到chatForm中,其中把TClientSocket的名字设为
ClientSocket, port设为1025,默认的active为false;把TServerSocket的名字设为
ServerSocket,port设为1025,默认的active为false,其他的不变;再放入两个memo,一个命
名为memo1,另外一个命名为memo2,其中把memo2的color设置为灰色,因为主要用来显示对方的
输入。下面我们一边编写代码一边解释原因。

  2、双击ListemItem。写入如下代码:

  procedure TChatForm.ListenItemClick(Sender: TObject);begin

  ListenItem.Checked := not ListenItem.Checked;

  if ListenItem.Checked then

  begin

  ClientSocket.Active := False;

  ServerSocket.Active := True;

  end

  else

  begin

  if ServerSocket.Active then

  ServerSocket.Active := False;

  end;end;该程序段的说明如下:当用户选择ListemItem时,该ListenItem取反,如果选中
的话,说明处于Listen状态,读者要了解的是:listen是Socket作为Server时一个专有的方
法,如果处于listen,则ServerSocket设置为活动状态;否则,取消listen,则关闭
ServerSocket。实际上,只有用户一开始选择该菜单项,表明该程序用作Server。反之,如果
用户选择ConnectItem,则必然作为Client使用。

  3、双击ConnectItem,敲入以下代码。

  procedure TChatForm.ConnectItemClick(Sender: TObject);begin

  if ClientSocket.Active then ClientSocket.Active := False;

  if InputQuery('Computer to connect to', 'Address Name:', Server) then

  if Length(Server) > 0 then

  with ClientSocket do

  begin

  Host := Server;

  Active := True;

  ListenItem.Checked := False;

  end;end;这段程序的主要功能就是当用户选择ConnectItem菜单项时,设置应用程序为客户
机,弹出input框,让用户输入服务器的地址。这也就是我们不一开始固定ClientSocket的host
的原因,这样用户可以动态地连接不同的服务器。读者需要了解的是主机地址只是Socket作为
客户机时具有的一个属性,Socket作为服务器时“一般“不用地址,因为它同本机绑定。

  4、在memo1的keydown方法中写入如下代码:

  procedure TChatForm.Memo1KeyDown(Sender: TObject; var Key: Word;

  Shift: TShiftState);begin

  if Key = VK_Return then

  if IsServer then

  ServerSocket.Socket.Connections[0].SendText(Memo1.Lines[Memo1.Lines.Count - 1])

  else

  ClientSocket.Socket.SendText(Memo1.Lines[Memo1.Lines.Count - 1]);end;该段代码的
作用很明显,就是开始发消息了。其中如果是Server的话,它只向第一个客户机发消息,由于
一个服务器可以连接多个客户机,而同客户机的每一个连接都由一个Socket来维持,因此
ServerSocket.Socket.Connnections数组中存储的就是同Client维持连接的Socket。在标准
Socket中,服务器方的Socket通过accept()方法的返回值获取维持同客户机连接的Socket,而发
送、接受消息的方法分别为send(sendto)和recv(recvfrom), Delphi对此进行了封装。

  5、其余代码的简要介绍。

  procedure TChatForm.ServerSocketAccept(Sender: TObject;

  Socket: TCustomWinSocket);begin

  IsServer := True;end;ServerSocket的Accept方法,当客户机第一次连接时完成,通过其
参数可以认为,它是在标准的accept方法后执行的,因为有TCustomWinSocket这个参数类型,
它应该是标准Server方Socket的返回值。

  procedure TChatForm.ClientSocketRead(Sender: TObject;

  Socket: TCustomWinSocket);begin

  Memo2.Lines.Add(Socket.ReceiveText);end;

  procedure TChatForm.ServerSocketClientRead(Sender: TObject;

  Socket: TCustomWinSocket);begin

  Memo2.Lines.Add(Socket.ReceiveText);end;这两段代码分别是服务器方和客户机方在收
到对方的消息时,由Delphi触发的,作用是在memo2中显示收到的消息。其中,
ClientSocketRead中的Socket实际上就是Socket本身,而在ServerSocketClientRead中的
Socket实际上是ServerSocket.Socket.Connection[]中的某个Socket。不过在Delphi中,对服
务器方的Socket进行了有效的封装。

  procedure TChatForm.ServerSocketClientConnect(Sender: TObject;

  Socket: TCustomWinSocket);begin

  Memo2.Lines.Clear;end;procedure TChatForm.ClientSocketDisconnect(Sender:
TObject;

  Socket: TCustomWinSocket);begin

  ListenItemClick(nil);end;这两段比较简单。其中ServerSocketClientConnect在
ServerSocket收到一个新的连接时触发。而ClientSocketDisconnect在ClientSocket发出
Disconncet时触发。

  procedure TChatForm.Exit1Click(Sender: TObject);begin

  ServerSocket.Close;

  ClientSocket.Close;

  Close;end;procedure TChatForm.Disconnect1Click(Sender: TObject);begin

  ClientSocket.Active := False;

  ServerSocket.Active := True;end;第一段为关闭应用程序。在标准Socket中,每个
Socket在关闭时,必须调用closesocket()方法,否则系统不会释放资源。而在
ServerSockt.Close和ClientSocket.Close中,系统内部肯定调用了closesocket()方法。

  三、标准Socket与Delphi中的Socket。

  标准的Socket的应用程序框架如下:Server方:Socket()[新建一个Socket]--Bind()[同
服务器地址邦定]--Listen()--Accept()--block wait--read()[接受消息,在windows
平台中,方法为send(TCP),或者是sendto(UDP)]--处理服务请求--Write()[发送消息,在
windows平台中,方法为send(TCP),或者为sendto(UDP)。

  Client方相对简单:Socket()--Connect()[通过一定的port连接特定的服务器,这是与
服务器建立连接]--Write()--Read()。

  Socket可以是基于TCP的,也可以是基于UDP,同时Socket甚至建立在其他的协议,比如
IPX/SPX,DECNet等。在新建一个Socket时,可以指定新建何类Socket。Bind()用来同服务器的
地址邦定,如果一个主机只有一个IP地址,实际上邦定的作用就相对多余了。Listen()开始监
听网络,Accept()用于接受连接,其返回值是保持同客户机联系的Socket。

  在Delphi中,对于Windows中的Socket进行了有效的封装。在Delphi中,按其继承关系,可
以分层两类:

  一、TComponent--TAbstractSocket--TCustomSocket--TCustomServerSocket--
TServerSocket TComponent--TAbstractSocket--TCustomSocket--TClientSocket

  二、直接从TObject继承过来: TObject--TCustomWinSocket--TServerWinSocket
TObject--TCustomWinSocket--TClientWinSocket TObject--TCustomWinSocket--
TServerClientWinSocket可以看出第一类建立在TCustomSocket基础上,第二类建立在
TCustomWinSocket的基础上。第一类建立在TComponet的基础上,第二类直接构建在TObject基
础上。因此如果用户非常熟悉Socket并且想要编写控制台程序时,可以使用TCustomWinScoket
类。

   同uses中可以看出,它们都在ScktComp.pas中实现,而在schtComp.pas中,则包含了
winsock.pas文件,如果继续深入winsock文件,在其中可以发现所有的Windows Socket的基本
方法。

   实际上,如果你了解了标准Socket的应用程序框架,对于使用Delphi编写Socket应用程序
也就得心应手了;这不是说你必须了解复杂的Socket中的标准函数,也没有必要,因为Delphi
已经为你做了很好的封装了,这也正是Delphi的强势所在,你只要了解那么一点点的基本框
架。

  这是我对Delphi中的Socket应用的理解,不足之处希望大家指正。同时也乐于为大家解答
Delphi中有关Socket的问题。



 
不错,正好练习练习
 
大家都说的不错,先从DELPHI自己带的控件开始学,以后在学API[:D]
 
上面所写都是利用控件(TClientSocket/TServerSocket或CAsyncSocket)实现的,它们都对
Socket API做了封装,你看不到底层的东西,所以觉得很神秘。其实Socket也就是几个API而已,
是比较简单的。

我举个TCP的例子(Console程序):
TCP Client:
-------------------------------------------------------
#include <stdio.h>
#include <sys/types.h>
#include <winsock2.h>
#include <memory.h>

#define SERVER_PORT 5432
#define MAX_LINE 256

void
main(int argc,char * argv[])
{

struct sockaddr_in sin;
struct hostent *hp;
char *host;
char buf[MAX_LINE];
int s;
int len;
WORD wVersionRequested;
WSADATA wsaData;
int err;

if (argc==2){
host = argv[1];
}
else {
fprintf(stderr,"usage:Client host/n");
exit(1);
}

wVersionRequested = MAKEWORD( 2, 0 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
exit(1);
}


/*translate host name into peer's IP address */
hp = gethostbyname(host);
if (!hp) {
fprintf(stderr, "simplex-talk unknow host: %s/n",host);
exit(1);
}

/*build address data structure */
memset((char *)&sin, 0,sizeof(sin));
sin.sin_family = AF_INET;
memcpy((char *)&sin.sin_addr,hp->h_addr_list[0], hp->h_length);
sin.sin_port = htons(SERVER_PORT);

/*active open */
if ((s = socket(PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET){
perror("simplex-talk:socket");
exit(1);
}
if (connect(s, (struct sockaddr *)&sin, sizeof(sin))!=0){
perror("simplex-talk:connect");
closesocket(s);
exit(1);
}
printf("connection established!/n");
/*main loop:get and send lines of text */
while (fgets(buf, sizeof(buf), stdin)) {
buf[MAX_LINE-1] = '/0';
len = strlen(buf) +1;
if ((send(s,buf,len,0))<0) {
printf("Socket error!/n");
closesocket(s);
exit(1);
}
}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
TCP Server:
--------------------
#include <stdio.h>
#include <sys/types.h>
#include <winsock2.h>
#include <memory.h>

#define SERVER_PORT 5432
#define MAX_PENDING 5
#define MAX_LINE 256
void
main()
{
struct sockaddr_in sin;
struct sockaddr_in sout;
char buf[MAX_LINE];
int len;
int lens;
SOCKET s,new_s;
WORD wVersionRequested;
WSADATA wsaData;
int err;

wVersionRequested = MAKEWORD( 2, 0 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
/* Tell the user that we could not find a usable */
/* WinSock DLL. */
exit(1);
}

/*build address data structure */
memset((char *)&sin,0,sizeof(struct sockaddr));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons(SERVER_PORT);
/*setup passive open*/
if ((s=socket(AF_INET,SOCK_STREAM,0)) == INVALID_SOCKET ){
perror("simplex-talk:socket");
exit(1);
}
if ((bind(s, (struct sockaddr *)&sin, sizeof(sin))) != 0){
perror("simplex-talk:bind");
exit(1);
}
if ((listen(s,MAX_PENDING))!=0){
perror("simplex-talk:listen");
exit(1);
}

/*wait for connection, then receive and print text */
while(1){
memcpy((char *)&sout,(char *)&sin,sizeof(struct sockaddr_in));
lens=sizeof(sout);
printf("waiting.../n");
if ((new_s =accept(s,(struct sockaddr *)&sout,&lens)) == INVALID_SOCKET) {
perror("simplex-talk:accept");
exit(1);
}
printf("connection established!/n");
while((len=recv(new_s,buf,sizeof(buf),0))>0){
fputs(buf,stdout);
}
printf("socket closed!/n");
closesocket(new_s);
}
}
============================================
我再举个Delphi的UDP的例子(图形界面程序):
unit udp;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, winsock,
StdCtrls;

const
WM_SOCK = WM_USER + 1; //自定义windows消息
UDPPORT = 646; //设定UDP端口号

type
Tfrmmain = class(TForm)
Button1: TButton;
Edit1: TEdit;
Memo1: TMemo;
Edit2: TEdit;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
s: TSocket;
addr: TSockAddr;
FSockAddrIn : TSockAddrIn;
//利用消息实时获知UDP消息
procedure ReadData(var Message: TMessage);
message WM_SOCK;
public
{ Public declarations }
procedure SendData(Content: String);
end;

var
frmmain: Tfrmmain;

implementation

{$R *.DFM}

procedure Tfrmmain.FormCreate(Sender: TObject);
var
TempWSAData: TWSAData;
//optval: integer;
begin
// 初始化SOCKET
if WSAStartup($101, TempWSAData)=1 then
showmessage('StartUp Error!');

s := Socket(AF_INET, SOCK_DGRAM, 0);
if (s = INVALID_SOCKET) then //Socket创建失败
begin
showmessage(inttostr(WSAGetLastError())+' Socket创建失败');
CloseSocket(s);
// exit;
end;
//发送方SockAddr绑定
addr.sin_family := AF_INET;
addr.sin_addr.S_addr := INADDR_ANY;
addr.sin_port := htons(UDPPORT);
if Bind(s, addr, sizeof(addr)) <> 0 then
begin
showmessage('bind fail');
end;
{optval:= 1;
if setsockopt(s,SOL_SOCKET,SO_BROADCAST,pchar(@optval),sizeof(optval)) = SOCKET_ERROR then
begin
showmessage('无法进行UDP广播');
end;}
WSAAsyncSelect(s, frmmain.Handle , WM_SOCK, FD_READ);
//接收端SockAddrIn设定
FSockAddrIn.SIn_Family := AF_INET;
FSockAddrIn.SIn_Port := htons(UDPPORT);
label3.Caption := '端口:'+inttostr(UDPPORT);
end;

procedure Tfrmmain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
CloseSocket(s);
end;

procedure Tfrmmain.ReadData(var Message: TMessage);
var
buffer: Array [1..4096] of char;
len: integer;
flen: integer;
Event: word;
value: string;
begin
flen:=sizeof(FSockAddrIn);
FSockAddrIn.SIn_Port := htons(UDPPORT);
Event := WSAGetSelectEvent(Message.LParam);
if Event = FD_READ then
begin
len := recvfrom(s, buffer, sizeof(buffer), 0, FSockAddrIn, flen);
value := copy(buffer, 1, len);
Memo1.Lines.add(value)
end;
end;

procedure Tfrmmain.SendData(Content: String);
var
value{,hostname}: string;
len: integer;
begin

FSockAddrIn.SIn_Addr.S_addr := inet_addr(pchar(Edit1.text)); //INADDR_BROADCAST; //INADDR_BROADCAST = -1 ?
value := Content;
len := sendto(s, value[1], Length(value), 0, FSockAddrIn, sizeof(FSockAddrIn));
if (WSAGetLastError() <> WSAEWOULDBLOCK) and (WSAGetLastError() <> 0) then
showmessage(inttostr(WSAGetLastError()));
if len = SOCKET_ERROR then
showmessage('send fail');
if len <> Length(value) then
showmessage('Not Send all');
end;

procedure Tfrmmain.Button1Click(Sender: TObject);
begin
senddata(Edit2.text);
end;

end.

======================================
不要被控件或类的封装所迷惑,Socket不过如此。

 
zw84611,还请对程序做一些解释,或者,介绍一下Socket API函数,谢谢!
 
yangxiao_jiang@download-city.com
给我来信,给你一本电子书。
 
yangxiao_jiang,已经给您电子邮件!
 
htonl() 把32位的数字从主机字节顺序转换到网络字节顺序。
htons() 把16位的数字从主机字节顺序转换到网络字节顺序。

什么是“主机字节顺序”和“网络字节顺序”呢?
 
多人接受答案了。
 
后退
顶部