WinSocket编程实验报告

实验六: WinSock编程实验报告

1实验目的和要求

1) 学习网络中进程之间通信的原理和实现方法;

2) 掌握在VB或VC++等集成开发环境中编写网络程序的方法;

3)编写一个简单的聊天程序:最低要求实现两人一组的两台计算机之间的收发文本及其显示;

2实验设备及分组

1)  Windows2000以上操作系统;

2) VB和Visual C++ 6.0语言环境;

3) 每人一台PC机。

3实验内容和步骤

3.1设计服务器界面

打开vb程序,新建一个工程,取名为server。在窗体中添加控件。将Winsock控件放入到窗体里,名称属性改为Winsockserver。在窗体里建立两个按钮,command1的caption属性为发送,command2的caption属性为退出。建立两个richtextbox,richtextbox1的名称属性改为textget,ScrollBars属性改为3-rtfBoth,Locked属性改为True,richtextbox2的名称属性改为textsend,ScrollBars属性改为3-rtfBoth。然后建立两个label,caption属性改为接受窗口和发送窗口。如图3-1所示。

图3-1 服务器界面

3.2设计客户端界面

    先建立一个工程,取名为client。在窗体中加入控件:将Winsock控件添加到窗体里,名称属性改为winsockclient。建立两个richtextbox,richtextbox1的名称属性改为textget,ScrollBars属性改为3-rtfBoth,Locked属性改为True,richtextbox2的名称属性改为textsend,ScrollBars属性改为3-rtfBoth。在窗体里建立三个按钮,command1的caption属性为发送,command2的caption属性为退出,command3的caption属性为连接。建立一个text。再建立三个Lable,caption属性分别为主机名,接收和发送,如图3-2所示。

图3-2 客户端界面

3.3编写程序及其说明

3.3.1服务器程序

Private Sub Command1_Click() ’发送选项

Dim a As String

  a = "服务器:"

  Winsockserver.SendData "(" & Time & ")" & vbCrLf & textsend.Text

  textget.Text = textget.Text & a & "(" & Time & ")" & vbCrLf & textsend.Text & vbCrLf ‘可自动换行,并有时间显示

  textsend.Text = "" ‘发送完毕后,文本框自动清空

End Sub

Private Sub Command2_Click()’退出后结束运行

End

End Sub

Private Sub Form_Load()‘窗体加载时,发送文本框与接收文本框不可见

textsend.Visible = False‘发送信息文本框不可见

  textget.Visible = False‘接收信息文本框可见

  Winsockserver.LocalPort = 1001‘服务器端口号设置

  Winsockserver.Listen‘服务器侦听

End Sub

Private Sub Winsockserver_Close()

Winsockserver.Close‘关闭服务器

End Sub

Private Sub Winsockserver_ConnectionRequest(ByVal requestID As Long) ‘连接后,WinSock控件工作

textsend.Visible = True

  textsend.Text = "" ‘清空文本框信息

  textget.Visible = True

  textget.Text = ""

  If Winsockserver.State <> sckClosed Then Winsockserver.Close‘如果socket没有关闭,则关闭socket

  Winsockserver.Accept requestID‘服务器接受请求,接受以后双方才能发送数据

MsgBox ("连接SERVER成功!")

End Sub

Private Sub Winsockserver_DataArrival(ByVal bytesTotal As Long)‘信息接收

Dim tmpstr As String

  Dim a As String

  a = "客户端:"

  Winsockserver.GetData tmpstr

  textget.Text = textget.Text & a & tmpstr & vbCrLf

End Sub

3.3.2客户端程序

Private Sub Command1_Click()

 winsockclient.Connect

End Sub

Private Sub Command2_Click()’ 连接,且Shape控件显示连接状态

 Dim aa As String

  aa = "客户端:"

  winsockclient.SendData "(" & Time & ")" & vbCrLf & textsend.Text

  textget.Text = textget.Text & aa & "(" & Time & ")" & vbCrLf & textsend.Text &

  vbCrLf

  textsend.Text = ""

End Sub

Private Sub Command3_Click()

 End

End Sub

Private Sub Form_Load() ‘程序加载,文本框不可见

Text1.Text = "这里输入要连接的服务器的IP" ‘提示输入ip地址

  textsend.Visible = False

  textget.Visible = False

winsockclient.RemotePort = 1001’客户端端口号设置为1001

winsockclient.RemoteHost = ""

End Sub

Private Sub Text1_Change()

winsockclient.RemoteHost = Text1.Text‘客户端验证主机名

End Sub

Private Sub winsockclient_Close()‘关闭客户端

 winsockclient.Close

End Sub

Private Sub winsockclient_Connect()‘客户端连接

textsend.Visible = True

  textsend.Text = ""

  textget.Visible = True

  textget.Text = ""

  Command2.Visible = True

MsgBox ("连接SERVER成功!")

End Sub

Private Sub winsockclient_ConnectionRequest(ByVal requestID As Long)

textsend.Visible = True

  textsend.Text = ""

  textget.Visible = True

  textget.Text = ""

  If Winsockserver.State <> sckClosed Then Winsockserver.Close‘如果socket没有关闭,则关闭socket

  Winsockserver.Accept requestID

End Sub

Private Sub winsockclient_DataArrival(ByVal bytesTotal As Long)‘信息接收

 Dim tmpstr As String

  Dim a As String

  a = "服务器:"

  winsockclient.GetData tmpstr

  textget.Text = textget.Text & a & tmpstr & vbCrLf

End Sub

3.4 运行程序

3.4.1 客户端运行结果

图3-3 客户端连接

运行后,填写要连接的主机的IP地址,点击连接,成功后,会提示连接服务器成功。点击确定,双方就可以进行通信。

图3-4 客户端信息接收

双方之间正常通信,接收端能显示信息内容和时间。

3.4.2 服务器运行结果

图3-5 服务器连接

经过客户端输入服务器的IP地址,若正常连接,服务器会提示连接成功。之后就可以进行双方的通信。

 

 

图3-6 服务器接收信息

与客户端一样,只要连接成功,双方就能实现正常通信。

4 实验结果分析

网络数据传输可以分为UDP(User Datagram Protocol,用户数据报协议)和TCP(Transmission Control Protocol,传输控制协议),这两种协议的差别在于他们的连接状态,TCP是一种有连接的协议,文件在传输时会先行建立连接,通过连接可确保文件传输的正确性;UDP是一种无连接的的协议,他在传输文件是不能确保传输端的文件能够正确无误的传送到接受端,所以当用户使用UDP协议时,通常要自己做接收确认工作。操作系统提供的Winsock控件可以提供以上两种传输功能。

服务器程序的实现过程是:

  (1)服务器程序必须设置好LocalPort属性,作为侦听端口,该值为一个整数(只要是一个其它TCP/IP应用程序没有使用过的值即可)。

  (2)使用Listen方法进入侦听状态,等待客户机程序的连接请求。

  (3)客户机程序发出连接请求,使服务器程序产生ConnectionRequest事件,该事件得到一个参数requestID。

  (4)服务器程序用Accept方法接受客户机程序的requestID请求。这样,服务器程序就可以用SendData方法发送数据了。Accept方法必须用上一步得到的requestID作为其参数。

  (5)当服务器程序接收到数据时,产生DataArrival事件,参数BytesTotal包含接收到的数据字节数。在该事件中,可以用GetData方法接收数据。

  (6)如果接受到Close事件,则用Close方法关闭TCP/IP连接。

客户机程序的实现过程是:

  (1) 客户程序设置Remotehost属性,以便指定运行服务器程序的主机名,该字符串可在“控制面板|网络|标识|计算机名”中查到。

  (2) 设置RemotePort属性,以便指定服务器程序的侦听端口。

  (3)使用Connect方法,向服务器提出连接请求。

  (4)服务器接受客户机程序的请求,客户机程序产生Connect事件,就可以用SendData方法发送数据了。

  (5)当客户机程序接收到数据时,产生DataArrival事件,参数BytesTotal包含接收到的数据字节数。在该事件中,可以用GetData方法接收数据。

  (6)如果接受到Close事件,则用Close方法关闭连接。

在使用 WinSock 控件时,首先需要考虑使用什么协议。可以使用的协议包括 TCP 和 UDP。两种协议之间的重要区别在于它们的连接状态:TCP 协议控件是基于连接的协议,可以将它同电话系统相比。在开始数据传输之前,用户必须先建立连接。 UDP 协议是一种无连接协议,两台计算机之间的传输类似于传递邮件:消息从一台计算机发送到另一台计算机,但是两者之间没有明确的连接。另外,单次传输的最大数据量取决于具体的网络。

本应用程序使用 TCP 协议,那么首先必须决定应用程序是服务器还是客户端。如果要创建一个服务器端,那么应用程序需要“监听”指定的端口。当客户端提出连接请求时,服务器端能够接受请求并建立连接。在连接建立之后,客户端与服务器端可以自由地互相通讯。在使用 UDP 协议的时候,可以任意地改变 RemoteHost 和 RemotePort 属性,同时始终保持绑定在同一个 LocalPort 上。TCP 协议与此不同,在改变RemoteHost和 RemotePort 属性之前,必须先关闭连接。

5 实验体会

 对基于socket的设计,任意的编程语言可以适用。由于通信专业所学编程语言比较少,再加上本人对C和C++语言编程了解的不多。因此,我选择了VB语言。 选择设计语言后,稍微复习了VB语言的一些知识,尽管这样,但我还是没有比较清楚的设计思路.因此,我还不断地查阅资料,学习别人的设计思路和方法,然后结合自己的想法将程序不断地完善.这样我的程序设计得以顺利地进行。

这次实验使我对书本上的知识有了更深的理解,特别是对于Winsocket的相关原理有了更深的了解,除此之外,我还对套接字、端口、IP地址之间的关系有了初步的理解, 在以后的实验中,我会更加对书上的代码进行研究,充分的理解其中的关键要素。

 

 

第二篇:WinSocket编程

WinSocket编程

默认分类 2009-12-07 10:33:56 阅读164 评论0 字号:大中小 什么是Socket

接触网络编程当然要了解Socket,Socket(套接字)是一种网络编程接口,Socket提供了很多灵活的函数,帮助程序员写出高效的网络应用。Socket分为BSD UNIX和windows两个版本。 在win32平台上的Winsock编程都要经过下列基本步骤:

定义变量——获得Winsock版本——加载Winsock库——初始化——创建套接字——设置套接字——关闭套接字——卸载Winsock库

使用winsock2 API编程,必须包含头文件winsock2.h (链接环境WS2_32.LIB),头文件winsock.h(WSOCK32.LIB)是为了兼容winsock1程序时使用的,另外mswsock.h(MSWSOCK.DLL)是微软的扩展类,用于开发高性能的winsock程序。

准备好后,你就可以着手建立你的第一个网络程序了。

二, Socket编程的基本过程

Socket通信分为面向连接的通信(TCP)和面向无连接的通信(UDP),通信流程如下:

面向连接的通信

WinSocket编程

无连接的通信

WinSocket编程

1,Winsock初始化和结束

每一个winsock应用程序必须首先加载相应的winsock dll版本。

方法是调用:

int WSAStartup(

WORD wVersionRequested, 库版本,高字节副版本,低字节主版本

LPWSADATA lpWSAData 结构指针,函数自动填充该结构。

); 函数调用成功返回0

可以用宏MAKEWORD(x, y)用来指定第一个参数的值

2,建立套接字

套接字是传输提供者的一个句柄。

SOCKET socket (

int af,

int type,

int protocol IPPROTO_TCP, IPPROTO_UDP, 0(如果不想指定)

);

第一个参数指定通信协议的协议族,AF_INET(IPv4)或

AF_INET6(IPv6)(因为Socket是网络编程接口而不是一个协议,它

使用流行的网络协议(TCP/IP,IPX)为应用程序提供的一个编程接口。)

第二个参数指定要创建的套接字的类型。SOCK_STREAM(TCP流套接字), SOCK_ DGRAM(UDP 数据包套接字),SOCK_RAW(原始套接字)

第三个参数指定应用程序所指定应用程序所使用的通信协议。 函数成功返回套接字描述符,失败返回INVALID_SOCKET

3,配置套接字

当创建一个套接字后,再进行网络通信之前,必须先配置Socket。面向连接的客户端Socket通过调用connect函数在Socket数据结构中保存地址和远端信息。无连接客户端,服务端以及面向连接Socket的服务端,通过调用bind函数来配置本地信息。

int bind(

SOCKET s, 创建的套接字

const struct sockaddr FAR* name, 指向地址缓冲区的指针

int namelen 地址缓冲区的大小

);

成功返回0,失败返回SOCKET_ERROR

当创建一个套接字后,套接字数据结构中有一个默认的IP地址和默认的端口号。一个服务程序必须调用bind函数来给其绑定一个IP地址和一个特定的端口号。

第二个参数指定一个sockaddr结构定义如下:

struct sockaddr {

u_short sa_family;

char sa_data[14];

};

我们通常使用另外一个等价的地址结构:

struct sockaddr_in {

short sin_family;

u_short sin_port;

struct in_addr sin_addr;

char sin_zero[8];

};

其中sin_family是通信协议族,

sin_port

指明端口号,

sin_addr

结构中有一个字段s_addr,表示IP地址,该字段是一个整数, 一般用函数inet_addr把点分字符串形式的IP地址转化成unsigned long型的整数值。

如果指定为htonl(INADDR_ANY),那么无论哪个网段上的客户机都能与该服务器通信,

否则,只有与指定IP地址处于同一网段上的客户机能与该服务器通信。

sin_zero[8]

为填充,使两个结构大小相同。

有一些细节学要说明:

在计算机把IP地址和端口号指定成多字节时,这个数是按“主机字节”(host-byte)顺序表示的,不同的处理器对数的表示方法有“大头”(big-endian——最有意义的字节到最无意义的字节)和“小头”(little-endian)两种形式。但是如果在网络上指定IP地址和端口号时,必须按照big-endian 的形式,一般称之为“网络字节”

(network-byte)顺序。

以下几个函数将主机字节顺序转换成网络字节顺序: u_long htonl(u_long hostlong);

int WSAHtonl(

SOCKET s,

u_long hostlong,

u_long FAR * lpnetlong

字节顺序的4个字节的数

);

u_short htons(u_short hostshort);

int WSAHtons(

SOCKET s,

u_short hostshort,

u_short FAR * lpnetshort

序的2个字节的数 通过指针返回网络通过指针返回网络字节顺

);

以下几个函数将网络字节顺序转换成主机字节顺序: u_long ntohl(u_long netlong);

int WSANtohl(

SOCKET s,

u_long netlong,

u_long FAR * lphostlong

);

u_short ntohs(u_short netshort);

int WSANtohs(

SOCKET s,

u_short netshort,

u_short FAR * lphostshort

);

4,实现功能

(1) 服务器端: 需要对绑定的端口进行侦听,函数原型如下

int listen (

SOCKET

s

,

int

backlog

);

Backlog

是客户连接请求队列的最大数量,而不是客户机连接的数量限制。 处于侦听的套接字将维护一个客户连接请求队列。

该函数执行成功返回0,失败返回

SOCKET_ERROR

此外,需要从连接请求队列中取出最前面的一个客户请求,需要用到accept()函数:

SOCKET accept (

SOCKET

s

,

struct sockaddr FAR*

addr

,

int FAR*

addrlen

);

该函数创建一个新的套接字来与客户套接字建立通信,如果连接成功,就返回新建的套接字描述符,

以后与客户通信的就是该套接字,而侦听套接字则继续接受新的连接;如果失败就返回

INVALID_SOCKET

第一个参数是侦听套接字

第二个套接字用来返回新创建的套接字的地址结构 第三个套接字返回地址结构的长度

(2) 客户端:

connect函数是客户机建立与远程服务器连接而使用的。 int connect (

SOCKET

s

,

const struct sockaddr FAR*

name

,

int

namelen

);

5,数据传输

收发数据时网络编程的一切在这里我们只讨论同步函数send和recv,

不讨论异步函数WSASend和WSARecv。

数据发送要用到send函数,原型如下:

int send(

SOCKET s,

const char FAR * buf, 发送数据缓冲区的地址 int len, 要发送的字节数

int flags 一般为0, MSG_DONTROUTE, MSG_OOB(外带数据)

);

成功返回发送字节数,出错返回SOCKET_ERROR

注意send函数把数据从buf复制到socket发送缓冲区后就返回了,

但是这些数据并不时马上就发送到连接的另一端。

在已连接的套接字上接受数据,recv函数是最基本的方式。 int recv(

SOCKET s,

char FAR* buf,

int len, 准备接收的字节数或buf缓冲区长度 int flags 0, MSG_PEEK, MSG_OOB 其中

MSG_PEEK表示将有用的数据复制到所提供的接收端缓冲区, 但是没有从系统缓冲区中将它删除

);

成功返回接收的数据的字节数量,失败返回SOCKET_ERROR,如果接受数据时网络中断返回0

6,关闭Socket

一旦完成任务,记得将套接字关闭以释放资源:

int closeSocket(SOCKET s)

三, Socket编程实例

讲了这么多其实还是看实例最为重要了,下面我提供了最简单的面向连接的客户端和服务端程序,

当服务端接受客户端的连接后,先是该客户机地IP地址,并向客户端发送一个回应消息,

最后关闭该连接套接字。这样的服务器当然没什么实际的用途。 设计一个基本的网络服务器有以下几个步骤:

1、初始化Windows Socket

2、创建一个监听的Socket

3、设置服务器地址信息,并将监听端口绑定到这个地址上

4、开始监听

5、接受客户端连接

6、和客户端通信

7、结束服务并清理Windows Socket和相关数据,或者返回第4步

#include <winsock2.h>

#include <stdio.h>

#define SERVPORT 5050

#pragma comment(lib,"ws2_32.lib")

void main(void)

{

WSADATA wsaData;

SOCKET sListen; // 监听socket

SOCKET sClient; // 连接socket SOCKADDR_IN serverAddr; // 本机地址信息

SOCKADDR_IN clientAddr; // 客户端地址信息

int clientAddrLen; // 地址结构的长度

int nResult;

// 初始化Windows Socket 2.2

WSAStartup(MAKEWORD(2,2), &wsaData);

// 创建一个新的Socket来响应客户端的连接请求

sListen = socket(AF_INET, SOCK_STREAM,

IPPROTO_TCP);

// 填写服务器绑定的地址信息

// 端口为5150

// IP地址为INADDR_ANY,响应每个网络接口的客户机活动 // 注意使用htonl将IP地址转换为网络格式

serverAddr.sin_family = AF_INET;

serverAddr.sin_port = htons(SERVPORT);

serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); memset(&(serverAddr.sin_zero), 0,

sizeof(serverAddr.sin_zero));

// 绑定监听端口

nResult = bind(sListen, (SOCKADDR *)&serverAddr, sizeof(SOCKADDR));

if (nResult == SOCKET_ERROR)

{

printf("bind failed!\n");

return;

}

// 开始监听,指定最大接受队列长度5,不是连接数的上限

listen(sListen, 5);

// 接受新的连接

while(1)

{

clientAddrLen = sizeof (SOCKADDR); sClient = accept(sListen, (SOCKADDR *)&clientAddr, &clientAddrLen);

if(sClient == INVALID_SOCKET)

{

printf("Accept failed!");

}

else

{

printf("Accepted client: %s : %d\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port)); // 向客户端发送信息

nResult = send(sClient, "Connect success!", 16, 0);

if (nResult == SOCKET_ERROR) {

printf("send failed!"); }

}

// 我们直接关闭连接,

closesocket(sClient);

}

// 并关闭监听Socket,然后退出应用程序

closesocket(sListen);

// 释放Windows Socket DLL的相关资源

WSACleanup();

}

客户机程序如下:

#include <winsock2.h>

#include <stdio.h>

#define SERVPORT 5050 // 端口为5150 #define MAXDATASIZE 100

#define SERVIP "127.0.0.1" // 服务器IP地址为"127.0.0.1",注意使用inet_addr将IP地址转换为网络格式 #pragma comment(lib,"ws2_32.lib")

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

{

WSADATA wsaData;

SOCKET sConnect;

SOCKADDR_IN serverAddr;

int recvbytes;

char buf[MAXDATASIZE];

//初始化Windows Socket 2.2

WSAStartup(MAKEWORD(2,2), &wsaData);

// 创建一个新的Socket来连接服务器

sConnect = socket(AF_INET, SOCK_STREAM,

IPPROTO_TCP);

// 填写连接地址信息

serverAddr.sin_family = AF_INET;

serverAddr.sin_port = htons(SERVPORT); serverAddr.sin_addr.s_addr = inet_addr(SERVIP);

memset(&(serverAddr.sin_zero), 0,

sizeof(serverAddr.sin_zero));

// 向服务器发出连接请求

if (connect(sConnect, (SOCKADDR *)&serverAddr, sizeof(SOCKADDR)) == SOCKET_ERROR) {

printf("connect failed!\n");

return;

}

// 接受服务器的回应消息

recvbytes = recv(sConnect, buf, MAXDATASIZE, 0); if (recvbytes == SOCKET_ERROR)

{

printf("recv failed!\n");

}

else

{

buf[recvbytes] = '\0';

printf("%s\n",buf);

}

closesocket(sConnect);

// 释放Windows Socket DLL的相关资源

WSACleanup();

}

四, 结束语

这里介绍的只不过是winsock最基础最基础的知识,开发网络程序从来都不是一件容易的事情,

尽管只需要遵守很少的一些规则: 创建套接字,发起连接,接受连接,

发送和接受数据。真正的困难在于:让你的程序可以适应从单单一个连接到几千个连接,

即开发一个大容量,具可扩展性的Winsock应用程序。

Winscok编程很重要的一部分是IO处理,分别提供了“套接字模式”和“套接字I/O模型”,

可对一个套接字上的I/O行为进行控制。

其中,套接字模式用于决定在随一个套接字调用时,那些Winsock函数的行为,

有阻塞和非阻塞两种模式。

而另一方面,套接字模型描述了一个应用程序如何对套接字上进行的I/O进行管理及处理,

包括包括Select,WSAAsyncSelect,WSAEventSelect ,IO重

叠模型,完成端口等。

在这里推荐一本好书《windows网络编程技术》,有兴趣研究的同志可以去啃一啃。

相关推荐