Api实现Socket

5 篇文章 0 订阅
program Project1;
{$APPTYPE CONSOLE}

uses
  windows,
  Messages,
  SysUtils,
  Variants,
  Classes,
  Winsock2,
  ChartoUnicode in 'ChartoUnicode.pas';

type
  Runner = function(sc: TSocket; argc: integer; argv: array of Pchar): integer;
  // 函数指针

type
  command = record
    cmd: Pansichar;
    run: Runner;
  end;

var
  wsadata: Twsadata;
  slisten: TSocket;
  i: integer;
  bStop: Boolean = FALSE;
  bok: Boolean = FALSE;
  ID: DWORD;
  ch: char;

 

 //命令函数
function show_time(sc: TSocket; argc: integer; argv: array of Pchar): integer;
var
  stime: SYSTEMTIME;
  buf: array [0 .. 1024] of char;
  len: integer;
  str: string;
begin
  GetSystemTime(stime);
  str := format('%d-%d-%d %d:%d:%d', [stime.wYear, stime.wMonth, stime.wDay, stime.wHour, stime.wMinute, stime.wSecond]);
  FillChar(buf, sizeof(buf), '');
  StrPCopy(buf, str);
  len := send(sc, buf, Length(buf), 0);
  Result := len;
end;

function good_bay(sc: TSocket; argc: integer; argv: array of Pchar): integer;
begin
  closesocket(sc);
  Result := 0;
end;

// 会话线程
// 所有关系到收发数据的缓冲都属于简单的char类型。也就是说,这些函数没有“Unicode”版本。
function TalkThread(p: Pointer): DWORD; stdcall;
const
  cmd_time: command = (cmd: 'time'; run: show_time);
  cmd_bye: command = (cmd: 'bye'; run: good_bay);
  // g_cmd_tab: array [0 .. 1] of command = (cmd_time, cmd_bye);
var
  sclient: TSocket;
  len, pos: integer;
  buffer: array [0 .. 1024] of char;
  c: char;
  bok, brun: Boolean;
  str: string;
  argc: integer;
  argv: array [0 .. 100] of Pchar;
  g_cmd_tab: array [0 .. 1] of command;

  // 关于记录型数组赋值
  // const
  // a1:command=(cmd:'123';run:show_time);
  // 如果其参数为记录数组(如下),那我将如何按上述样式传参
  // procedure   Cell(cells :array   of command);
  // 应用Cell([a1,a2]);
begin
  sclient := TSocket(p);
  bok := True;
  while (CompareText(buffer, 'exit') <> 0) do // 循环收发交互
  begin
    // ①服务器先发至人
    FillChar(buffer, sizeof(buffer), '');
    StrPCopy(buffer, 'F:\Socket>'); // xtractFilePath(ParamStr(0))
    len := send(sclient, buffer, Length(buffer), 0);
    if len <= 0 then
    begin
      bok := FALSE;
      break;
    end;

    // ②客户端发话
    pos := 0;
    FillChar(buffer, sizeof(buffer), '');
    c := #0;
    while (c <> #32) do // 按Space结束
    begin
      len := recv(sclient, c, sizeof(c), 0);
      if len = 0 then
      begin
        bok := FALSE;
        break;
      end;

      //if (c <> #32) then // 字符加入数组buffer
      begin
        buffer[pos] := c;
        Inc(pos);
      end;
    end;
    if not bok then
    begin
      break;
    end;
    Writeln('客户端说:' + buffer); // recv(sclient, buffer, length(buffer), 0)

    c := #10;
    send(sclient, c, Length(c), 0); // 显示回车
    c := #13;
    send(sclient, c, Length(c), 0);

    // 实例化
    argc := 1;
    argv[0] := buffer;

    g_cmd_tab[0] := cmd_time;
    g_cmd_tab[1] := cmd_bye;
    // g_cmd_tab[0].cmd := 'time';
    // g_cmd_tab[0].run := show_time;
    // g_cmd_tab[1].cmd := 'bye';
    // g_cmd_tab[1].run := good_bay;

    // ③服务器回应
    if argv[0] <> 'exit' then
    begin
      i := (sizeof(g_cmd_tab)) div (sizeof(g_cmd_tab[0]));
      brun := FALSE;
      for pos := 0 to i - 1 do
      begin
        if (CompareText(argv[0], g_cmd_tab[pos].cmd) = 0) then
        begin
          g_cmd_tab[pos].run(sclient, argc, argv);
          brun := True;
          break;
        end;
      end;
      if brun = FALSE then
      begin
        StrPCopy(buffer, 'input erro!');
        send(sclient, buffer, Length(buffer), 0);
      end;

      c := #10;
      send(sclient, c, Length(c), 0);
      c := #13;
      send(sclient, c, Length(c), 0);
    end; { if }
  end; { while }

  closesocket(sclient);
  Result := 0;
end;

// 循环侦听线程
function ListenThread(p: Pointer): DWORD; stdcall;
var
  slisten, sclient: TSocket;
  addr: TSockAddrIn;
  IP: ansistring;
  len: integer;
  IDD: DWORD;
begin
  slisten := TSocket(p);
  len := sizeof(addr);

  // 用一个循环来反复判断是否有客户端请求,如果存在请求就创建一个用来接受数据的读取线程
  while (bStop = FALSE) do
  begin
    sclient := accept(slisten, PSockAddr(@addr), @(len));
    if sclient <> INVALID_SOCKET then // 存在请求
    begin
      IP := inet_ntoa(addr.sin_addr);
      Writeln(IP + '已经连接' + ',端口号是' + inttostr(ntohs(addr.sin_port)));
      CloseHandle(CreateThread(nil, 0, @TalkThread, Pointer(sclient), 0, IDD));
    end;
  end;
  Result := 0;
end;

function BindPort(s: TSocket; w: word): integer;
var
  addr: TSockAddrIn;
  HostEnt: PHostEnt;
  buffer: array [0 .. 63] of Ansichar;
  IP: ansistring;
begin
  ZeroMemory(@addr, sizeof(addr));
  addr.sin_family := AF_INET;
  addr.sin_port := htons(w);
  // addr.sin_addr.S_addr := htonl(INADDR_ANY);;
  // /// ///
  GetHostName(buffer, sizeof(buffer));
  HostEnt := GetHostByName(buffer); // h_addr_list: PPAnsiChar
  // IP := StrPas(inet_ntoa(PInAddr(HostEnt.h_addr_list^)^));
  IP := StrPas(inet_ntoa(TInAddr(PInAddr(HostEnt.h_addr_list^)^)));
  // /// ///
  addr.sin_addr.S_addr := inet_addr(Pansichar(IP));
  Writeln('服务器IP:' + IP + ',端口号是' + inttostr(ntohs(addr.sin_port)));
  Result := bind(s, (PSockAddr(@addr))^, sizeof(addr));
end;

// main
begin
  { TODO -oUser -cConsole Main : Insert code here }
  if (WSAStartup(MAKEWORD(2, 2), wsadata)) <> 0 then
  begin
    Writeln('无法在本台计算机上启动Socket!');
  end
  else
  begin
    slisten := socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if slisten = INVALID_SOCKET then
    begin
      Writeln('无法建立Socket!');
      i := WSAGetLastError;
    end
    else
    begin
      if BindPort(slisten, 8986) = SOCKET_ERROR then
      begin
        Writeln('无法绑定端口!');
        i := WSAGetLastError;
      end
      else
      begin
        if listen(slisten, 100) = SOCKET_ERROR then
        begin
          Writeln('无法启动侦听');
        end
        else
        begin
          Writeln('启动侦听!');
          CloseHandle(CreateThread(nil, 0, @ListenThread,
            Pointer(slisten), 0, ID));
          Writeln('按回车键结束');
          Readln(ch); // 输入字符
          if ch = #13 then
          begin
            bStop := True;
            closesocket(slisten);
          end;
        end;
      end;
    end;
    WSACleanup();
  end;

end.

1 面向连接的套接字的系统调用时序图

服务端代码:

unit ServerSocket;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, winSock2;

type
  TMyServer = class(TForm)
    StatusBar1: TStatusBar;
    Memo1: TMemo;
    edt_send: TEdit;
    btn_create: TButton;
    btn_sender: TButton;
    Button1: TButton;
    procedure btn_createClick(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure btn_senderClick(Sender: TObject);
  private

    { Private declarations }
  public
    { Public declarations }
  end;

var
  MyServer: TMyServer;

var
  slisten: TSocket;
  sclient: TSocket;
  i: integer;
  bStop: Boolean = FALSE;
  bok: Boolean = FALSE;

implementation

{$R *.dfm}
// function TalkThread(p: Pointer): DWORD; stdcall;
// var
// addr: TsockAddrin;
// IP: ansistring;
// buffer: array [0 .. 1024] of char;
// ch: char;
// len, pos: integer;
// begin
// sclient := TSocket(p);
// while bok = FALSE do
// begin
// fillchar(buffer, sizeof(buffer), '');
// len := recv(sclient, buffer, Length(buffer), 0);
// if len > 0 then
// begin
// MyServer.Memo1.Lines.Add('客户端说:' + buffer);
// end;
// end;
// result := 0;
// end;

// 循环侦听线程
function ListenThread(p: Pointer): DWORD; stdcall;
var
  addr: TsockAddrin;
  IP: ansistring;
  buffer: array [0 .. 63] of char;
  len: integer;
begin
  fillchar(buffer, sizeof(buffer), '');
  slisten := TSocket(p);
  len := sizeof(addr);
  // 用一个循环来反复判断是否有客户端请求,如果存在请求就创建一个用来接受数据的读取线程
  while (bStop = FALSE) do
  begin
    sclient := accept(slisten, PSockAddr(@addr), @(len));
    if sclient <> INVALID_SOCKET then // 存在请求
    begin
      IP := inet_ntoa(addr.sin_addr);
      MyServer.StatusBar1.Panels[0].Text := IP + '已经连接' + ',端口号是' + inttostr(ntohs(addr.sin_port));
      while bok = FALSE do
      begin
        len := recv(sclient, buffer, Length(buffer), 0);
        if len > 0 then
        begin
          MyServer.Memo1.Lines.Add('客户端说:' + buffer);
        end;
      end;
    end;
  end;
  result := 0;
end;

function BindPort(s: TSocket; w: word): integer;
var
  addr: TsockAddrin;
  HostEnt: PHostEnt;
  buffer: array [0 .. 63] of ansichar;
  IP: ansistring;
begin
  ZeroMemory(@addr, sizeof(addr));
  addr.sin_family := AF_INET;
  addr.sin_port := htons(w);
  // addr.sin_addr.S_addr := htonl(INADDR_ANY);;
  GetHostName(buffer, sizeof(buffer));
  HostEnt := GetHostByName(buffer); // h_addr_list: PPAnsiChar
  IP := StrPas(inet_ntoa(TInAddr(PInAddr(HostEnt.h_addr_list^)^)));
  addr.sin_addr.S_addr := inet_addr(Pansichar(IP));
  result := bind(s, (PSockAddr(@addr))^, sizeof(addr));
end;

procedure TMyServer.btn_createClick(Sender: TObject);
var
  wsadata: Twsadata;
  ID: DWORD;
begin
  if (WSAStartup(MAKEWORD(2, 2), wsadata)) <> 0 then
  begin
    ShowMessage('无法在本台计算机上启动Socket!');
  end
  else
  begin
    slisten := socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if slisten = INVALID_SOCKET then
    begin
      ShowMessage('无法建立Socket!');
      i := WSAGetLastError;
    end
    else
    begin
      if BindPort(slisten, 8986) = SOCKET_ERROR then
      begin
        ShowMessage('无法绑定端口!');
        i := WSAGetLastError;
      end
      else
      begin
        if listen(slisten, 100) = SOCKET_ERROR then
        begin
          ShowMessage('无法启动侦听');
        end
        else
        begin
          StatusBar1.Panels[0].Text := '启动侦听!';
          CloseHandle(CreateThread(nil, 0, @ListenThread,
            Pointer(slisten), 0, ID));
        end;
      end;
    end;
  end;
end;

procedure TMyServer.btn_senderClick(Sender: TObject);
var
  len: integer;
  buffer: array [0 .. 1024] of char;
begin
  fillchar(buffer, sizeof(buffer), '');
  bStop := True;
  if bStop = True then
  begin
    StrPCopy(buffer, edt_send.Text);
    send(sclient, buffer, Length(buffer), 0);
    Memo1.Lines.Add('服务端说:' + buffer);
  end;
end;

procedure TMyServer.Button1Click(Sender: TObject);
begin
  closesocket(slisten);
  closesocket(sclient);
  WSACleanup();
  StatusBar1.Panels[0].Text := '关闭侦听!';
end;

end.

客户端代码:

unit ClientSocket;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, winsock2;

type
  TMyClient = class(TForm)
    StatusBar1: TStatusBar;
    Memo1: TMemo;
    edt_send: TEdit;
    btn_send: TButton;
    btn_link: TButton;
    Button1: TButton;
    procedure btn_sendClick(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure btn_linkClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  MyClient: TMyClient;
  client: Tsocket;
  bStop: Boolean = false;

implementation

{$R *.dfm}

function TalkeThread(p: Pointer): DWORD; stdcall;
var
  addr: TSockAddrIn;
  IP: ansistring;
  buffer: array [0 .. 1024] of char;
  ch: char;
  len, pos: integer;
begin
  FillChar(buffer, sizeof(buffer), '');
  pos := 0;
  while bStop = false do
  begin
    len := recv(client, ch, sizeof(ch), 0);
    if len > 0 then
    begin
      if ch <> #32 then // 字符加入数组buffer
      begin
        buffer[pos] := ch;
        Inc(pos);
      end;
    end;
  end;
  if length(buffer) <> 0 then
  begin
    MyClient.Memo1.Lines.Add('服务端说:' + buffer);
  end;
  Result := 0;
end;

procedure TMyClient.btn_linkClick(Sender: TObject);
var
  wsadata: Twsadata;
  addr: TSockAddrIn;
  HostEnt: PHostEnt;
  buffer: array [0 .. 63] of AnsiChar;
  IP: ansistring;
  ID: DWORD;
  i, len: integer;
begin
  if (WSAStartup(MAKEWORD(2, 2), wsadata)) <> 0 then
  begin
    ShowMessage('无法在本台计算机上启动Socket!');
  end
  else
  begin
    client := socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
    if client = INVALID_SOCKET then
    begin
      ShowMessage('无法建立Socket!');
      i := WSAGetLastError;
    end
    else
    begin
      ZeroMemory(@addr, sizeof(addr));
      addr.sin_family := AF_INET;
      addr.sin_port := htons(8986);
      GetHostName(buffer, sizeof(buffer));
      HostEnt := GetHostByName(buffer);
      IP := StrPas(inet_ntoa(TInAddr(PInAddr(HostEnt.h_addr_list^)^)));
      addr.sin_addr.S_addr := inet_addr(Pansichar(IP));
      if SOCKET_ERROR = connect(client, TSockAddr(addr), sizeof(addr)) then
      begin
        ShowMessage('连接失败!');
        i := WSAGetLastError;
      end
      else
      begin
        StatusBar1.Panels[0].Text := IP + '已经连接服务端' + ',其端口号是' + inttostr(ntohs(addr.sin_port));
        CloseHandle(CreateThread(nil, 0, @TalkeThread, nil, 0, ID));
      end;
    end;
  end;

end;

procedure TMyClient.btn_sendClick(Sender: TObject);
var
  len: integer;
  buffer: array [0 .. 1024] of char;
begin
  FillChar(buffer, sizeof(buffer), '');
  StrPCopy(buffer, edt_send.Text);
  send(client, buffer, length(buffer), 0);
  Memo1.Lines.Add('我说:' + buffer);
end;

procedure TMyClient.Button1Click(Sender: TObject);
begin
  closesocket(client);
  WSACleanup();
  bStop := false;
  StatusBar1.Panels[0].Text := '关闭服务!';
end;

end.

 

2 无连接协议的套接字调用时序图 

 

3 面向连接的应用程序流程图

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值