Download presentation
Presentation is loading. Please wait.
1
TCP/IP Socket网络编程
2
内容大纲 TCP/IP协议体系结构 Socket编程接口 TCP/IP网络程序框架与示例 Windows Socket
Linux Socket TCP/IP网络程序框架与示例
3
Internet与TCP/IP协议 第一节 TCP/IP协议体系结构
4
TCP/IP协议通信模型
5
数据的封装与传递过程
6
一些基本概念 IP地址 端口号 字节序
7
IP地址 IP地址是Internet中主机的标识 表示形式:常用点分形式,如202.38.64.10,最后都会转换为一个32位的整数。
Internet中的主机要与别的机器通信必须具有一个IP地址 一个IP地址为32位(IPV4),或者128位(IPV6) 每个数据包都必须携带目的IP地址和源IP地址,路由器依靠此信息为数据包选择路由 特殊的IP地址:广播地址、多播地址 表示形式:常用点分形式,如 ,最后都会转换为一个32位的整数。 IP地址分级 子网掩码
8
端口号 为了区分一台主机接收到的数据包应该递交给哪个进程来进行处理,使用端口号 TCP端口号与UDP端口号独立
端口号一般由IANA (Internet Assigned Numbers Authority) 管理 众所周知端口:1~1023,1~255之间为大部分众所周知端口,256~1023端口通常由UNIX占用 注册端口:1024~49151 动态或私有端口:49151~65535
9
端到端通信数据包投递过程
10
一个比喻 如果把IP数据包的投递过程看成是给远方的一位朋友寄一封信,那么
端口号就是这位朋友的名字(依靠这个信息最终把这封信交付给这位收信者)
11
字节序 大尾端(Big-Endian):字节的高位在内存中放在存储单元的起始位置 小尾端(Little-Endian):与大尾端相反
12
字节序 网络字节序(NBO,Network Byte Order) 主机字节序(HBO,Host Byte Order)
使用统一的字节顺序,避免兼容性问题 主机字节序(HBO,Host Byte Order) 不同的机器HBO是不一样的,这与CPU的设计有关 Motorola 68K系列,HBO与NBO是一致的 Intel X86系列,HBO与NBO不一致 通信过程中:主机字序网络字序网络字序主机字序
13
Internet与TCP/IP协议 第二节 Socket编程接口
14
内容 Socket简介 Socket常用函数介绍 TCP/IP网络程序框架与实例 通信方式 Windows Socket
Linux Socket Socket常用函数介绍 TCP/IP网络程序框架与实例 通信方式 阻塞 非阻塞
15
为什么需要Socket 普通的I/O操作过程 TCP/IP协议被集成到操作系统的内核中,引入了新型的“I/O”操作
打开文件->读/写操作->关闭文件 TCP/IP协议被集成到操作系统的内核中,引入了新型的“I/O”操作 进行网络操作的两个进程在不同的机器上,如何连接? 网络协议具有多样性,如何进行统一的操作 需要一种通用的网络编程接口:Socket
16
什么是Socket 独立于具体协议的网络编程接口 在ISO模型中,主要位于会话层和传输层之间
BSD Socket(伯克利套接字)是通过标准的UNIX文件描述符和其它程序通讯的一个方法,目前已经被广泛移植到各个平台。
17
Socket类型 流式套接字(SOCK_STREAM) 数据报套接字(SOCK_DGRAM) 原始套接字(SOCK_RAW)
提供了一个面向连接、可靠的数据传输服务,数据无差错、无重复的发送且按发送顺序接收。内设置流量控制,避免数据流淹没慢的接收方。数据被看作是字节流,无长度限制。 数据报套接字(SOCK_DGRAM) 提供无连接服务。数据包以独立数据包的形式被发送,不提供无差错保证,数据可能丢失或重复,顺序发送,可能乱序接收。 原始套接字(SOCK_RAW) 可以对较低层次协议,如IP、ICMP直接访问。
18
Socket的位置
19
两类系统中使用的Socket 不同操作系统中的Socket Windows Socket (Winsock)
Linux Socket (BSD Socket)
20
Windows Socket 简称Winsock,是在Windows环境下使用的一套网络编程规范,基于4.3BSD的BSD Socket API制定 1991年Winsock 1.1,16位,由WINSOCK.DLL支持,主要用在Windows 95中 1997年Winsock 2.2 版,32位,由WSOCK32.DLL支持,主要用在Windows 98及以后的版本中 已经成为Windows环境下网络编程的事实标准 三类函数 与BSD Socket相兼容的基本函数 与BSD Socket相兼容的网络信息检索函数 Windows专用扩展函数
21
Linux Socket 基本上就是BSD Socket 需要使用的头文件
数据类型:#include <sys/types.h> 函数定义:#include <sys/socket.h>
22
Socket常用函数介绍 基本函数 网络信息检索函数
23
基本函数 网络连接函数 socket 创建套接字 bind 绑定本机端口 connect 建立连接 listen 监听端口
accept 接受连接 recv, recvfrom 数据接收 send, sendto 数据发送 close, shutdown 关闭套接字
24
基本函数 转换函数 IP地址转换函数 字节排序函数 inet_addr() 点分十进制数表示的IP地址转换为网络字节序的IP地址
inet_ntoa() 网络字节序的IP地址转换为点分十进制数表示的IP地址 字节排序函数 htonl 4字节主机字节序转换为网络字节序 ntohl 4字节网络字节序转换为主机字节序 htons 2字节主机字节序转换为网络字节序 ntohs 2字节网络字节序转换为主机字节序
25
网络信息检索函数 网络信息检索函数 gethostname 获得主机名 getpeername 获得与套接口相连的远程协议地址
getsockname 获得套接口本地协议地址 gethostbyname 根据主机名取得主机信息 gethostbyaddr 根据主机地址取得主机信息 getprotobyname 根据协议名取得主机协议信息 getprotobynumber 根据协议号取得主机协议信息 getservbyname 根据服务名取得相关服务信息 getservbyport 根据端口号取得相关服务信息 getsockopt/setsockopt 获取/设置一个套接口选项 ioctlsocket 设置套接口的工作方式
26
Windows中的Socket编程 Windows中的Socket编程 Winsock 的启动 Winsock API基本函数
TCP/IP网络程序框架(C/S模式) 阻塞与非阻塞通信方式 实例程序说明
27
Winsock Winsock是一个基于Socket模型的API,在Windows系统中广泛使用
它在Berkeley接口函数的基础上,还增加了基于消息驱动机制的Windows扩展函数 Winsock1.1只支持TCP/IP网络,Winsock2.2增加了对更多协议的支持
28
Winsock(2) 需要包含头文件Winsock2.h,需要使用库ws2_32.lib,包含办法可以用语句来告诉编译时调用该库
#pragma comment(lib,”ws2_32.lib”); 如果使用Visual C++ 6.0,可以通过“工程” > “设置”>“工程设置”>“链接”>“对象/库模块”中加入“ws2_32.lib”
29
Windows Socket的启动 使用Winsock API编制的网络应用程序中,在调用任何一个Winsock函数之前都必须检查协议栈安装情况,使用函数WSAStartup()完成操作。 int WSAStartup( WORD wVersionRequested, LPWSADATA lpWSAData ); wVersionRequested是一个WORD型(双字节型)数值,指定使用的版本号,对Winsock2.2而言,此参数的值为0x0202,也可以用宏MAKEWORD(2,2)来获得 lpWSAData是一个指向WSADATA结构的指针,它返回关于Winsock实现的详细信息
30
Winsock启动示例 #include <Winsock2.h> WORD wVersionRequested;
WSADATA wsaData; wVersionRequested=MAKEWORD(2,2); if(WSAStartup(wVersionRequested,&wsaData)!=0) { //Winsock初始化错误 return; } if(wsaData.wVersion!=wVersionRequested) { //Winsock版本不匹配 WSACleanup(); //说明WinsockDLL正确加载,可以执行以下代码
31
创建套接口socket() 应用程序在使用套接口通信前,必须要拥有一个套接口,使用socket()函数来给应用程序创建一个套接口。
int af, int type, int protocol );
32
socket()参数说明 af参数说明套接字接口要使用的协议地址族。如果想建立一个TCP或UDP,只能用常量AF_INET表示使用互联网协议(IP)地址。 type参数描述套接口的类型,af是AF_INET的时候只能为SOCK_STREAM、SOCK_DGRAM或SOCK_RAW protocol说明该套接口使用的特定协议,当协议地址族af和协议类型type确定后,协议字段可以使用的值是限定的 协议 地址族 套接口类型 套接口类型使用的值 协议字段 互联网协议(IP) AF_INET TCP SOCK_STREAM IPPROTO_TCP UDP SOCK_DGRAM IPPROTO_UDP Raw SOCK_RAW IPPROTO_RAW IPPROTO_ICMP
33
指定本地地址-bind() 当socket()创建了一个套接口后,需要将该套接口与该主机上提供服务的某端口联系在一起,bind()函数用于完成这样的绑定。 int bind( SOCKET s, const struct sockaddr FAR * name, int namelen );
34
bind()参数说明 s标识未绑定的套接口描述字,是socket()函数调用成功时返回的值
name是一个与指定协议有关的地址结构指针,存储了套接口的地址信息,Winsock中使用sockaddr_in结构指定IP地址和端口信息 struct sockaddr_in{ short sin_family; u_short sin_port; struct in_addr sin_addr; char sin_zero[8]; } sin_family一般为AF_INET,表示使用IP地址族;sin_port是以网络字节序表示的16位端口号;sin_addr是网络字节序的32位IP地址;sin_zero字段一般不用,用0填充 namelen表示地址参数(name)的长度 IP地址参数为INADDR_ANY,则由系统内核来自动指定,port为0,则由系统自动指派一个1024~5000之间惟一的端口号
35
bind()实例 #include <Winsock2.h> SOCKET s; sockaddr_in tcpaddr;
int iSockErr; int port=5000; //端口号 s=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); tcpaddr.sin_family=AF_INET; tcpaddr.sin_port=htons(port); tcpaddr.sin_addr.s_addr=htonl(INADDR_ANY); if(bind(s,(LPSOCKADDR)&tcpaddr,sizeof(tcpaddr))==SOCKET_ERROR){ iSockErr=WSAGetLastError(); //根据不同的错误类型进行不同的处理 return; } 函数调用成功,进行其他处理。
36
服务器端启动监听-listen()函数 在一个服务器端用socket()调用成功创建了一个套接口,并用bind()函数和一个指定的地址关联后,就需要指示该套接口进入监听连接请求状态,这需要通过listen()函数来实现 int listen( SOCKET s, int backlog ); s代表一个已绑定了地址,但还未建立连接的套接口描述字 backlog指定了正在等待连接的最大队列长度(1-5)
37
客户端请求连接-connect()函数 当服务器端建立好套接口并与一个本地地址绑定后,就进入监听状态,等待客户发出连接请求。在客户端套接口建立好之后,就调用connect()函数来与服务器建立连接。 int connect( SOCKET s, const struct sockaddr FAR * name, int namelen );
38
connect()函数参数说明 s将要建立连接的套接口描述字
name是一个指向远端套接口地址结构(sockaddr_in)的指针,表示s套接口欲与其建立一条连接 namelen是服务器端的地址长度,即name的长度
39
Connect()函数的说明 在客户端使用该函数请求建立连接时,将激活建立连接的三次握手,用来建立一条到服务器TCP的连接。如果调用该函数前没有调用bind()来绑定本地地址,则由系统隐式绑定一个地址到该套接口 该函数用在UDP的客户端时,connect()函数并不是真正地发出建立请求连接的请求,调用将从本地操作系直接返回。这样可以将服务器的地址信息保存下来,在后续UDP端口发送数据时,由套接口自动在发送函数中填入服务器地址,而不需要由应用程序在调用发送函数时填入
40
服务器端接受连接-accept()函数 在服务器端通过listen()函数调用表示服务器进入监听客户的连接请求状态,而在服务器端调用accept()函数时表示可以接收来自客户端由connect()发出的连接请求,双方进入连接状态。 SOCKET accept( SOCKET s, struct sockaddr FAR * addr, int FAR * addrlen );
41
addr是一个地址结构的指针,用来存放发出连接请求的那个客户机的IP地址信息 addrlen指出客户套接口地址结构的长度
accept()函数参数说明 s标识一个套接字,该套接口处于监听状态 addr是一个地址结构的指针,用来存放发出连接请求的那个客户机的IP地址信息 addrlen指出客户套接口地址结构的长度 函数说明:该函数用于面向连接的服务器端,在IP协议族中,只用于TCP服务器端
42
在已经建立连接的套接口上发送数据,可以使用send()函数 int send( SOCKET s,
const char FAR * buf, int len, int flags );
43
send()函数参数说明 s用于标识已建立连接的套接字 buf是一个字符缓冲区,内有将要发送的数据 len即将发送的缓冲区中的字符数 flags用于控制数据传输方式,0表示按正常方式发送数据;宏MSG_DONTROUTE说明系统目标主机就在直接连接的本地网络中,无需路由选择;MSG_OOB指出数据是按带外数据发送的 函数说明:send()函数适用于已建立连接的数据报或流式套接口发送数据,对于数据报类型套接口必须注意发送数据长度不大于通信子网的IP包最大长度
44
对于已建立连接的套接口来说,要从套接口上接收数据,就要使用recv()函数。 int recv( SOCKET s,
char FAR * buf, int len, int flags );
45
s为已建立连接的套接口 buf为用于接收数据的缓冲区 len为缓冲区的长度
recv()函数参数说明 s为已建立连接的套接口 buf为用于接收数据的缓冲区 len为缓冲区的长度 flags指定调用的方式。0表示接收的是正常数据,无特殊行为。MSG_PEEK表示会使有用的数据复制到所提供的接收端缓冲区内,但是没有从系统缓冲区中将数据删除。MSG_OOB表示处理带外数据。
46
无连接的套接口上接收数据-recvfrom()
int recvfrom( SOCKET s, char FAR * buf, int len, int flags, struct sockaddr FAR * from, int FAR * fromlen );
47
recvfrom()函数参数说明 s标识一个套接口的描述字 buf接收数据的缓冲区 len接收数据缓冲区的长度
flags调用操作方式,同recv()中的flags from可选指针,指向装有源地址的缓冲区 fromlen可选指针,指向from缓冲区的长度值 函数说明:该函数的用法与有连接时recv()的用法一致,要注意的是该函数也可以用于有连接时数据的接收
48
在无连接套接口上发送数据-sendto()
int sendto( SOCKET s, const char FAR * buf, int len, int flags, const struct sockaddr FAR * to, int tolen );
49
sendto()函数参数说明 s本机的套接字 buf待发送数据的缓冲区 len指明buf缓冲区中要发送的数据长度
flags调用方式标志位,同send()中的flags to可选指针,指向接收数据的目的套接口地址 tolen是to所指的地址的长度 函数说明:该函数的使用方法类似send()函数,当用于无连接套接字接口,调用函数前要设置,指出目标IP地址和目标端口号。如果用于有连接的套接口时,则不能指定目标地址和目标端口,将to设置为空,地址长度设为0。当然在有连接的情况下很少使用该函数
50
关闭读写通道-shutdown()函数 在一个套接口上的读写操作完成后,应该首先使用shutdown()函数来关闭套接口的读通道、写通道或读写通道,这样做的好处是当双方不再有数据要发送或接收时,可以通知对方,以防止数据丢失,并能“优雅”地关闭连接。 int shutdown( SOCKET s, int how );
51
shutdown()函数参数说明 1 2 s标识一个套接口的描述字 how是一个标志,用于描述禁止哪些操作,取值如下表所示 关闭方式 参数值
说 明 SD_RECEIVE 表示不允许再调用接收函数,它关闭读通道。套接口接收缓冲区中的所有数据都被丢弃,并且有新数据到达套接口时,也被TCP协议层丢弃,但它对发送缓冲区没有影响,进程仍然可以在套接口上发送数据 SD_SEND 1 表示不允许再调用发送函数,它关闭写通道。在套接口发送缓冲区中的数据都被发送出去,得到接收端确认之后,就生成一个FIN包关闭连接。但它对接收缓冲区没有影响,进程仍然可以在套接口上接收数据 SD_BOTH 2 关闭读写通道,相当于执行了上面SD_RECEIVE和SD_SEND两个命令
52
关闭套接口-closesocket()函数
shutdown函数只关闭读写通道,并不关闭套接口,且套接口所占有的资源将被一直保留到closesocket()调用之前。 一个套接口不再使用时一定要关闭这个套接口,以释放与该套接口关联的所有资源,包括等候处理的数据。 int closesocket( SOCKET s ); 参数s表示即将被关闭的套接口
53
char * inet_ntoa ( struct in_addr in )
IP地址转换函数 char * inet_ntoa ( struct in_addr in ) in为传入参数,表示一个结构型的IP主机地址,该函数将一个32位数字表示的IP地址转换成点分十进制IP地址字符串 unsigned long inet_addr(const char FAR * cp) 该函数将一个点分十进制IP地址字符串转换成32位数字表示的IP地址。 两函数互为反函数
54
字节序转换函数 u_long htonl( u_long hostlong )
4字节主机字节序表示的整数转换为4字节相应的网络字节序表示的整数 u_short htons( u_short hostshort ) 2字节主机字节序表示的整数转换为2字节相应的网络字节序表示的整数 u_long ntohl( u_long netlong ) 4字节网络字节序表示的整数转换为4字节相应的主机字节序表示的整数 u_short ntohs( u_short netshort ) 2字节网络字节序表示的整数转换为2字节相应的主机字节序表示的整数
55
终止使用Winsock-WSACleanup()函数
当应用程序不再使用Winsock API中的任何函数时,必须调用WSACleanup()将其从Windows Socket的实现中注销,以释放为此应用程序或DLL分配的任何资源。 int WSACleanup(void); 函数说明:WSACleanup()函数是任何一个Winsock应用程序在最后必须要调用的函数。在一个多线程的环境下,WSACleanup()函数中止了Windows Sockets在所有线程上的操作
56
TCP/IP网络程序框架 面向连接的C/S程序工作流程 无连接的C/S程序工作流程
57
面向连接的C/S程序工作流程(TCP) 服务器端工作流程 使用WSAStartup()函数检查系统协议栈安装情况
使用socket()函数创建服务器端通信套接口 使用bind()函数将创建的套接口与服务器地址绑定 使用listen()函数使服务器套接口做好接收连接请求准备 使用accept()接收来自客户端由connect()函数发出的连接请求 根据连接请求建立连接后,使用send()函数发送数据,或者使用recv()函数接收数据 使用closesocket()函数关闭套接口(可以先用shutdown()函数先关闭读写通道) 最后调用WSACleanup()函数结束Winsock Sockets API
58
客户端程序工作流程 面向连接的C/S程序工作流程(TCP) 使用WSAStartup()函数检查系统协议栈安装情况
使用socket()函数创建客户端套接口 使用connect()函数发出也服务器建立连接的请求(调用前可以不用bind()端口号,由系统自动完成) 连接建立后使用send()函数发送数据,或使用recv()函数接收数据 使用closesocet()函数关闭套接口 最后调用WSACleanup()函数,结束Winsock Sockets API
59
<本地IP地址,本地端口号> <远程IP地址,远程端口号>
面向连接的C/S程序工作流程(TCP) 服务器与客户端五元组的建立 五元组 <协议> <本地IP地址,本地端口号> <远程IP地址,远程端口号> 服务器端五元组 由socket()确定 由服务器端调用bind()时确定 由accept()确定 客户端五元组 由客户端的bind()调用确定。如果客户端没有进行bind()调用,或调用了bind()但没有指定具体地址或端口号,则由系统内核自动确定地址和端口 由connect()确定
60
面向连接的C/S程序工作流程图(TCP)
61
无连接的C/S程序工作流程(UDP) 无连接的数据报传输服务通信时,客户端与服务器端所使用的函数是类似的,其工作流程如下:
使用WSAStartup()函数检查系统协议栈的安装情况 使用socket()函数创建套接口,以确定协议类型 调用bind()函数将创建的套接口与本地地址绑定,确定本地地址和本地端口号 使用sendto()函数发送数据,或者使用recvfrom()函数接收数据 使用closesocket()函数关闭套接口 调用WSACleanup()函数,结束Windows Sockets API
62
无连接的C/S程序工作流程(UDP) 注意事项: 通信的一方可以不用bind()绑定地址和端口,由系统分配
不绑定IP、端口号的一方必须先向绑定地址的一方发送数据 无连接的应用程序也可以调用connect()函数,在本地返回,由内核将connect()中指定的目标IP地址和端口号记录下来,在以后的通信中就可以使用面向连接的数据发送函数send()和数据接收函数recv() 无连接的数据报传输过程中,作为服务器的一方必须先启动 无连接客户端一般不调用connect(),在数据发送前客户与服务器各自通过socket()和bind()建立了半相关,发送数据时除指定本地套接口的地址外,还需要指定接收方套接口地址,从而在数据收发过程中动态建立全连接
63
无连接的C/S程序工作流程图(UDP)
64
阻塞通信与非阻塞通信 阻塞方式:套接字进行I/O操作时,函数要等待到相关的操作完成以后才能返回,对提高处理机的利用率不利,但编程简单。
阻塞方式编程简单,一个套接口的默认操作模式为阻塞,可以调用函数ioctlsocket()进行设置。
65
并发服务器
66
Internet与TCP/IP协议 第三节 网络编程实例
67
基于TCP的客户/服务器-服务器代码 // server.cpp : 定义控制台应用程序的入口点。 #include "stdafx.h"
#include <Winsock2.h> #include <stdio.h> #include <stdlib.h> #define DEFAULT_PORT 5050 //服务端默认端口 int _tmain( ) { int iPort = DEFAULT_PORT; WSADATA wsaData; SOCKET sListen,sAccept; int iLen; //客户地址长度 int iSend;//发送数据长度 char buf[] = "I am a server";//要发送给客户的信息 struct sockaddr_in ser,cli;//服务器和客户的地址 if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0) printf("Failed to load Winsock.\n"); return -1; }
68
基于TCP的客户/服务器-服务器代码 sListen = socket(AF_INET,SOCK_STREAM,0);//创建服务器端套接口
if(sListen == INVALID_SOCKET) { printf("socket() Failed: %d\n",WSAGetLastError()); return -1; } //以下建立服务器端地址 //使用IP地址族 ser.sin_family = AF_INET; //使用htons()把双字节主机序端口号转换为网络字节序端口号 ser.sin_port = htons(iPort); //htonl()把一个四字节主机序IP地址转换为网络字节序主机地址 //使用系统指定的IP地址INADDR_ANY ser.sin_addr.s_addr = htonl(INADDR_ANY); //bind()函数进行套接定与地址的绑定 if(bind(sListen,(LPSOCKADDR)&ser,sizeof(ser)) == SOCKET_ERROR) printf("bind() Failed: %d\n",WSAGetLastError());
69
基于TCP的客户/服务器-服务器代码 //进入监听状态 if(listen(sListen,5) == SOCKET_ERROR) {
printf("lisiten() Failed: %d\n",WSAGetLastError()); return -1; } //初始化客户地址长度参数 iLen = sizeof(cli); //进入一个无限循环,等待客户的连接请求 while(1) sAccept = accept(sListen,(struct sockaddr *)&cli,&iLen); if(sAccept == INVALID_SOCKET) printf("accept() Failed: %d\n",WSAGetLastError()); //输出客户IP地址和端口号 printf("Accepted client IP:[%s],port:[%d]\n",inet_ntoa(cli.sin_addr),ntohs(cli.sin_port));
70
基于TCP的客户/服务器-服务器代码 iSend = send(sAccept,buf,sizeof(buf),0);
//给连接的客户发送信息 iSend = send(sAccept,buf,sizeof(buf),0); if(iSend == SOCKET_ERROR) { printf("send() Failed: %d\n",WSAGetLastError()); break; } else if(iSend == 0) else printf("send() byte: %d\n",iSend); closesocket(sAccept); closesocket(sListen); WSACleanup(); return 0;
71
基于TCP的客户/服务器-客户端代码 // client.cpp : 定义控制台应用程序的入口点。 #include "stdafx.h"
#include <Winsock2.h> #include <stdio.h> #include <stdlib.h> #define DATA_BUFFER 1024 //默认缓冲区大小 int _tmain(int argc, char * argv[]) { WSADATA wsaData; SOCKET sClient; int iPort = 5050; int iLen;//从服务器端接收的数据长度 char buf[DATA_BUFFER];//接收数据的缓冲区 struct sockaddr_in ser;//服务器端地址 //判断参数输入是否正确:client [Server IP] if(argc<2) //提示在命令行中输入服务器IP地址 printf("Usage: client [server IP address]\n"); return -1; }
72
基于TCP的客户/服务器-客户端代码 memset(buf,0,sizeof(buf));//接收缓冲区初始化
if(WSAStartup(MAKEWORD(2,2),&wsaData)!=0) { printf("Failed to load Winsock.\n"); return -1; } //填写要连接的服务器地址信息 ser.sin_family = AF_INET; ser.sin_port = htons(iPort); //inet_addr()将命令行中输入的点分IP地址转换为二进制表示的网络字节序IP地址 ser.sin_addr.s_addr = inet_addr(argv[1]); //建立客户端流式套接口 sClient = socket(AF_INET,SOCK_STREAM,0); if(sClient == INVALID_SOCKET) printf("socket() Failed: %d\n",WSAGetLastError());
73
基于TCP的客户/服务器-客户端代码 //请求与服务器端建立TCP连接
if(connect(sClient,(struct sockaddr *)&ser,sizeof(ser)) == INVALID_SOCKET) { printf("connect() Failed: %d\n",WSAGetLastError()); return -1; } else //从服务器端接收数据 iLen = recv(sClient,buf,sizeof(buf),0); if(iLen == 0) else if(iLen == SOCKET_ERROR) printf("recv() Failed: %d\n",WSAGetLastError()); printf("recv() data from server: %s\n",buf);
74
基于TCP的客户/服务器-客户端代码 服务器端 客户端 closesocket(sClient); WSACleanup();
return 0; } 服务器端 客户端
75
高级网络编程API MFC编程技术定义了用于网络编程的Winsock类,类名为CAsyncSocket;还定义了一个派生于CAsyncSocket的CSocket类。这两个类简单实用,用户可以使用它们来实现自己的网络程序。 与前面的介绍相似,使用MFC的Winsock类进行操作时需要使用Winsock2.h、Winsock32.dll和ws2_32.lib三个文件
76
课后习题 使用UDP数据报套接字改写示例程序。 实现一个简单的具有聊天室功能的服务器以及相应的客户端,可以使用UDP也可以使用TCP。
Similar presentations