1 unit Unit1; 2 3 interface 4 5 uses 6 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 7 Dialogs, StdCtrls, WinSock, ExtCtrls; 8 9 type 10 TWorkThread = class(TThread) 11 private 12 FClientSocket: TSocket; 13 FMemo: TMemo; 14 FBuff: array [0..10] of Char; 15 procedure ShowRecv; 16 protected 17 procedure Execute;override; 18 public 19 constructor Create(ClientSocket: TSocket; Memo: TMemo); 20 end; 21 22 TForm1 = class(TForm) 23 Memo1: TMemo; 24 Button1: TButton; 25 Timer1: TTimer; 26 procedure Button1Click(Sender: TObject); 27 procedure Timer1Timer(Sender: TObject); 28 private 29 { Private declarations } 30 public 31 { Public declarations } 32 end; 33 34 var 35 Form1: TForm1; 36 ServerSocket: TSocket; 37 ClientSocket: TSocket; 38 39 implementation 40 41 {$R *.dfm} 42 43 procedure TForm1.Button1Click(Sender: TObject); 44 var 45 WSData: WSAData; 46 47 LocalAddr: TSockAddrIn; 48 SocketMode: Integer; 49 begin 50 //初始化Winsock 51 WSAStartUp($202, WSData); 52 //创建套接字 53 ServerSocket:= Socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 54 55 //设置LocalAddr的参数 56 LocalAddr.sin_family:= AF_INET; //IPV4族 57 LocalAddr.sin_addr.S_addr:= Inet_addr('127.0.0.1');//点分字符串格式的IP地址转换为互联网格式 58 LocalAddr.sin_port:= Htons(1077); //Host To Net Short,主机字节顺序转为网络字节顺序 59 //绑定本机IP地址、端口,绑定之前先设置好LocalAddr的参数 60 Bind(ServerSocket, LocalAddr, SizeOf(LocalAddr)); 61 62 //设置WinSock I/O模式 63 SocketMode:= 1; 64 IoCtlSocket(ServerSocket, FIONBIO, SocketMode); 65 66 //开始监听,最多同时监听5个连接 67 Listen(ServerSocket, 5); 68 Timer1.Enabled:= True; 69 end; 70 71 { TWorkThread } 72 73 constructor TWorkThread.Create(ClientSocket: TSocket; Memo: TMemo); 74 begin 75 inherited Create(False); 76 77 FClientSocket:= ClientSocket; 78 FMemo:= Memo; 79 end; 80 81 procedure TWorkThread.Execute; 82 var 83 FDSet: TFDSET; 84 begin 85 inherited; 86 87 FreeOnTerminate:= True; 88 89 while not Terminated do 90 begin 91 FD_Zero(FDSet); //初始化FDSet 92 FD_SET(FClientSocket, FDSet); //将FClientSocket加入FDSet 93 if Select(0, @FDSet, nil, nil, nil) > 0 then //测试FDSet中是否有可读的连接 94 begin 95 if Recv(FClientSocket, FBuff, SizeOf(FBuff), 0) > 0 then //如果收到消息就显示出来 96 Synchronize(ShowRecv) 97 else 98 Break; 99 end; 100 end; 101 end; 102 103 procedure TWorkThread.ShowRecv; 104 begin 105 FMemo.Lines.Add(FBuff); 106 end; 107 108 procedure TForm1.Timer1Timer(Sender: TObject); 109 begin 110 ClientSocket:= Accept(ServerSocket, nil, nil); 111 if ClientSocket <> INVALID_SOCKET then 112 TWorkThread.Create(ClientSocket, Memo1); 113 end; 114 115 end.
1.使用Select模型,要定义一个FDSet结构,将客户端Socket加入该结构,用Select函数轮询测试该Socket的读写状态。FDSet结构:
typedef struct fd_set {
u_int fd_count;
SOCKET fd_array[FD_SETSIZE];
} fd_set;
2.操作FDSet结构有4个预定义的宏:
FD_CLR(s, *set)
Removes the descriptor s from set.
FD_ISSET(s, *set)
Nonzero if s is a member of the set. Otherwise, zero.
FD_SET(s, *set)
Adds descriptor s to set.
FD_ZERO(*set)
Initializes the set to the null set.
3.上面的代码为了方便,用了一个Timer来Accept,然后为每一个连接进来的Socket创建线程,在线程中用Select测试是否可读,为使代码简单,没有处理异常的代码。