win32 socket 编程(三)——TCP/IP
一、TCP/IP解析
TCP/IP协议的核心部分是传输层协议(TCP、UDP),网络层协议(IP)和物理接口层,这三层通常是在操作系统内核中实现。因此用户一般不涉及。编程时,编程界面有两种形式:
1.1、是由内核直接提供的系统调用;
1.2、使用以库函数方式提供的各种函数。
前者为核内实现,后者为核外实现。用户服务要通过核外的应用程序才能实现,所以要使用套接字(socket)来实现。
二、TCP/IP服务器及客户端操作流程
2.1服务器操作流程
2.1.1 加载套接字库。在初始化阶段调用 WSAStartup()
此函数在应用程序中初始化 Windows Sockets DLL,只有此函数调用成功后,应用程序才可以再调用其他Windows Sockets DLL 中的 API 函数。该函数原型
- int WSAStartup(WORD wVersionRequested, LPWSADATA lpWSAData);
参数:
wVersionRequested:Socket的版本号;
WORD 类型、MAKEWORD、LOBYTE、HIBYTE 宏 ;
WORD 类型是一个 16 位的无符号整型, 在 WTYPES.H 中被定义为: typedef unsigned short WORD;
其目的是提供两个字节的存储, 在 Socket 中这两个字节可以表示主版本号和副版本号。 使用 MAKEWORD 宏可以给一个 WORD 类型赋值。例如要表示主版本号 2,副版本号 0,可以使用如下代码:
- WORD wVersionRequested;
- wVersionRequested = MAKEWORD(, );
注意:低位内存存储主版本号2,高位内存存储副版本号0,其值为 0x0002。 使用宏 LOBYTE 可以读取 WORD 的低位字节HIBYTE 可以读取 WORD 的高位字节。 lpWSAData:指向一个用于存储 Socket 库信息的WSAStartup结构。
返回值:
a) 等于0,初始化成功;
b)不等于0, 初始化失败;
2.1.2 创建套接字 Socket
初始化 WinSockt 的动态连接库后,需要在服务器端建立一个监听的 Socket,为此可以调用 Socket()函数用来建立这个监听的 Socket,并定义此 Socket 所使用的通信协议.此函数调用成功返回 Socket 对象,失败则返回 INVALID_SOCKET.调用 WSAGetLastError()可得知原因,所有 WinSocket 的 API 函数都可以使用这个函数来获取失败的原因。函数原型如下:
- SOCKET socket(int af,int type,int protocol)
参数:
a)af: 代表网络地址族,目前只有一种取值有效,即 AF_INET, 代表 internet 地址族。
b) type: 代表网络协议类型, SOCK_DGRAM 代表 UDP 协议, SOCK_STREAM 代表 TCP 协议。
c) protocol: 指定网络地址族特殊协议,目前无用,赋值0即可。
如果要建立的是遵从 TCP/IP 协议的 socket,第二个参数 type 应为 SOCK_STREAM,如为 UDP(数据报)的socket,应为 SOCK_DGRAM。sockets(套接字)编程有三种:流式套接字(SOCK_STREAM),数据报套接字(SOCK_DGRAM),原始套接字(SOCK_RAW)。基于 TCP 的 socket 编程是采用的流式套接字。
返回值:SOCKET 类型
SOCKET 是 socket 套接字类型,在 WINSOCK2.H 中有如下定义:
typedef unsigned u_int;
typedef u_int SOCKET;
可知套接字实际上就是一个无符号整形,它将被 Socket 环境管理和使用。 套接字将被创建、设置、用来发送和接收数据,最后会被关闭。
2.1.3绑定端口(blind)
将创建的套接字绑定到本机地址的某一端口上,接下来要为服务器端定义的这个监听的 Socket 指定一个地址及端口(Port),这样客户端才知道待会要连接哪一个地址的哪个端口,为此要调用 bind()函数,该函数调用成功返回 0,否则返回 SOCKET_ERROR.
- int bind( SOCKET s,const struct sockaddr FAR *name,int namelen);
参 数:
a)s:被绑定的套接字
b) name: 是一个sockaddr结构指针,该结构中包含了要绑定的地址和端口。
c) namelen:第二个参数name 的长度;
如果使用者不在意地址或端口的值,那么可以设定地址为 INADDR_ANY,及 Port 为 0。对于多接口主机使用INADDR_ANY指定了一个通配地址,让该主机的任何一个IP地址都匹配。Windows Sockets会自动将其设定适当之地址及 Port(1024 到 5000 之间的值)。此后可以调用 getsockname()函数来获知其被设定的值。
下面对其涉及的类型作一番解析:
(1) sockaddr_in类型:
sockaddr_in 定义了socket发送和接收数据包的地址,其定义如下:
- strucrt sockaddr_in
- {
- short sin_family;
- u_short sin_port;
- struct in_addr sin_addr;
- char sin_zero[];
- };
其中 in_addr 定义如下:
- struct in_addr
- {
- union
- {
- struct {u_char s_b1, s_b2, s_b3, s_b4} S_un_b;
- struct {u_short s_w1, s_w2} S_un_w;
- u_long S_addr;
- }S_un;
- };
首先阐述 in_addr 的信义。 很显然它是一个存储 ip 地址的联合体,有三种表达方式:
a)第一种用四个字节来表示IP地址的四个数字;
b) 第二种用两个双字节来表示IP地址;
c) 第三种用一个长整型来表示IP地址;
给 in_addr 赋值的一种最简单方法是使用 inet_addr 函数,它可以把一个代表IP地址的字符串赋值。转换为in_addr类型。如:
- addrServer.sin_addr = inet_addr("192.168.0.2");
其反函数是 inet_ntoa,可以把一个 in_addr 类型转换为一个字符串。
sockaddr_in的含义比in_addr的含义要广泛,其各个字段的含义和取值如下:
a)第一字段 short sin_family,代表网络地址族,如前所述,只能取值AF_INET;
b) 第二字段 u_short sin_port,代表IP地址端口,由程序员指定;
c) 第三字段 struct in_addr sin_addr,代表IP地址;
d)第四个字段char sin_zero[8],是为了保证sockaddr_in与SOCKADDR类型的长度相等而填充进来的字段。
(2) sockaddr 类型
sockaddr 类型是用来表示 Socket 地址的类型,同 socketaddr_in 类型相比,sockaddr 的适用范围更广。
TCP/IP 地址。sockaddr 的定义如下:
- struct sockaddr
- {
- ushort sa_family;
- char sa_data[];
- };
可知sockaddr 的16个字节,而sockaddr_in也有16个字节,所以sockaddr_in是可以强制类型转换为sockadddr的。事实上也往往使用这种方法。
2.1.4为套接字设置监听模式,准备客户请求。
当服务器端的 Socket 对象绑定完成之后,服务器端必须建立一个监听的队列来接收客户端的连接请求.listen()函数使服务器端的 Socket 进入监听状态,并设定可以建立的最大连接数(目前最大值限制为 5,最小值为 1).该函数调用成功返回 0,否则返回 SOCKET_ERROR。
- int listen(SOCKET s,int backlog );
参 数:
a) s:需要建立监听的 Socket;
b) backlog:最大连接个数;
2.1.5 接受连接请求
当 Client 提出连接请求时,Server 端 hwnd 视窗会收到 Winsock Stack 送来自定义的一个消息,这时可以分析 lParam,然后调用相关的函数来处理此事件。为了使服务器端接受客户端的连接请求,就要使用accept()函数,该函数新建一 Socket 与客户端的 Socket 相通,原先监听之 Socket 继续进入监听状态,等待他人的连接要求。该函数调用成功返回一个新产生的 Socket 对象,否则返回 INVALID_SOCKET。
- SOCKET accept(SCOKET s,struct sockaddr FAR *addr,int FAR *addrlen );
参数:
a) s: 监听套接字
b) addr:存放来连接的客户端的地址、端口信息;
c) addrlen:addr 的长度
2.1.6 用新返回的套接字和客户端进行通信。send/recv
send函数通过一个已建立连接的套接字发送数据,函数声明如下:
- int send(SOCKET s, const char FAR *buf, int len, int flags);
参数:
a) s: 是一个已建立连接的套接字。
b) buf:指向一个缓冲区,该缓冲区包含将要传递的数据。
c) len:缓冲区的长度。
d) flags:收发数据方式的标识,如果不需要特殊要求可以设置为0。
调用send函数向客户端发送数据,注意这个函数使用的套接字需要使用已建立连接的那个套接字,而不是用于监听的那个套接字。
recv函数从一个已连接的套接字接收数据。函数原型如下:
- int recv(SOCKET s,char FAR* buf, int len, int flags);
参数:
a) s:建立连接之后准备接收数据的那个套接字。
b) buf:指向缓冲区的指针,用来保存接收的数据。
c)len:缓冲区的长度。
d)flags:收发数据方式的标识,如果不需要特殊要求可以设置为0。
发送完数据之后还可以从客户端接收数据,这可以使用recv函数,应注意该函数的第一个参数也应该是建立连接之后的那个套接字,并且定义一个字符数组recvBuf,用来保存接收的数据。
2.1.6 关闭套接字
结束服务器和客户端的通信连接是很简单的,这一过程可以由服务器或客户机的任一端启动,只要调用closesocket()就可以了,而要关闭 Server 端监听状态的 socket,同样也是利用此函数.另外,与程序启动时调用 WSAStartup()函数相对应,程式结束前,需要调用 WSACleanup()来通知 Winsock Dll 释放 Socket 所占用的资源.这两个函数都是调用成功返回 0,否则返回 SOCKET_ERROR
- int PASCAL FAR closesocket( SOCKET s );
参数:
s:Socket 的识别码;
- int PASCAL FAR WSACleanup( void );
win32 socket 编程(三)——TCP/IP的更多相关文章
- 嵌入式linux的网络编程(1)--TCP/IP协议概述
嵌入式linux的网络编程(1)--TCP/IP协议概述 1.OSI参考模型及TCP/IP参考模型 通信协议用于协调不同网络设备之间的信息交换,它们建立了设备之间互相识别的信息机制.大家一定都听说过著 ...
- 计算机网络(十三),Socket编程实现TCP和UDP
十三.Socket编程实现TCP和UDP 1.TCP (1)TCPServer.java类 package com.interview.javabasic.socket; import com.int ...
- 【转载】[基础知识]【网络编程】TCP/IP
转自http://mc.dfrobot.com.cn/forum.php?mod=viewthread&tid=27043 [基础知识][网络编程]TCP/IP iooops 胖友们楼主我又 ...
- win32 socket 编程(一)——TCP/IP
一.基本概念 a) 同步:指发送方发出数据后,等收到接收方发回的响应,才发下一个数据包的通信方式. nb)异步:指的是发送方不等接收方响应,便接着发下个数据包的通信方式. c) 阻塞:指调用某函数时, ...
- win32 socket编程(二)——TCP/IP
一.大端.小端法定义 1.1小端法(Little-Endian)就是低位字节排放在内存的低地址端即该值的起始地址,高位字节排放在内存的高地址端. (主机字节顺序) 1.2 大端法(Big-Endian ...
- win32 socket编程(五)——客户端实例(TCP)
一.客户端操作流程 1.1 加载套接字库(WSAStartup()) 1.2创建套接字(socket()). 1.3向服务器发出连接请求(connect()). 对于客户端来说,它不需要绑定,可以直接 ...
- Linux系统编程(30)—— socket编程之TCP/IP协议
在世界上各地,各种各样的电脑运行着各自不同的操作系统为大家服务,这些电脑在表达同一种信息的时候所使用的方法是千差万别.就好像圣经中上帝打乱了各地人的口音,让他们无法合作一样.计算机使用者意识到,计算机 ...
- day8---多线程socket 编程,tcp粘包处理
复习下socket 编程的步骤: 服务端: 1 声明socket 实例 server = socket.socket() #括号里不写 默认地址簇使用AF_INET 即 IPv4 ...
- 2015-07学习总结——网络编程(TCP/IP)
之前学习的主要内容是单机上的处理,比如编程语言.游戏编程.数据库.多媒体编解码.其实对网络也有些接触,比如WWW.HTTP.UDP/TCP.RTP.RTMP.SNMP.FTP.单播组播.Telnet. ...
随机推荐
- Day01_课后练习题
1.(将摄氏温度转化华氏温度)编写一个从控制台读取摄氏温度并将他转变为华氏温度并予以显示的程序.转换公式如下. Fahrenheit = (9 / 5) * celsius + 32 这里是这个程序 ...
- Linux分屏操作
需要安装工具tmux (1)安装工具 在ubuntu系统中使用sudo apt-get install tmux安装tmux工具 (2)使用工具 1,输入命令tmux使用工具 2,上下分屏:ctrl ...
- 原来程序是这样从NandFlash拷贝并跳转到SDRAM的
重新看了一下FL2440的BootLoader,终于把程序是怎样从Nandflash拷贝并跳转到SDRAM的过程弄清楚了,在这边做一下笔记.先上张图: 1. 当S3C2440被配置成从Nand Fla ...
- Finer Resolution Observation and Monitoring -Global Land Cover更精细的分辨率观测和监测-全球土地覆盖
http://data.ess.tsinghua.edu.cn/ 全球土地覆盖数据是了解人类活动与全球变化之间复杂互动的关键信息来源.FROM-GLC(全球土地覆盖的精细分辨率观测和监测)是首个使用陆 ...
- SpringBoot与数据源
1.JDBC <dependency> <groupId>org.springframework.boot</groupId> <artifactId> ...
- MySQL Schedule Event
建立事件历史日志表-- 用于查看事件执行时间等信息create table t_event_history ( dbname varchar(128) not null default ' ...
- oracle 如何更改密码的hash
如何迁移oracle user的密码到新的环境,一下列出了方法: select name,spare4||';'||password pwd from sys.user$ where name = ' ...
- Python分析《武林外传》 -----转载
转载原链接:http://www.cnblogs.com/fredkeke/p/8328387.html 我一向比较喜欢看武侠电影.小说,但是06年武林外传开播的时候并没有追剧,简单扫几眼之后发现他们 ...
- EXCEL生成随机密码函数
CHAR(INT(RAND()*26+65))&INT(RAND()*9+1)&CHAR(INT(RAND()*26+97))&INT(RAND()*900+100)& ...
- 【C++进阶:移位运算符的用法】
数据在计算机中以补码存储 移位运算符:<< 左移运算符 >> 右移运算符 一丶 << 左移运算符 移位规则:左边抛弃,右边补零 int num=10; num& ...