3.3 Windows Sockets 与 UNIX 套接口编程实例

3.3 Windows Sockets
与 UNIX 套接口编程实例
下面是一个简单的基于连接的点对点实时通信程序.它由两部分组成,服务器在主机UNIX下
直接运行, 客户机在Windows下运行.
介绍
3.3.1 SERVER
由于SERVER是在UNIX下运行的,它对套接口的使用都是BSD的标准函数,程序也比较简
单, 只有一段程序,下面简要解释一下.
首先,建立自己的套接口.在互连网的进程通信中,全局标识一个进程需要一个被称为"半相
关"的三元组(协议,本地主机地址,本地端口号)来描述,而一个完整的进程通信实例则需要一个被
称为"相关"的五元组(协议, 本地主机地址,本地端口号,远端主机地址,远端端口号)来描述.
s=socket(AF_INET, SOCK_STREAM, 0)
该函数建立指定地址格式,数据类型和协议下的套接口,地址格式为AF_INET(唯一支持的
格式),数据类型SOCK_STREAM表示建立流式套接口,参数三为0,即协议缺省.
bind(s, (struct sockaddr *)&server, sizeof(server))
该函数将建立服务器本地的半相关,其中,server是sockaddr_in结构,其成员描述了本地端
口号和本地主机地址,经过bind()将服务器进程在网上标识出来.
然后,建立连接.先是调用listen()函数表示开始侦听.再通过accept()调用等待接收连接.
listen(s,1)表示连接请求队列长度为1,即只允许有一个请求,若有多个请求,则出现错误,给
出错误代码WSAECONNREFUSED.
ns = accept(s, (struct sockaddr *)&client, &namelen))

. 26 .

accept()阻塞(缺省)等待请求队列中的请求,一旦有连接请求来,该函数就建立一个和s有相
同属性的新的套接口.client也是一个sockaddr_in结构,连接建立时填入请求连接的套接口的半
相关信息.
接下来,就可以接收和发送数据了.
recv(ns,buf,1024,0)
send(ns,buf,pktlen,0)
上面两个函数分别负责接收和发送数据,recv从ns(建立连接的套接口)接收数据放入buf
中,send则将buf中数据发送给ns.至于第四个参数,表示该函数调用方式,可选择
MSG_DONTROUTE和MSG_OOB, 0表示缺省.
最后,关闭套接口.
close(ns);
close(s);
3.3.2 CLIENT
介绍
客户端是在Windows上运行的,使用了一些Windows Sockets的扩展函数,稍微复杂一些.包
括了.RC和.C两个文件,其中的主窗口函数ClientProc()是程序的主要部分,下面简单解释一下.
首先,是在WinMain()中建立好窗口后,即向主窗口函数发一条自定义的WM_USER消息,
做相关的准备工作.在主窗口函数中,一接收到WM_USER消息,首先调用WSAStartup()函数初
始化Windows Sockets DLL,并检查版本号.如下:
Status = WSAStartup(VersionReqd, lpmyWSAData);
其中,VersionReqd描述了WINSOCK的版本(这里为1.1版),lpmyWSAData指向一个
WSADATA结构,该结构描述了Windows Sockets的实现细节.
WSAStartup()之后,进程通过主机名(运行时命令行参数传入)获取主机地址,如下:
hostaddr = gethostbyname(server_address);
hostaddr指向hostent结构,内容参见5.2.1.
然后,进程就不断地消息循环,等待用户通过菜单选择"启动".这时,通过调用Client()来启动
套接口.在Client()中,首先也是调用socket()来建立套接口.如下:
if ((s = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
{
AlertUser(hWnd, "Socket Failed");
return (FALSE);
}
紧接着,调用WSAAsyncSelect()函数提名FD_CONNECT网络事件,如下:
if (!SetSelect(hWnd, FD_CONNECT))
return (FALSE);
SetSelect()主要就是调用WSAASyncSelect(),让Windows Sockets DLL在侦测到连接建立
时,就发送一条UM_SOCK的自定义消息,使消息循环继续下去.如下:
BOOL SetSelect(HWND hWnd, long lEvent)
{
if (WSAAsyncSelect(s, hWnd, UM_SOCK, lEvent) == SOCKET_ERROR)
{
AlertUser(hWnd, "WSAAsyncSelect Failure.");
return (FALSE);
}
return (TRUE);
}
. 27 .

为建立连接,必须马上调用connect()如下,由于先调用了WSAASyncSelect(),connect()便是
非阻塞调用.进程发出连接请求后就不管了,当连接建立好后,WINSOCK DLL自动发一条消息给
主窗口函数,以使程序运行下去.
connect(s, (struct sockaddr FAR *)&dst_addr, sizeof(dst_addr));
窗口函数在收到UM_SOCK消息后,判断是由哪个网络事件引起的,第一次,必然是由连接事
件引起的,这样,就会执行相应的程序段,同样调用SetSelect()来提名FD_WRITE事件.希望在套
接口可发送数据时接到消息.在收到FD_WRITE消息时,先调用send()发送数据,再调用
SetSelect()来提名FD_READ事件, 希望在套接口可接收数据是接到消息.在收到FD_READ消
息时,先调用recv()来接收数据再提名FD_WRITE事件,如此循环下去.直到发生连接关闭的事件
FD_CLOSE,这时就调用WSAAsyncSelect(s,hWnd,0,0)来停止异步选择.在窗口函数接到
WM_DESTROY消息时(即关闭窗口之前),先调用closesocket()(作用同UNIX 中的close())来关
闭套接口,再调用WSACleanup()终止Windows Sockets DLL,并释放资源.
3.3.3 
源程序清单
程序1:CLIENT.RC
ClientMenu MENU
BEGIN
POPUP "&Server"
BEGIN
MENUITEM "&Start...", 101
MENUITEM "&Exit",  102
END
END
程序2:CLIENT.C
#define USERPORT 10001
#define IDM_START 101
#define IDM_EXIT  102
#define UM_SOCK WM_USER + 0X100
#include <alloc.h>
#include <mem.h>
#include <windows.h>
#include <winsock.h>
#define MAJOR_VERSION 1
#define MINOR_VERSION 2
#define WSA_MAKEWORD(x,y)  ((y)*256+(x))
HANDLE hInst;
char server_address[256] = {0};
char buffer[1024];
char FAR * lpBuffer = &buffer[0];
SOCKET s = 0;
struct sockaddr_in dst_addr;
struct hostent far *hostaddr;
struct hostent hostnm;
struct servent far *sp;
int count = 0;
. 28 .

BOOL InitApplication(HINSTANCE hInstance);
long FAR PASCAL ClientProc(HWND hWnd, unsigned message, UINT wParam, LONG lParam);
void AlertUser(HWND hWnd, char *message);
BOOL Client(HWND hWnd);
BOOL ReceivePacket(HWND hWnd);
BOOL SetSelect(HWND hWnd, long lEvent);
BOOL SendPacket(HWND hWnd, int len);
int PASCAL WinMain(HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpCmdLine, int
nCmdShow)
{
HWND hWnd;
MSG msg;
lstrcpy((LPSTR)server_address, lpCmdLine);
if (!hPrevInstance)
if (!InitApplication(hInstance))
return (FALSE);
hInst = hInstance;
hWnd = CreateWindow("ClientClass", "Windows ECHO Client",
WS_OVERLAPPEDWINDOW,\
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL,\
hInstance, NULL);
if (!hWnd)
return (FALSE);
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
PostMessage(hWnd, WM_USER, (WPARAM)0, (LPARAM)0);
while (GetMessage(&msg, NULL, NULL, NULL))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (msg.wParam);
}
BOOL InitApplication(HINSTANCE hInstance)
{
    WNDCLASS WndClass;
char *szAppName = "ClientClass";
    // fill in window class information
WndClass.lpszClassName = (LPSTR)szAppName;
WndClass.hInstance     = hInstance;
WndClass.lpfnWndProc   = ClientProc;
WndClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
. 29 .

WndClass.hIcon         = LoadIcon(hInstance, NULL);
WndClass.lpszMenuName  = "ClientMenu";
WndClass.hbrBackground = GetStockObject(WHITE_BRUSH);
WndClass.style         = CS_HREDRAW | CS_VREDRAW;
WndClass.cbClsExtra    = 0;
WndClass.cbWndExtra    = 0;
    // register the class
    if (!RegisterClass(&WndClass))
return(FALSE);
    return(TRUE);
}
long FAR PASCAL ClientProc(HWND hWnd, unsigned message, UINT wParam, LONG lParam)
{
int length, i;
WSADATA wsaData;
int Status;
switch (message)
{
case WM_USER:
{
WORD wMajorVersion, wMinorVersion;
LPWSADATA lpmyWSAData;
WORD  VersionReqd;
int ret;
wMajorVersion = MAJOR_VERSION;
wMinorVersion = MINOR_VERSION;
VersionReqd = WSA_MAKEWORD(wMajorVersion,wMinorVersion);
lpmyWSAData = (LPWSADATA)malloc(sizeof(WSADATA));
Status = WSAStartup(VersionReqd, lpmyWSAData);
if (Status != 0)
{
AlertUser(hWnd, "WSAStartup() failed\n");
PostQuitMessage(0);
}
hostaddr = gethostbyname(server_address);
if (hostaddr == NULL)
{
AlertUser(hWnd, "gethostbyname ERROR!\n");
WSACleanup();
PostQuitMessage(0);
}
_fmemcpy(&hostnm, hostaddr, sizeof(struct hostent));
. 30 .

}
break;
case WM_COMMAND:
switch (wParam)
{
case IDM_START:
if (!Client(hWnd))
{
closesocket(s);
AlertUser(hWnd, "Start Failed");
}
break;
case IDM_EXIT:
// WSACleanup();
PostQuitMessage(0);
break;
}
break;
case UM_SOCK:
switch (lParam)
{
case FD_CONNECT:
if (!SetSelect(hWnd, FD_WRITE))
closesocket(s);
break;
case FD_READ:
if (!ReceivePacket(hWnd))
{
AlertUser(hWnd, "Receive Packet Failed.\n");
closesocket(s);
break;
}
if (!SetSelect(hWnd, FD_WRITE))
closesocket(s);
break;
case FD_WRITE:
for (i = 0; i < 1024; i ++)
buffer[i] = (char)'A' + i % 26;
length = 1024;
if (!SendPacket(hWnd, length))
{
AlertUser(hWnd, "Packet Send Failed!\n");
closesocket(s);
break;
}
if (!SetSelect(hWnd, FD_READ))
closesocket(s);
break;
case FD_CLOSE:
if (WSAAsyncSelect(s, hWnd, 0, 0) == SOCKET_ERROR)
. 31 .

AlertUser(hWnd, "WSAAsyncSelect Failed.\n");
break;
default:
if (WSAGETSELECTERROR(lParam) != 0)
{
AlertUser(hWnd, "Socket Report Failure.");
closesocket(s);
break;
}
break;
            }
break;
case WM_DESTROY:
closesocket(s);
WSACleanup();
PostQuitMessage(0);
break;
default:
return (DefWindowProc(hWnd, message, wParam, lParam));
}
return(NULL);
}
void AlertUser(HWND hWnd, char *message)
{
MessageBox(hWnd, (LPSTR)message, "Warning", MB_ICONEXCLAMATION);
return;
}
BOOL Client(HWND hWnd)
{
memset(&dst_addr,'\0', sizeof (struct sockaddr_in));
_fmemcpy((char  FAR *)&dst_addr.sin_addr,(char  FAR *)hostnm.h_addr,hostnm.h_length);
dst_addr.sin_family = hostnm.h_addrtype;
dst_addr.sin_port = htons(USERPORT);
if ((s = socket(AF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
{
AlertUser(hWnd, "Socket Failed");
return (FALSE);
}
if (!SetSelect(hWnd, FD_CONNECT))
return (FALSE);
connect(s, (struct sockaddr FAR *)&dst_addr, sizeof(dst_addr));
return (TRUE);
}
BOOL ReceivePacket(HWND hWnd)
{
HDC hDc;
. 32 .

int length;
int i1,i2,i3;
char line1[255], line2[255], line3[255];
count ++;
if ((length = recv(s, lpBuffer, 1024, 0)) == SOCKET_ERROR)
return (FALSE);
if (length == 0)
return (FALSE);
if (hDc = GetDC(hWnd))
{
i1 = wsprintf((LPSTR)line1, "TCP Echo Client No.%d", count);
i2 = wsprintf((LPSTR)line2, "Receive %d bytes",length);
i3 = wsprintf((LPSTR)line3, "Those are:%c, %c, %c, %c, %c,
%c",buffer[0],buffer[1],buffer[2],buffer[100],buffer[1000],buffer[1023]);
TextOut(hDc, 10, 2, (LPSTR)line1, i1);
TextOut(hDc, 10, 22, (LPSTR)line2, i2);
TextOut(hDc, 10, 42, (LPSTR)line3, i3);
ReleaseDC(hWnd, hDc);
}
return (TRUE);
}
                                                                      
BOOL SetSelect(HWND hWnd, long lEvent)
{
if (WSAAsyncSelect(s, hWnd, UM_SOCK, lEvent) == SOCKET_ERROR)
{
AlertUser(hWnd, "WSAAsyncSelect Failure.");
return (FALSE);
}
return (TRUE);
}
BOOL SendPacket(HWND hWnd, int len)
{
int length;
if ((length = send(s, lpBuffer, len, 0)) == SOCKET_ERROR)
return (FALSE);
else
if (length != len)
{
AlertUser(hWnd, "Send Length NOT Match!");
return (FALSE);
}
return (TRUE);
}
程序3:SERVER.C
#include <sys/types.h>
. 33 .

#include <sys/mntent.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#define USERPORT 10001
#define HOST_IP_ADDR "192.1.1.2"
main(int argc, char **argv)
{
char buf[1024];
struct sockaddr_in client;
struct sockaddr_in server;
int s;
int ns;
int namelen;
int pktlen;
if ((s=socket(AF_INET, SOCK_STREAM, 0))<0)
{
perror("Socket()");
return;
}
bzero((char *)&server,sizeof(server));
server.sin_family = AF_INET;
server.sin_port = htons(USERPORT);
server.sin_addr.s_addr = INADDR_ANY;
if (bind(s, (struct sockaddr *)&server, sizeof(server))<0)
{
perror("Bind()");
return;
}
if (listen(s,1)!=0)
{
perror("Listen()");
return;
}
namelen = sizeof(client);
if ((ns = accept(s, (struct sockaddr *)&client, &namelen)) ==-1)
{
perror("Accept()");
return;
}
for (;;)
{
if ((pktlen = recv(ns,buf,1024,0))<0)
{
perror("Recv()");
break;
}
. 34 .

else
if (pktlen == 0)
{
printf("Recv():return FAILED,connection is shut down!\n");
break;
}
else
printf("Recv():return SUCCESS,packet length = %d\n",pktlen);
sleep(1);
if (send(ns,buf,pktlen,0)<0)
{
perror("Send()");
break;
}
else
printf("Send():return SUCCESS,packet length = %d\n",pktlen);
}
close(ns);
close(s);
printf("Server ended successfully\n");
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值