1.2.1 流式套接字编程

1.2  获取网络中计算机的IP地址和计算机名

在开发网络应用的过程中,经常需要获取网络中某台计算机的IP地址和计算机名称。在本节的内容中,将介绍如何使用Visual C++ 6.0开发一个实现上述功能的应用程序。

1.2.1  流式套接字编程(1)

网络数据的传输是通过套接字实现的。套接字有3种类型:流式套接字(SOCK_ STREAM),数据报套接字(SOCK_DGRAM)及原始套接字(RAW)。在本小节的内容中,将首先讲解流式套接字编程的基本知识。

流式套接字是面向连接的,提供双向、有序、无重复且无记录边界的数据流服务,适用于处理大量数据,可靠性高,但开销也大,编程模型如图1-11所示。

 
(点击查看大图)图1-11  流式套接字编程模型

1.服务器端编程步骤

(1) 在初始化阶段调用函数WSAStartup()

此函数在应用程序中初始化Windows Sockets DLL,只有此函数调用成功后,应用程序才可以再调用其他Windows Sockets DLL中的API函数。

在程序中该函数的调用形式如下:

  1. int WSAStartup(
  2. WORD wVersionRequested,     //所使用WinSocket版本
  3. LPWSADATA lpWSAData         //存储系统返回的WinSocket信息
  4. );

(2) 建立Socket

初始化WinSock的动态链接库后,需要在服务器端建立一个监听Socket,为此可以调用socket()函数来建立这个监听的Socket,并定义此Socket所使用的通信协议:

  1. SOCKET socket(
  2. int af,                 //目前只提供PF_INET(AF_INET)
  3. int t   ype,                    //Socket的类型(SOCK_STREAM、SOCK_DGRAM)
  4. int protocol                //通讯协议(如果使用者不指定则设为0)
  5. );

调用成功返回Socket对象,失败则返回INVALID_SOCKET(调用WSAGetLastError()可得知原因,所有WinSocket的函数都可以使用这个函数来获取失败的原因)。

如果要建立的是遵从TCP/IP协议的Socket,第二个参数type应为SOCK_STREAM,如为UDP(用户数据报协议)的Socket,type应为SOCK_DGRAM。

(3) 绑定端口

接下来要为服务器端定义的监听Socket指定一个地址及端口(Port),这样客户端才知道待会儿要连接哪一个地址的哪个端口,为此我们要调用bind()函数,该函数调用成功返回0,否则返回SOCKET_ERROR:

  1. int bind(
  2. SOCKET s,                   //Socket对象名
  3. const struct sockaddr FAR *name,    //Socket的地址值,即所在机器的IP地址
  4. int namelen             //name的长度
  5. );

如果使用者不在意地址或端口的值,那么可以设定地址为INADDR_ANY,及Port为0,Windows Sockets会自动将其设定为适当的地址及Port(1024到5000之间的值)。此后可以调用getsockname()函数来获知其被设定的值。

(4) 监听

当服务器端的Socket对象绑定完成之后,必须建立一个监听的队列来接收客户端的连接请求。listen()函数使服务器端的Socket进入监听状态,并设定可以建立的最大连接数(目前最大值限制为5,最小值为1),该函数调用成功返回0,否则返回SOCKET_ERROR:

  1. int listen(
  2. SOCKET s,                   //需要建立监听的Socket
  3. int backlog             //最大连接个数
  4. );

服务器端的Socket调用完listen()后,如果此时客户端调用connect()函数提出连接申请的话,服务器端必须再调用accept()函数,这样服务器端和客户端才算正式完成通信程序的连接动作。

为了知道什么时候客户端提出连接要求,从而服务器端的Socket在恰当的时候调用accept()函数完成连接的建立,我们就要使用WSAAsyncSelect()函数,让系统主动来通知我们有客户端提出连接请求了,该函数调用成功返回0,否则返回SOCKET_ERROR:

  1. int WSAAsyncSelect(
  2. SOCKET s,                       //Socket 对象
  3. HWND hWnd,                  //接收消息的窗口句柄
  4. unsigned int wMsg,          //传给窗口的消息
  5. long lEvent                 //被注册的网络事件
  6. );

被注册的网络事件lEvent就是应用程序向窗口发送消息的网路事件,该值为下列值FD_READ、FD_WRITE、FD_OOB、FD_ACCEPT、FD_CONNECT、FD_CLOSE的组合,各个值的具体含义如下。

FD_READ:希望在套接字s收到数据时收到消息。

FD_WRITE:希望在套接字s上可以发送数据时收到消息。

FD_ACCEPT:希望在套接字s上收到连接请求时收到消息。

FD_CONNECT:希望在套接字s上连接成功时收到消息。

FD_CLOSE:希望在套接字s上连接关闭时收到消息。

FD_OOB:希望在套接字s上收到OOB数据时收到消息。

具体应用时,wMsg是在应用程序中定义的消息名称,而消息结构中的lParam则为以上各种网络事件名称。所以,可以在窗口处理自定义消息函数中使用以下结构来响应Socket的不同事件:

  1. switch(lParam) {
  2. case FD_READ:
  3. ...
  4. break;
  5. case FD_WRITE:
  6. ...
  7. break;
  8. ...
  9. }

(5) 服务器端接受客户端的连接请求

当Client提出连接请求时,Server端的hwnd窗口会收到Winsock Stack送来的我们自定义的一个消息,这时,我们可以分析lParam,然后调用相关的函数来处理此事件。为了使服务器端接受客户端的连接请求,就要使用accept()函数,该函数新建一个Socket与客户端的Socket相通,原先监听的Socket继续进入监听状态,等待其他客户端的连接要求,该函数调用成功返回一个新产生的Socket对象,否则返回INVALID_SOCKET:

  1. SOCKET accept(
  2. SOCKET s,               //Socket的识别码
  3. struct sockaddr FAR *addr,      //存放连接的客户端地址
  4. int FAR *addrlen        //地址长度
  5. );

(6) 结束Socket连接

结束服务器和客户端的通信连接是很简单的,这一过程可以由服务器或客户机的任一端启动,只要调用closesocket()就可以了,而要关闭Server端监听状态的Socket,同样也是利用此函数。另外,与程序启动时调用WSAStartup()函数相对应,程序结束前,需要调用WSACleanup()来通知Winsock Stack释放Socket所占用的资源。这两个函数都是调用成功返回0,否则返回SOCKET_ERROR。closesocket()函数的原型如下:

  1. int closesocket(
  2. SOCKET s;               //Socket的识别码
  3. );

(7) 最后调用WSACleanup

代码如下:

  1. int WSACleanup(void);

2.客户端编程步骤

(1) 建立客户端的Socket

客户端应用程序首先也是调用WSAStartup()函数来与Winsock的动态链接库建立关系,然后同样调用socket()来建立一个TCP或UDP Socket(相同协定的Sockets才能相通,TCP对TCP,UDP对UDP)。与服务器端的Socket不同的是,客户端的Socket可以调用 bind()函数,由自己来指定IP地址及port号码;但是也可以不调用bind(),而由Winsock来自动设定IP地址及port号码。

(2) 提出连接请求

客户端的Socket使用connect()函数来提出与服务器端的Socket建立连接的申请,函数调用成功返回0,否则返回SOCKET_ERROR:

  1. int connect(
  2. SOCKET s,           //服务器端Socket的识别码
  3. const struct sockaddr FAR *name,    //Socket想要连接的对方地址
  4. int namelen     //地址长度
  5. );

作为客户端的监控程序,其实现过程要比服务器简单许多。由于需要接收数据,因此在异步选择函数中需要设定待监测的网络事件为FD_CLOSE和FD_READ。在消息响应函数中可以通过对消息参数的低位字节进行判断而区分出具体发生的是何种网络事件,并对其做出相应的反应。

3.数据的传送

基于TCP/IP连接协议(流式套接字)的服务是设计客户机/服务器应用程序时的主流标准,但有些服务是可以通过无连接协议(数据报套接字)提供的。一般情况下TCP Socket的数据发送和接收是调用send()及recv()这两个函数来达成,而UDP Socket则是用sendto()及recvfrom()这两个函数,这两个函数调用成功返回发送或接收的资料的长度,否则返回SOCKET_ERROR。send()函数的原型如下:

  1. int send(
  2. SOCKET s,                   //Socket的识别码
  3. const char FAR *buf,        //存放要传送的资料的暂存区
  4. int len,                    //buf的长度
  5. int flags                   //此函数被调用的方式
  6. );

对于Datagram Socket而言,若是Datagram的大小超过限制,则将不会送出任何资料,并会传回错误值。对Stream Socket而言,在Blocking模式下,若是传送系统内的存储空间不够存放这些要传送的资料,send()将会被block住,直到资料送完为止;如果该Socket被设定为 Non-Blocking模式,那么将视目前的output buffer空间有多少,就送出多少资料,并不会被block住。

flags的值可设为0或MSG_DONTROUTE及MSG_OOB的组合。

recv()函数的原型如下:

  1. int recv(
  2. SOCKET      s,                      // Socket的识别码
  3. char FAR    *buf,                   // 存放接收到资料的暂存区
  4. int             len,                        // buf的长度
  5. int             flags;                  // 此函数被调用的方式
  6. );
时间: 2024-11-07 18:21:50

1.2.1 流式套接字编程的相关文章

流式套接字(SOCK_STREAM),数据报套接字 (SOCK_DGRAM) 的比较

1.流式套接字 使用这种套接字时,数据在客户端是顺序发送的,并且到达的顺序是一致的.比如你在客户端先发送1,再发送2,那么在服务器端的接收顺序是先接收到1,再接收到2,流式套接字是可靠的,是面向连接的: 2.数据报套接字 这种套接字是无连接的,数据是打包成数据包发送的,到达的顺序不一定与发送的顺序是一致的,并且数据不一定是可达的,并且接收到的数据还可能出错. 既然这样那为什么还要使用这种套接字呢?因为现每个使用udp的程序都有自己的对数据进行确认的协议.如TFTP协议规定了每收到一个消息比如,

基于流式套接字的回射客服端编程程序

基于流式套接字的回射客服端编程程序 #include <WS2tcpip.h> #include<unistd.h> #include<stdio.h> #include<string.h> #include<stdlib.h> #include <winsock2.h> #define MAXLINE 512 #define PORT 7210 //填服务器端口号 #define IP_ADDRESS "127.0.0.1

基于Linux平台实现的流式套接字客户端服务器端代码

(1)服务器段代码如下: #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <errno.h> #include <string.h> #include <netdb.h> #include <sys/types.h> #include <time.h> #include <sys/socket.h> #inclu

多客户登录(基于TCP的流式套接字Socket编程)

1.序列化对象 package com.ljb.app.socket; import java.io.Serializable; /**  * 用户类(实现序列化)  * @author LJB  * @version 2015年3月12日  */ public class User implements Serializable{  private String name;  private String password;    public String getName() {   ret

1.3.1 数据报套接字编程

1.3  实现超链接 在网络应用过程中,特别是在Web程序中,超级链接用得非常普遍.其实使用VC技术,也可以实现超级链接功能.在本节的内容中,将介绍使用Visual C++ 6.0开发一个实现超级链接功能的应用程序.在开始之前,首先简单介绍与之相关的基础知识. 1.3.1  数据报套接字编程 流式套接字主要用于TCP协议,接下来将要学的数据报套接字主要用于UDP协议.数据报套接字(Datagram Socket)提供双向的通信,但没有可靠/有序/不重复的保证,所以UDP传送数据可能会收到无次序.

sockt套接字编程

一.Socket简介 Socket是进程通讯的一种方式,即调用这个网络库的一些API函数实现分布在不同主机的相关进程之间的数据交换. 几个定义: (1)IP地址:即依照TCP/IP协议分配给本地主机的网络地址,两个进程要通讯,任一进程首先要知道通讯对方的位置,即对方的IP. (2)端口号:用来辨别本地通讯进程,一个本地的进程在通讯时均会占用一个端口号,不同的进程端口号不同,因此在通讯前必须要分配一个没有被访问的端口号. (3)连接:指两个进程间的通讯链路. (4)半相关:网络中用一个三元组可以在

套接字编程(VC_Win32)

简介(源于维基) Berkeley套接字(也作BSD套接字应用程序接口)刚开始是4.2BSD Unix操作系统(于1983发布)的一套应用程序接口.然而,由于AT&T的专利保护着UNIX,所以只有在1989年Berkeley大学才能自由地发布自己的操作系统和网络库.Berkeley套接字接口,一个应用程序接口(API),使用一个Internet套接字的概念,使主机间或者一台计算机上的进程间可以通讯. 它可以在很多不同的输入/输出设备和驱动之上运行,尽管这有赖于操作系统的具体实现. 接口实现用于T

Python网络编程—socket套接字编程(UDP)

套接字介绍 1.套接字 : 实现网络编程进行数据传输的一种技术手段 2.Python实现套接字编程:import socket 3.套接字分类 流式套接字(SOCK_STREAM): 以字节流方式传输数据,实现tcp网络传输方案.(面向连接--tcp协议--可靠的--流式套接字) 数据报套接字(SOCK_DGRAM):以数据报形式传输数据,实现udp网络传输方案.(无连接--udp协议--不可靠--数据报套接字) UDP套接字编程 服务端流程 1.创建数据报套接字 sockfd = socket

Python网络编程—socket套接字编程(TCP)

套接字介绍 1.套接字 : 实现网络编程进行数据传输的一种技术手段 2.Python实现套接字编程:import socket 3.套接字分类 流式套接字(SOCK_STREAM): 以字节流方式传输数据,实现tcp网络传输方案.(面向连接--tcp协议--可靠的--流式套接字) 数据报套接字(SOCK_DGRAM):以数据报形式传输数据,实现udp网络传输方案.(无连接--udp协议--不可靠--数据报套接字) tcp套接字 服务端流程 1.创建套接字 sockfd=socket.socket