我写了一个很简单的阻塞模式客户端1. 启动SOCKET 和 创建阻塞模式的SOCKET
procedure Startup;
var
rWSAData: TWSADATA;
wSockVer: Word;
pIniFile: TIniFile;
begin
wSockVer := MAKEWORD(2,0);
if Winsock.WSAStartup( wSockVer, rWSAData ) <> 0 then
begin
Exit;
end;
m_dwSocket := Winsock.socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
end;2. 连接服务器
SevrConnect(dwSocket: DWORD; const pchAddr: PAnsiChar; wPort: WORD): Boolean;
var
uAddr: Integer;
pHost: PHostEnt;
rRemote: TSockAddrIn;
begin
Result := False;
// 没有创建要连接的SOCKET
if dwSocket = DWORD(INVALID_SOCKET) then Exit;
// 先假定szAddr是IP地址
uAddr := Winsock.inet_addr( pchAddr );
// 不是IP地址, 就认为这是主机的名称
if uAddr = INADDR_NONE then
begin
// 尝试用主机名称获得主机IP
pHost := Winsock.gethostbyname( pchAddr );
// 也不是主机名称就退出
if pHost = nil then
Exit;
// 得到以网络字节顺序排列的IP地址
uAddr := PInAddr(pHost^.h_addr_list^)^.S_addr;
end;
// 填写服务器地址信息
rRemote.sin_family := AF_INET;
// 端口
rRemote.sin_port := Winsock.htons( wPort );
// IP地址
rRemote.sin_addr.S_addr := uAddr;
// 连接到远程机
Result := (Winsock.connect( dwSocket, rRemote, Sizeof(rRemote) ) = 0 ); if not Result then
begin
// 判断是否已经连接中, 连接中也是已连接的状态
if WinSock.WSAGetLastError = WinSock.WSAEISCONN then
Result := True;
end;
end;3. 发送字符串信息
procedure ProcMsg(lpcszMsg: PChar);
begin
if SevrConnect( m_dwSocket, '127.0.0.1', 8888 ) then
WinSock.send( m_dwSocket, lpcszMsg[0], StrLen(lpcszMsg), 0 );
end;以上是客户端的处理过程, 创建时会启动并创建SOCKET, 发送时均会检测是否有连接, 然后发送字符串以下是非阻塞服务器的处理过程
// 1. 窗体创建时启动, 创建SOCKET 并监听
procedure TForm1.FormCreate(Sender: TObject);
var
rSin: sockaddr_in;
rWSAData: TWSADATA;
wSockVer: Word;
begin
m_nConnCnt := 0;
wSockVer := MAKEWORD(2,0);
if Winsock.WSAStartup( wSockVer, rWSAData ) <> 0 then
begin
Memo.Lines.Add( 'WSAStartUp Failed!' );
Exit;
end;
// 要监听首先必须创建SOCKET
m_dwSocket := Winsock.socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
if m_dwSocket = INVALID_SOCKET then
begin
Memo.Lines.Add( 'project can''t create socket!' );
Exit;
end; rSin.sin_family := AF_INET;
rSin.sin_port := htons( 8888 );
rSin.sin_addr.S_addr := INADDR_ANY;
if WinSock.bind( m_dwSocket, rSin, sizeof(rSin) ) = SOCKET_ERROR then
begin
Memo.Lines.Add( 'project can''t bind Port: 8765' );
WinSock.closesocket( m_dwSocket );
m_dwSocket := INVALID_SOCKET;
Exit;
end; if WinSock.WSAAsyncSelect( m_dwSocket, Handle, WM_SOCKET,
FD_ACCEPT or FD_CLOSE or FD_READ ) <> 0 then
begin
Memo.Lines.Add( 'project can''t set WM_SOCKET message!' );
WinSock.closesocket( m_dwSocket );
m_dwSocket := INVALID_SOCKET;
Exit;
end; if WinSock.listen( m_dwSocket, 5 ) = SOCKET_ERROR then
begin
Memo.Lines.Add('project listen fialed,' +
'please release the limition of firewall or AntiVirus soft');
WinSock.closesocket( m_dwSocket );
m_dwSocket := WinSock.INVALID_SOCKET;
Exit;
end;
Memo.Lines.Add( 'project listening' );
end;2. 在对应的消息处理过程
procedure TForm1.WMSOCKET(var Message: TMessage);
var
dwSocket, dwConnectSock: TSocket;
byBuf: array [0..4095] of Char;
sTmp, sIP: String;
nIndex: Integer;
wPort: Word;
SockAddr: sockaddr_in;
pHost: PHostEnt;
begin
dwSocket := Message.wParam;
Message.Result := 0;
if Winsock.WSAGETSELECTERROR(Message.lParam) <> 0 then
begin
Winsock.closesocket( dwSocket );
Memo.Lines.Add( 'TCP Asy connect error' );
Exit;
end; case Winsock.WSAGETSELECTEVENT(Message.lParam) of
FD_ACCEPT:
begin
Memo.Lines.Add( 'connect to Main' );
Inc(m_nConnCnt);
dwConnectSock := Winsock.accept( dwSocket, nil, nil );
case Winsock.WSAAsyncSelect( dwConnectSock, Handle,
WM_D7SOCKET, FD_READ or FD_WRITE or FD_CLOSE ) of
0:
begin
nIndex := Sizeof(SockAddr);
ZeroMemory( @SockAddr, nIndex );
WinSock.getpeername( dwConnectSock, SockAddr, nIndex );
wPort := WinSock.ntohs( SockAddr.sin_port );
sIP := WinSock.inet_ntoa( SockAddr.sin_addr );
nIndex := WinSock.inet_addr( PChar(sIP) );
pHost := WinSock.gethostbyaddr( @nIndex, 4, AF_INET );
if pHost = nil then
sTmp := '未知计算机'
else
sTmp := pHost^.h_name;
Memo.Lines.Add( sTmp + ' ' + sIP + ':' + IntToStr(wPort) + ' connected' );
CheckListBoxClientList.Checked[CheckListBoxClientList.Items.AddObject(
sTmp + ' ' + sIP + ':' + IntToStr(wPort), TObject(dwConnectSock) )] := True;
end;
else
begin
Memo.Lines.Add( 'Accept filed.' );
Winsock.closesocket( dwConnectSock );
Exit;
end;
end;
end;
FD_CLOSE:
begin
Winsock.closesocket( dwSocket );
nIndex := CheckListBoxClientList.Items.IndexOfObject( TObject(dwSocket) );
if nIndex <> -1 then
CheckListBoxClientList.Items.Delete( nIndex );
Dec(m_nConnCnt);
end;
FD_READ:
begin
Winsock.closesocket( dwSocket );
nIndex := CheckListBoxClientList.Items.IndexOfObject( TObject(dwSocket) );
if nIndex <> -1 then
CheckListBoxClientList.Items.Delete( nIndex );
Dec(m_nConnCnt);
end;
FD_READ:
begin
ZeroMemory( @byBuf[0], 4096 );
WinSock.recv( dwSocket, byBuf[0], 4096, 0 );
ShowMessage(String(@byBuf[0]));
end;
end;
end;窗体关闭的过程
procedure TForm1.FormClose(Sender: TObject;
var Action: TCloseAction);
var
i: Integer;
begin
Action := caFree;
for i := 0 to CheckListBoxClientList.Items.Count - 1 do
Winsock.closesocket( TSOCKET(CheckListBoxClientList.Items.Objects[i]) );
Winsock.closesocket( m_dwSocket );
end;出现的现象如下:
1. 很明显, 我在服务器是用ShowMessage, 按照阻塞来说, 客户端应该的send是不能返回的, 但实际上是直接返回了..
2. 在客户端进行连接, 关闭的时候, 服务器是很正确能够释放客户端的Socket的, 但是要是客户端不关闭, 直接关闭服务器, 出现的情况是: 客户端的连接并不会断开, SevrConnect 函数会一直返回True, 也就是要么是连接得上, 要么就是还在连接中.
3. 直接关闭服务器后, 再重开监听, 客户端只要不关闭, 就一直不会重新连接重启的服务器...这个是什么问题?
虽然看很多示例程序, 都是客户端和服务器均为阻塞, 要么均为非阻塞, 是不是由于我一边用阻塞, 一边用非阻塞, 就会有这种结果? 求高手解答....
procedure Startup;
var
rWSAData: TWSADATA;
wSockVer: Word;
pIniFile: TIniFile;
begin
wSockVer := MAKEWORD(2,0);
if Winsock.WSAStartup( wSockVer, rWSAData ) <> 0 then
begin
Exit;
end;
m_dwSocket := Winsock.socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
end;2. 连接服务器
SevrConnect(dwSocket: DWORD; const pchAddr: PAnsiChar; wPort: WORD): Boolean;
var
uAddr: Integer;
pHost: PHostEnt;
rRemote: TSockAddrIn;
begin
Result := False;
// 没有创建要连接的SOCKET
if dwSocket = DWORD(INVALID_SOCKET) then Exit;
// 先假定szAddr是IP地址
uAddr := Winsock.inet_addr( pchAddr );
// 不是IP地址, 就认为这是主机的名称
if uAddr = INADDR_NONE then
begin
// 尝试用主机名称获得主机IP
pHost := Winsock.gethostbyname( pchAddr );
// 也不是主机名称就退出
if pHost = nil then
Exit;
// 得到以网络字节顺序排列的IP地址
uAddr := PInAddr(pHost^.h_addr_list^)^.S_addr;
end;
// 填写服务器地址信息
rRemote.sin_family := AF_INET;
// 端口
rRemote.sin_port := Winsock.htons( wPort );
// IP地址
rRemote.sin_addr.S_addr := uAddr;
// 连接到远程机
Result := (Winsock.connect( dwSocket, rRemote, Sizeof(rRemote) ) = 0 ); if not Result then
begin
// 判断是否已经连接中, 连接中也是已连接的状态
if WinSock.WSAGetLastError = WinSock.WSAEISCONN then
Result := True;
end;
end;3. 发送字符串信息
procedure ProcMsg(lpcszMsg: PChar);
begin
if SevrConnect( m_dwSocket, '127.0.0.1', 8888 ) then
WinSock.send( m_dwSocket, lpcszMsg[0], StrLen(lpcszMsg), 0 );
end;以上是客户端的处理过程, 创建时会启动并创建SOCKET, 发送时均会检测是否有连接, 然后发送字符串以下是非阻塞服务器的处理过程
// 1. 窗体创建时启动, 创建SOCKET 并监听
procedure TForm1.FormCreate(Sender: TObject);
var
rSin: sockaddr_in;
rWSAData: TWSADATA;
wSockVer: Word;
begin
m_nConnCnt := 0;
wSockVer := MAKEWORD(2,0);
if Winsock.WSAStartup( wSockVer, rWSAData ) <> 0 then
begin
Memo.Lines.Add( 'WSAStartUp Failed!' );
Exit;
end;
// 要监听首先必须创建SOCKET
m_dwSocket := Winsock.socket( AF_INET, SOCK_STREAM, IPPROTO_TCP );
if m_dwSocket = INVALID_SOCKET then
begin
Memo.Lines.Add( 'project can''t create socket!' );
Exit;
end; rSin.sin_family := AF_INET;
rSin.sin_port := htons( 8888 );
rSin.sin_addr.S_addr := INADDR_ANY;
if WinSock.bind( m_dwSocket, rSin, sizeof(rSin) ) = SOCKET_ERROR then
begin
Memo.Lines.Add( 'project can''t bind Port: 8765' );
WinSock.closesocket( m_dwSocket );
m_dwSocket := INVALID_SOCKET;
Exit;
end; if WinSock.WSAAsyncSelect( m_dwSocket, Handle, WM_SOCKET,
FD_ACCEPT or FD_CLOSE or FD_READ ) <> 0 then
begin
Memo.Lines.Add( 'project can''t set WM_SOCKET message!' );
WinSock.closesocket( m_dwSocket );
m_dwSocket := INVALID_SOCKET;
Exit;
end; if WinSock.listen( m_dwSocket, 5 ) = SOCKET_ERROR then
begin
Memo.Lines.Add('project listen fialed,' +
'please release the limition of firewall or AntiVirus soft');
WinSock.closesocket( m_dwSocket );
m_dwSocket := WinSock.INVALID_SOCKET;
Exit;
end;
Memo.Lines.Add( 'project listening' );
end;2. 在对应的消息处理过程
procedure TForm1.WMSOCKET(var Message: TMessage);
var
dwSocket, dwConnectSock: TSocket;
byBuf: array [0..4095] of Char;
sTmp, sIP: String;
nIndex: Integer;
wPort: Word;
SockAddr: sockaddr_in;
pHost: PHostEnt;
begin
dwSocket := Message.wParam;
Message.Result := 0;
if Winsock.WSAGETSELECTERROR(Message.lParam) <> 0 then
begin
Winsock.closesocket( dwSocket );
Memo.Lines.Add( 'TCP Asy connect error' );
Exit;
end; case Winsock.WSAGETSELECTEVENT(Message.lParam) of
FD_ACCEPT:
begin
Memo.Lines.Add( 'connect to Main' );
Inc(m_nConnCnt);
dwConnectSock := Winsock.accept( dwSocket, nil, nil );
case Winsock.WSAAsyncSelect( dwConnectSock, Handle,
WM_D7SOCKET, FD_READ or FD_WRITE or FD_CLOSE ) of
0:
begin
nIndex := Sizeof(SockAddr);
ZeroMemory( @SockAddr, nIndex );
WinSock.getpeername( dwConnectSock, SockAddr, nIndex );
wPort := WinSock.ntohs( SockAddr.sin_port );
sIP := WinSock.inet_ntoa( SockAddr.sin_addr );
nIndex := WinSock.inet_addr( PChar(sIP) );
pHost := WinSock.gethostbyaddr( @nIndex, 4, AF_INET );
if pHost = nil then
sTmp := '未知计算机'
else
sTmp := pHost^.h_name;
Memo.Lines.Add( sTmp + ' ' + sIP + ':' + IntToStr(wPort) + ' connected' );
CheckListBoxClientList.Checked[CheckListBoxClientList.Items.AddObject(
sTmp + ' ' + sIP + ':' + IntToStr(wPort), TObject(dwConnectSock) )] := True;
end;
else
begin
Memo.Lines.Add( 'Accept filed.' );
Winsock.closesocket( dwConnectSock );
Exit;
end;
end;
end;
FD_CLOSE:
begin
Winsock.closesocket( dwSocket );
nIndex := CheckListBoxClientList.Items.IndexOfObject( TObject(dwSocket) );
if nIndex <> -1 then
CheckListBoxClientList.Items.Delete( nIndex );
Dec(m_nConnCnt);
end;
FD_READ:
begin
Winsock.closesocket( dwSocket );
nIndex := CheckListBoxClientList.Items.IndexOfObject( TObject(dwSocket) );
if nIndex <> -1 then
CheckListBoxClientList.Items.Delete( nIndex );
Dec(m_nConnCnt);
end;
FD_READ:
begin
ZeroMemory( @byBuf[0], 4096 );
WinSock.recv( dwSocket, byBuf[0], 4096, 0 );
ShowMessage(String(@byBuf[0]));
end;
end;
end;窗体关闭的过程
procedure TForm1.FormClose(Sender: TObject;
var Action: TCloseAction);
var
i: Integer;
begin
Action := caFree;
for i := 0 to CheckListBoxClientList.Items.Count - 1 do
Winsock.closesocket( TSOCKET(CheckListBoxClientList.Items.Objects[i]) );
Winsock.closesocket( m_dwSocket );
end;出现的现象如下:
1. 很明显, 我在服务器是用ShowMessage, 按照阻塞来说, 客户端应该的send是不能返回的, 但实际上是直接返回了..
2. 在客户端进行连接, 关闭的时候, 服务器是很正确能够释放客户端的Socket的, 但是要是客户端不关闭, 直接关闭服务器, 出现的情况是: 客户端的连接并不会断开, SevrConnect 函数会一直返回True, 也就是要么是连接得上, 要么就是还在连接中.
3. 直接关闭服务器后, 再重开监听, 客户端只要不关闭, 就一直不会重新连接重启的服务器...这个是什么问题?
虽然看很多示例程序, 都是客户端和服务器均为阻塞, 要么均为非阻塞, 是不是由于我一边用阻塞, 一边用非阻塞, 就会有这种结果? 求高手解答....
在send的后面加个recv...只要传送成功, 服务器均返回随便的一个东西让客户端recv, recv不到东西的时候, 就代表连接失效了, 然后就关闭socket再创建, 下次发送信息再连接..这样就正确了只是...为何明明服务器close客户端的连接socket, 怎么客户端的connect方法还是会返回正确的呢...有高手可以解答一下么...
你Close之后,再Connect,不是相当于再次连接服务器吗,当然会成功,服务器本身又没有关!(我没仔细看代码,只是猜测理解啊,呵呵)