网络编程技术
This presentation is the property of its rightful owner.
Sponsored Links
1 / 124

网络编程技术 PowerPoint PPT Presentation


  • 111 Views
  • Uploaded on
  • Presentation posted in: General

网络编程技术. 设计、制作、授课 : 谭献海 Email : [email protected] 常用的网络编程技术(方法) : 数据链路级(帧) 直接网卡编程(可编程芯片) 基于 packet driver 的编程技术 基于 NDIS ( Network Driver Interface Specification ) 的网络编程 VPACKET , PACKET32 BPF ( Libpcap/winpcap) 网络层或传输层( IP/TCP/UDP ): Berkeley Sockets 编程技术 Winsock JAVA 网络编程 高级:

Download Presentation

网络编程技术

An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -

Presentation Transcript


4737528

网络编程技术

设计、制作、授课:谭献海

Email:[email protected]


4737528

常用的网络编程技术(方法) :

数据链路级(帧)

直接网卡编程(可编程芯片)

基于packet driver的编程技术

基于NDIS( Network Driver Interface Specification )的网络编程

VPACKET,PACKET32

BPF(Libpcap/winpcap)

网络层或传输层(IP/TCP/UDP):

Berkeley Sockets编程技术

Winsock

JAVA网络编程

高级:

JAVA网络编程

ASP,PHP,JSP,.net (自学)

高级语言网络编程类库(VB,VC,delphi)(自学)

网络编程技术(方法)概述


Socket

Topics

Socket编程基础

Socket函数介绍

基于socket的通信流程

Socket编程实例

Socket编程技术


4737528

Socket编程基础


4737528

Internet

Client Process

Server Process

Internet

transport layer (TCP/UDP)

transport layer (TCP/UDP)

network layer (IP)

network layer (IP)

link layer (e.g. ethernet)

link layer (e.g. ethernet)

physical layer

physical layer

Internet

Sockets as means for inter-process communication (IPC)

application layer

application layer

Socket

Socket

OS network

stack

OS network

stack

  • An interface between application and the network

  • The application can send/receive data to/from the network -- communication


Sockets

What is a socket?

To the kernel, a socket is an endpoint of communication.

To an application, a socket is a file descriptor that lets the application read/write from/to the network.

Remember: All Unix I/O devices, including networks, are modeled as files. (So as Windows)

Clients and servers communicate with each by reading from and writing to socket descriptors.

The main distinction between regular file I/O and socket I/O is how the application “opens” the socket descriptors.

Sockets


Socket1

Socket API

UC Berkeley

BSD unix /Berkeley unix

De facto standard

socket

通信协议应用程序接口(APIs)----依赖于操作系统和编程语言

UNIX: Berkeley Sockets (C语言)

System V: Transport Layer Interface (TLI) (C语言)

WINDOWS:WINSOCK


Socket2

socket是实现进程间通信(IPC)的 BSD 方法。

类比:客户将插头插入一个服务器端口

建立一个双向的连接管道

socket的抽象表示

客户端

服务器

插口(port)


Socket3

网络地址 (network address)

IP地址 (IP Address)

2种不同的通信方式(面向连接/无连接)

流(Stream)

阻塞(Block)、非阻塞(Non-block)

同步(Synchronous)、异步(asynchronous)

字节顺序(Bytes Order)

带外数据(Out-of-band Data)

一些有关Socket编程的概念


A socket eye view of the internet

Each host machine (or network interface) has an/more IP address

A Socket-eye view of the Internet

medellin.cs.columbia.edu

(128.59.21.14)

newworld.cs.umass.edu

(128.119.245.93)

cluster.cs.columbia.edu

(128.59.21.14, 128.59.16.7, 128.59.16.5, 128.59.16.4)


Ports

Each host application has one/more ports (1~65,536)

Some ports (0~1023) are reserved for specific applicatons

20,21: FTP

23: Telnet

80: HTTP

Ports

Port 0

Port 1

Port 65535


Address pair

An address is an IP + port

A socket provides an interface to an IP:port pair

Address Pair

Local IP: 111.22.3.4

Local Port: 2249

Remote IP: 123.45.6.78

Remote Port: 3726


Socket4

流式套接字(SOCK_STREAM)

提供了一种面向连接、可靠的数据传输服务,数据无差错、无重复的发送且按发送顺序接收。内设置流量控制,避免数据流淹没慢的接收方。数据被看作是字节流,无长度限制。

数据报套接字(SOCK_DGRAM)

提供无连接服务。数据包以独立数据包的形式被发送,不提供无差错保证,数据可能丢失或重复,顺序发送,可能乱序接收。

原始套接字(SOCK_RAW)

可以对较低层次协议,如IP、ICMP直接访问。

原始套接字保存了数据包中的完整IP头,而流和数据报两种套接字只能收到IP数据。

Socket类型


Two essential types of sockets

SOCK_STREAM

a.k.a. TCP

reliable delivery

in-order guaranteed

connection-oriented

bidirectional

SOCK_DGRAM

a.k.a. UDP

unreliable delivery

no order guarantees

no notion of “connection”

can send or receive

socket

socket

D1

3

2

1

Dest.

3

2

D2

1

App

App

D3

Two essential types of sockets


Socket5

Socket的位置


Stream

所谓流,是指一串具有相同的5元组(源IP地址、目的IP地址、协议和传输层源/目的端口号)的分组的集合。

流(Stream)


Byte ordering

Problem:

different machines / OS’s use different word orderings

little-endian: lower bytes first, e.g. Intel CPU

big-endian: higher bytes first, e.g. Motorola 68k CPU

these machines may communicate with one another over the network

128

128

119

119

40

40

12

12

byte-ordering

Big-Endian

machine

Little-Endian

machine

12.40.119.128

128.119.40.12


Network byte order

All values stored in a sockaddr_inmust be in network byte order (NBO:big-endian).

Whenever the source of the address isn’t the network, use htonl and htons to transform

Network Byte Order

Common Mistake:

Ignoring Network Byte Order


4737528

流套接口的抽象中包括了带外数据这一概念

带外数据是相连的每一对流套接口间一个逻辑上独立的传输通道

带外数据是独立于普通数据传送给用户的,这一抽象要求带外数据设备必须支持每一时刻至少一个带外数据消息被可靠地传送

在任何时刻仅有一个带外数据信息等候发送

对于仅支持带内数据的通讯协议(例如IP)来说,紧急数据是与普通数据在同一序列中发送的,系统通常把紧急数据从普通数据中分离出来单独存放(看成是带外数据)。

带外数据


4737528

Socket函数介绍


Berkeley socket

数据类型:#include <sys/types.h>

函数定义:#include <sys/socket.h>

地址类型: netdb.h

Berkeley socket需要用到的头文件


Berkeley socket1

网络连接函数

获取/设置socket的参数或信息函数

转换函数

Berkeley Socket 常用函数列表


Network functions needed

Specify local and remote communication endpoints

Initiate a connection

Send and receive data

Terminate a connection

Network Functions needed:


Network functions needed1

socket

bind

connect

listen

accept

select

recv, recvfrom

send, sendto

close, shutdown

Network Functions needed:


Socket6

网络信息检索函数

gethostname 获取主机名

getpeername 获取与套接口相连的远程协议地址

getsockname 获取套接口本地协议地址

gethostbyname 根据主机名获取主机信息

gethostbyaddr  根据主机地址获取主机信息

getprotobyname 根据协议名获取主机协议信息

getprotobynumber 根据协议号获取主机协议信息

getservbyname 根据服务名获取相关服务信息

getservbyport 根据端口号获取相关服务信息

getsockopt/setsockopt 获取/设置套接口选项

ioctlsocket 设置套接口的工作方式

获取/设置socket的参数或信息


4737528

IP地址转换

inet_addr()

inet_ntoa()

字节顺序转换

htons()--"Host to Network Short"

htonl()--"Host to Network Long"

ntohs()--"Network to Host Short"

ntohl()--"Network to Host Long"

Notes: Short—16bit; Long—32bit

转换函数


Sockaddr

struct sockaddr {

unsigned short sa_family; /* address family, AF_xxx */

char sa_data[14]; /* 14 bytes of protocol address */

};

此数据结构用作bind、connect、recvfrom、sendto等函数的参数,指明地址信息

数据结构:sockaddr


Socket address

The address family is one of

AF_UNIXUnix internal protocols

AF_INETInternet protocols

AF_NSXerox NS protocols

AF_IMPLINKIMP link layer

其中,AF_ 代表“Address Family”

socket address


Socket address1

Socket地址的数据部分根据不同的地址类型来解释,常见的地址类型是Internet、XNS和UNIX。

socket address


Socket address structures for internet xns and unix families

2-byteFamily

2-byteFamily

2-byteFamily

2-byte port

4-byte net ID

Pathname

(up to 108 bytes)

6-byte host ID

4-byte netid,hostid

2-byte port

(unused)

(unused)

socket address structures for Internet,XNS and Unix families

Struct sockaddr_in(因特网) Struct sockaddr_ns(XNS)

struct sockaddr_un(Unix)


4737528

对于使用最多的Internet簇,其网络地址主要包括两大部分:端口号和IP地址,它的地址结构在<netinet/in.h>中定义

因特网地址


Sockaddr in

Internet地址

struct sockaddr_in {

short int sin_family; /* Address family */

unsigned short int sin_port; /* Port number */

/* 16-bit port number , network byte ordered */

struct in_addr sin_addr; /* Internet address */

/* 32-bit netid/hosted (IP)地址,network byte ordered */

unsigned char sin_zero[ 8]; /* unused */

};

该结构与sockaddr兼容,供用户填入IP地址参数

数据结构:sockaddr_in


Sin port

端口号通常分为三个范围块

0 ~ 1023:由IANA(Internet Assigned Numbers Authority)管理,保留为公共的服务使用

1024 ~ 49151:普通用户注册的端口号

49152 ~ 65535:动态或私有的端口号

sin_port


Sockaddr in1

struct sockaddr_in my_addr;

my_addr.sin_family = AF_INET;

my_addr.sin_port = htons(3490); /* short, NBO*/

my_addr.sin_addr.s_addr = inet_addr("132.241.5.10");

bzero(&(my_addr.sin_zero), 8);

程序中填写sockaddr_in结构


4737528

特殊地址INADDR_ANY允许服务器监听主机的每个网络接口上的客户机活动,相当于地址0.0.0.0 。采用多个网卡的话,使用这种bind,将在所有网卡上进行绑定。在这种情况下,你可以收到发送到所有有效地址上数据包。

特殊地址INADDR_BROADCAST用于在一个IP网络中发送广播UDP数据报。要使用这个特殊地址,需要应用设置套接字选项SO_BROADCAST。

特殊地址


4737528

Socket基本函数


Socket7

功能:创建一个socket

函数调用:

int socket(int family, int type, int protocol);

函数返回Socket描述符,返回-1表示出错

family参数一般取AF_INET, protocol参数一般取0

应用示例:

TCP:sockfd = socket(AF_INET,SOCK_STREAM,0);

UDP:sockfd =socket(AF_INET, SOCK_DGRAM,0);

socket()


Socket8

‘int protocol’通常设定为0。这样的目的是使系统选择默认的由协议族和连接类型所确定的协议,如:

sockfd = socket(AF_INET, SOCK_STREAM, 0); /* create TCP socket */

socket()

Note: 0 implies IP_PROTO_TCP

  • 也有少量的特殊应用场合,需要特别声明具体的协议域。

  • “协议”域与“family”域及“type”域的可能组合如下:


Family type

Family

Type

Protocol

Antual protocol

AF_INET

AF_INET

AF_INET

AF_INET

SOCK_DGRAM

SOCK_STREAM

SOCK_RAW

SOCK_RAW

IPPROTO_UDP

IPPROTO_TCP

IPPROTO_ICMP

IPPROTO_RAW

UDP

TCP

ICMP

IP

AF_NS

AF_NS

AF_NS

AF_NS

SOCK_STREAM

SOCK_SEQPACKET

SOCK_RAW

SOCK_RAW

NSPROTO_SPP

NSPROTO_SPP

NSPROTO_ERROR

NSPROTO_RAW

SPP

SPP

Error Protocol

(raw)

“协议” 与“family” 及“type”域的可能组合

常见TCP/IP协议的定义:

#define IPPROTO_IP 0  #define IPPROTO_ICMP 1 

#define IPPROTO_IGMP 2  #define IPPROTO_TCP 6 

#define IPPROTO_UDP 17  #define IPPROTO_RAW 255 


4737528

在服务器上运行,给socket指定一个众所周知的 (well-known) 端口地址

int bind (int sockfd, const struct sockaddr *myaddr, socklen_t addrlen)

IP 地址+端口号

bind()

如果调用成功,则返回值为0,如果调用失败返回值为-1,并设定相应的错误代码errno。最常见的错误是该端口已经被其他程序绑定。

需要注意的一点:在Linux系统中,小于1024的端口(即众所周知的端口)只有拥有root权限的程序才能绑定


4737528

Bind()系统调用主要用于:

A)服务员向系统注册它的众所周知的地址,它告诉系统:“这是我的地址(服务),所有以这个地址接收的报文都交给我,由我来服务。”面向连接和无连接的服务员在接受顾客的请求之前都必须做这一步。

B)顾客可为它自己注册一个特定的地址,以便通信的对方(服务员)可以用这个有效的地址送回响应,这就像在信封上写明回信地址的道理一样。

bind()


Skipping the bind

SOCK_DGRAM:

if only sending, no need to bind. The OS finds a port each time the socket sends a packet

if receiving, need to bind

SOCK_STREAM:

The OS binds a port to the client during connection setup

Skipping the bind()


Connection setup sock stream

A connection occurs between two kinds of participants

passive: waits for an active participant to request connection

active: initiates connection request to passive side

Once connection is established, passive and active participants are “similar”

both can send & receive data

either can terminate the connection

Connection Setup (SOCK_STREAM)


Connection setup cont d

Passive participant

step 1: listen (for incoming requests)

step 3: accept (a request)

step 4: data transfer

The accepted connection is on a new socket

The old socket continues to listen

Active participant

step 2: request & establish connection

step 4: data transfer

l-sock

a-sock-1

socket

socket

a-sock-2

Connection setup cont’d

Passive Participant

Active 1

Active 2


Listen

服务器开始监听已经绑定的端口

需要在此前调用bind()函数,否则由系统指定一个随机的端口

int listen(int sockfd, int queue_length);

接收队列

一个新的Client的连接请求先被放在接收队列中,等待Server程序调用accept函数接受该连接请求

queue_length用于指定接收队列的长度,也就是在Server程序调用accept函数之前最大允许进入的连接请求数,多余的连接请求将被拒绝,典型取值为5

listen() 调用


4737528

1. socket()

2. bind(80)

3. listen()

80

Listen

queue

服务器初始化

Web 服务器

操作系统


Connect

客户端建立与服务器的连接

int connect (int sockfd, const struct sockaddr *servaddr, socklen_t addrlen)

servaddr是事先填写好的结构,用于指定所要连接的服务器的地址(Server的IP地址和端口号)。

connect() 调用


4737528

建立与服务器的连接

Web 服务器

1. socket()

客户

2. bind(80)

3. listen()

connect()

OS

80

Listen

queue

Request

from (IP, port)


4737528

服务器忙

Web 服务器

客户请求在listen队列中获取先进先出服务(排队)

操作系统

80

Listen

队列

客户1

客户2

客户3


Connect1

在阻塞模式下,当无法建立连接时connect的阻塞时间为20秒左右,可采用gethostbyaddr事先判断到服务主机的路径是否是通的,或者先ping一下对方主机的IP地址。

采用gethostbyaddr阻塞时间不管成功与否均为4秒左右。

采用Ping方式时间约2秒左右

connect


Accept

accept()函数用于响应客户的连接请求,建立与客户端的连接

产生一个新的socket描述符来描述该连接

这个连接用来与发起该连接请求的Client交换信息

int accept(int sockfd,struct sockaddr *addr,int addrlen);

addr将在函数调用后被填入连接对方(客户端)的地址信息,如对方的IP、端口等。

accept缺省是阻塞函数,如果队列中没有连接请求等待,那么就会使调用方阻塞,直到有一个连接请求到达。

accept()


Accept1

accept() 调用

Web 服务器

  • The top client in the queue get service (FIFO)

  • The accepted connection is on a new socket

  • The old socket continues to listen

已连接的

socket

accept()

操作系统

80

Listen

队列

客户 1

客户2

客户3


Accept2

include <string.h> #include <sys/types.h> #include <sys/socket.h> #define MYPORT 80 /* the port users will be connecting to */ #define BACKLOG 5 /* how many pending connections queue will

hold */ main() { int sockfd, new_fd; /* listen on sock_fd, new connection on new_fd */ struct sockaddr_in my_addr; /* my address information */ struct sockaddr_in their_addr; /* connector's address information */

int sin_size; sockfd = socket(AF_INET, SOCK_STREAM, 0);

/* do some error checking! */

accept()的使用


Accept3

my_addr.sin_family = AF_INET; /* host byte order */ my_addr.sin_port = htons(MYPORT); /* short, network byte order */ my_addr.sin_addr.s_addr = INADDR_ANY; /* auto-fill with my IP */ bzero(&(my_addr.sin_zero), 8); /* zero the rest of the struct */ /* did you remember your error checking? */ bind(sockfd, (struct sockaddr *)&my_addr, sizeof(struct sockaddr)); listen(sockfd, BACKLOG); sin_size = sizeof(struct sockaddr_in); new_fd = accept(sockfd, &their_addr, &sin_size);

printf(”对方地址: %s\n", inet_ntoa(their_addr.sin_addr));

……

accept()


4737528

服务器可工作在两种不同的方式:

循环方式(iterative mode)——在计算机中一次只运行一个服务器进程。当有多个客户进程请求服务时,服务器进程就按请求的先后顺序依次做出响应。

并发方式(concurrent)——在计算机中同时运行多个服务器进程,每一个服务器进程对某个特定的客户进程做出响应。

服务器的两种工作方式


4737528

使用无连接的 UDP 的服务器通常都工作在循环方式——一个服务器在同一时间只能向一个客户提供服务。

服务器收到客户的请求后,就发送 UDP 用户数据报响应该客户。但对其他客户发来的请求则暂时不予理睬,这些请求都在服务器端的队列中排队等候服务器的处理。

当服务器进程处理完毕一个请求时,就从队列中读取来自下一个客户的请求,然后继续处理。

无连接循环服务器


4737528

无连接循环服务器的特点

客户

服务器

一次一个

客户

临时端口

客户

熟知端口

UDP

临时端口

客户

UDP

UDP

临时端口

服务器只使用一个熟知端口。每一个客户则使用自己创建的临时端口(端口号自己设定)。

UDP


4737528

循环型服务员代码

int sockfd,newsockfd;

if ((sockfd=socket(…))<0)

err_sys(“socket error”);

if (bind(sockfd,…)<0)

err_sys(“bind error”);

if (listen(sockfd,5)<0)

err_sys(“listen error”);

for ( ; ; )

{

newsockfd=accept(sockfd,…);/* blocks */

if (newsockfd<0)

err_sys(“accept error”);

doit(newsockfd);/* process the request */

close(newsockfd);

}


4737528

服务器在同一时间可向多个客户提供服务。

TCP 是面向连接的,因此在服务器和多个客户之间必须建立多条 TCP 连接,每一条 TCP 连接要在其数据传送完毕后才能释放。

使用 TCP 的服务器只能有一个熟知端口,主服务器在熟知端口等待客户发出的请求。一旦收到客户的请求,就立即创建一个从属服务器,并指明从属服务器使用临时端口和该客户建立 TCP 连接,然后主服务器继续在原来的熟知端口等待向其他客户提供服务。

面向连接并发服务器


4737528

面向连接并发服务器的特点

创建

主服务器

客户

从属服务器

熟知端口仅用于

接受服务请求

临时端口

客户

临时端口

TCP

TCP 连接

客户

TCP

TCP

临时端口

主服务器有时又称为父服务器,而从属服务器又称为子服务器。

TCP


4737528

并发型服务员代码

int sockfd,newsockfd;

if ((sockfd=socket(…))<0)

err_sys(“socket error”);

if (bind(sockfd,…)<0)

err_sys(“bind error”);

if (listen(sockfd,5)<0)

err_sys(“listen error”);

for ( ; ; )

{newsockfd=accept(sockfd,…);/* blocks */

if (newsockfd<0)

err_sys(“accept error”);

if (fork()==0)/* child */

{close(sockfd);

doit(newsockfd);/* process the request */

exit(0); }

close(newsockfd);/* parent */

}


Client server connection oriented

Concurrent server

BIND

LISTEN

RECEIVE

SEND

SOCKET

CONNECT

ACCEPT

TCP three-way handshake

RECEIVE

SEND

CLOSE

Client+server: connection-oriented


4737528

发送和接收

  • 在完成了上述的初始化工作后,就可以开始传输数据了。

  • 面向连接的发送和接收 : send() and recv()

  • 无连接协议的发送和接收:sendto()和recvfrom()


4737528

recv

  • 用于TCP协议中接收信息

  • int recv(int sockfd, void *buf, int len, int flags);

  • Sockfd: socket descriptor

  • Buf:指向容纳接收信息的缓冲区的指针

  • Len:接收缓冲区的大小

  • Flags: integer, special options, usually just 0

  • 函数返回实际接收到的字节数,返回-1表示出错

  • recv缺省是阻塞函数,直到接收到信息或出错才返回


4737528

用于TCP协议中发送信息

int send(int sockfd, const void *msg, int len, int flags);

Msg: 指向待发送信息的指针

Len: 待发送的字节数

Flags:发送标志(其含义将在后面介绍)

函数返回已发送的字节数,返回-1表示出错

send缺省是阻塞函数,直到发送完毕或出错

注意:如果函数返回值与参数len不相等,则剩余的未发送信息需要再次发送

send


Sending receiving notes

send( ) might send only a fraction of the buffer

Need to push the rest

See sendall( ) in Beej’s guide

recv( ) might get several messages together, or a fraction of a message

Need to be able to identify message’s beginning and end

Sending/Receiving notes


Recvfrom

用于UDP协议中接收信息

int recvfrom(int sockfd,void *buf,int len,unsigned int flags struct sockaddr *from, int *fromlen);

Buf:指向容纳接收信息的缓冲区的指针

Len:缓冲区的大小

Flags:接收标志(其含义将在后面介绍)

From:指明接收数据的来源

Fromlen:地址长度

函数返回实际接收的字节数,返回-1表示出错

recvfrom是阻塞函数,直到接收到信息或出错

recvfrom


Sendto

用于UDP协议中发送信息

int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *to, int tolen);

Buf:指向容纳发送报文的缓冲区的指针

Len:缓冲区的大小

Flags:发送标志

To:指明发送数据的目的地

tolen :地址长度

函数返回已发送的字节数,返回-1表示出错

sendto缺省是阻塞函数,直到发送完毕或出错

注意:如果函数返回值与参数len不相等,则剩余的未发送信息需要再次发送

sendto


Send sendto recv recvfrom flags

Flags一般情况下设置为0。但可以选择下列设置

MSG_DONTROUTE

对send, sendto有效

表示不使用路由(一般不使用)

MSG_PEEK

对recv, recvfrom有效

窥探,读出网络数据后不清除已读的数据

MSG_OOB

对发送接收都有效

表示读/写带外数据(out-of-band data)

send, sendto, recv, recvfrom函数调用的参数:flags


Close

close

  • 关闭特定的socket连接

  • 调用函数:int close(int sockfd);

  • 关闭连接将中断对该socket的读写操作。

  • 关闭用于listen()函数的socket将禁止其他Client的连接请求


Shutdown

Shutdown()函数可以单方面的中断连接,即禁止某个方向的信息传递。

函数调用

int shutdown(int sockfd, int how);

参数how:

0 - 禁止接收信息

1 - 禁止发送信息

2 - 接收和发送都被禁止,与close()函数效果相同

返回0表示调用成功,返回-1表示出错

shutdown


Dealing with blocking calls

Many of the functions we saw block until a certain event,such as

accept: until a connection comes in

connect: until the connection is established

recv, recvfrom: until a packet (of data) is received

send, sendto: until data is pushed into socket’s buffer

happened

For simple programs, blocking is convenient

What about more complex programs?

multiple connections

simultaneous sends and receives

simultaneously doing non-networking processing

Dealing with blocking calls


Dealing with blocking cont d

Options:

create multi-process or multi-threaded code

turn off the blocking feature using the fcntl() file-descriptor control function

use the select() function call.

Dealing with blocking (cont’d)


Fcntl function call

Function:get or alter socket I/O attribution

int fcntl(int filedes, int cmd, args ...);

return status: -1 if error

filedes: argument is an open file descriptor

cmd: specifies the operation to be performed

arg: depends upon the value of cmd

Common attribution:

FIONREAD: 返回socket缓冲区中未读数据的字节数

FIONBIO: argp为零时为阻塞模式,非零时为非阻塞模式

SIOCATMARK :判断是否有未读的带外数据(仅用于TCP协议),返回true或false

e.g.

sockfd = socket(PF_INET, SOCK_STREAM, 0);

fcntl(sockfd, F_SETFL, O_NONBLOCK);

fcntl() function call


Using select

What does select do?

can be permanent blocking, time-limited blocking or non-blocking

input: a set of file-descriptors

output: info on the file-descriptors’ status

i.e., can identify sockets that are “read-ready”,or “write-ready”,or “an exception is registered”

calls involving that socket will return immediately

Using select()


Select function call

int status = select(nfds, &readfds, &writefds, &exceptfds, &timeout);

return status: # of ready objects, -1 if error

nfds: 1 + largest file descriptor to check

readfds: set of descriptors to check if read-ready

writefds: set of descriptors to check if write-ready

exceptfds: set of descriptors to check if an exception is registered

timeout: time after which select returns, even if nothing ready - can be 0 or 

select() function call


To be used with select

For sets, select uses a structure, struct fd_set

it is just a bit-vector

if bit i is set in [readfds, writefds, exceptfds], select will check if file descriptor (i.e. socket) i is ready for [reading, writing, exception]

Before calling select:

FD_ZERO(&fdvar): clears the structure

FD_SET(i, &fdvar): add i to fd-set, to check file desc. i

After calling select:

int FD_ISSET(i, &fdvar): boolean returns TRUE iff i is “ready to read”

Standard input (STDIN) is file descriptor 0

To be used with select():


4737528

fd_set结构

  • #define FD_SETSIZE 64

  • typedef struct fd_set {

  • u_int fd_count; /* how many are SET ? */

  • SOCKET fd_array[FD_SETSIZE]; /* an array of SOCKETs */

  • } fd_set;

  • 参数:

    • fd_count:已设定socket的数量

    • fd_array:socket列表,FD_SETSIZE为最大socket数量,建议不小于64(微软建议)。


Timeout parameter

struct timeval {

int tv_sec; // seconds

int tv_usec; // microseconds

};

Set timeout to NULL for no timeout

Set both to 0 for immediate timeout

Timeout parameter


4737528

Select()返回时,如果有下列事件发生,对应的套接字将被标识

对于readfds,主要有以下事件:

数据可读

连接已经关闭、重启或中断

listen已经调用,并且有一个连接未决,accept函数将成功

对于writefds,主要有以下事件:

数据能够发送

如果一个非阻塞连接调用正在被处理,连接已经成功

对于exceptfds,主要有以下事件:

如果一个非阻塞连接调用正在被处理,连接企图失败

OOB( Out Of Band )数据可读。 OOB数据,是指一些特殊的信息,比如错误信息, 传输控制信号等 。

关于未决I/O操作


Select

fd_set rset;/* 定义可读的socket集合 */

FD_ZERO(& rset);/* 清空rset集合 */

FD_SET(sockfd, rset);/* 将sockfd 加入rset集合 */

nready = select(maxfd+1, &rset, null, null, null);

/* maxfd是已知的最大的socket */

/* 只考虑数据可读事件 */

if (FD_ISSET(sockfd, &rset)) {

……

}

Select应用举例


4737528

Socket辅助函数


Address argument

Address Argument’s syntax

202.115.64.3

www.swjtu.edu.cn

www.swjtu.edu.cn smtp

www.swjtu.edu.cn:25

But 32-bit IP address must be used in network packet

Converted into 32-bit IP address

Library routine

inet_addr: Converted into 32-bit IP address

Gethostbyname: converts host’s name to structure containing long integer

Address argument(地址变量)


Inet addr

unsigned longinet_addr(const char FAR *cp)

Argument (输入)

ASCII string: dotted decimal address

e.g. 202.115.64.3

Return (输出)

IP address in binary

该函数将一个点分十进制IP地址字符串转换成32位数字表示的IP地址。

inet_addr


4737528

IP地址格式转换

下列两个函数用于在Internet的点分十进制IP地址与32比特IP地址之间进行转换。

#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

unsigned long inet_addr (const char *cp);

inet_addr将一个点分十进制IP地址字符串转换成32位数字表示的IP地址

举例:

inet_addr("192.1.8.84")=1409810880

inet_addr("127.0.0.1")= 16777343

如果发生错误,函数返回INADDR_NONE值。

其它系统调用


4737528

char* inet_ntoa (struct in_addr in);

inet_ntoa将一个32位数字表示的IP地址转换成点分十进制IP地址字符串

举例:

char * ipaddr=NULL;

char addr[20];

in_addr inaddr;

inaddr. s_addr=16777343;

ipaddr= inet_ntoa(inaddr);

strcpy(addr,ipaddr);

这样addr的值就变为127.0.0.1

其他基本的系统调用


Gethostbyname

function: converts host’s name to structure containing long integer

Argument (输入)

ASCII string: domain name

e.g. www.swjtu.edu.cn

Return (输出)

address of a hostent structure (a pointer to hostent structure)

0 if name cannot mapped into IP address

gethostbyname


Hostent in netdb h

struct hostent {

char *h_name; /*official host name */

char **h_aliases; /*other aliases */

int h_addrtype; /*address type */

int h_length; /* address length */

char **h_addr_list; /* list of addresses */

/*如果该主机有多个网卡,就包括地址的列表*/

};

#define h_addr h_addr_list[0]

hostent: in netdb.h


Example

struct hostent *hptr;

char *name = “www.swjtu.edu.cn”

If (hptr = gethostbyname(name)){

/* IP address is now in hptr->h_addr_list */

} else {

/*error in name – handle it */

}

Example


Getting well known port

www.swjtu.edu.cn smtp

convert smtp to port

getservbyname (service,protocol)

converts service and protocol’s name to structure servent containing long integer

Argument (输入)

String: desired service

String: protocol being used

Return (输出)

A pointer to structure servent

Getting well-known port


Servent in netdb h

struct servent {

char *s_name; /*official service name */

char **s_aliases; /*other aliases */

int s_port; /*port for this service */

char **s_proto; /* protocol to use */

};

servent : in netdb.h


Example1

struct servent *sptr;

If (sptr = getservbyname(“smtp”, “tcp”)){

/* port number is now in sptr->s_port */

} else {

/*error occurred – handle it */

}

Example


Getting protocol number

Map protocol name to integer constant assigned to it

getprotobyname

Argument

String: protocol name

Return

address of structure protoent

0 if specified name does not exist

Getting protocol number


Protoent in netdb h

struct protoent {

char *p_name; /*official protocol name */

char **s_aliases; /*list of aliases allowed*/

int s_proto; /*official protocol number */

};

protoent : in netdb.h


Example2

struct protoent *pptr;

If (pptr = getprotobyname(“udp”)){

/* official protocol number is now in pptr->p_proto */

} else {

/*error occurred – handle it */

}

Example


4737528

你是谁: getpeername()

如果想知道是谁正在和你连接,可使用系统调用getpeername,该系统调用返回和给定管套相连的对方对等层地址和端口号。------- #include <sys/types.h>

#include <sys/socket.h> int getpeername(int sockfd, struct sockaddr *peer, int *addrlen); ------- 其中,参数peer是一个指向‘struct sockaddr’或者‘struct sockaddr_in’的指针。如果执行成功,就得到了对方的地址和端口号,然后用inet_ntoa()和gethostbyaddr()来得到对方更多的信息。

其它系统调用


4737528

我是谁: getsockname

该系统调用返回和一个socket相关联的本地名字

-------

#include <sys/types.h>

#include<sys/socket.h> int getsockname(int sockfd, struct sockaddr *addr, int *addrlen);

------- addr用来存放返回的本地地址;addrlen存放的是返回的地址的长度。

其它系统调用


4737528

系统调用gethostname() :gets the name of the current host

- ------ #include <unistd.h> int gethostname(char *hostname, size_t size); - ------ hostname是一个存放主机名字(域名)的字符数组返回的hostname可以作为gethostbyname()的参数,调用该系统调用,这样又可以得到自己的IP地址了。

其它系统调用


4737528

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字节相应的主机字节序表示的整数

字节序转换函数


4737528

字节操作

Bcopy(char *src,char *dest,int nbytes); 字符串拷贝

Bzero(char *dest,int nbytes);

把指定数量的空字节写入指定的目的地址。

Int bcmp(char *ptr1,char *ptr2,int nbytes);

对任意两个字符串进行比较。

返回值(与标准I/O函数strcmp的返回值不同):

0----相同

非零----不相同。

其它系统调用


4737528

为了灵活使用套接字,我们可以对它的属性进行设定。

有关socket属性的系统调用

#include <sys/types.h>

#include <sys/socket.h>

int setsockopt(int sockfd,int level,int optname,char *optval,int optlen);

int getsockopt(int sockfd,int level,int optname,char *optval,int optlen);

其中sockfd变元是一个套接字描述符,level为套接字选项的级别,指定由系统中哪一个程序(通用管套程序、TCP/IP程序、还是XNS程序等)来解释选择项,有些选项因协议而异,有些则对所有套接字都适用。

Optname是属性的名字,optval是一个取或存放属性的指针(缓冲区),optlen指定缓冲区的长度。

其它系统调用


4737528

例如:optlen=sizeof(maxseg);

If(getsockopt(sockfd,IPPROTO_TCP,TCP_MAXSEG,(char *)&maxseg,&optlen)<0)

err_sys(“TCP_MAXSEG getsockopt error”);

printf(“TCP maxseg=%d\n”,maxseg);

sendbuff=16384;

if(setsockopt(sockfd,SOL_SOCKET,SO_SNDBUF,(char *)&maxseg,sizeof(sendbuff))<0)

err_sys(“SO_SNDBUF setsockopt error”);

下表为getsockopt和setsockopt的属性

其他基本的系统调用


4737528

名字

get

set

描述

数据类型

IPPROTO_IP

IP_OPTIONS

IP报头中的选择项

IPPROTO_TCP

TCP_MAXSEG

TCP_NODELAY

获取TCP最大段长度

不推迟发送联合分组

int

int

NSPROTO_SPP

SO_HEADERS_ON_INPUT

SO_HEADERS_ON_OUTPUT

SP_DEFAULT_HEADERS

SO_LAST_HEADER

SO_MTU

在输入时传送SPP报头

在输出时传送SPP报头

为输出包设置缺省SPP报头

取出上一个SPP报头

SPP MTU值

Int

Int

S_sphdr

S_sphdr

U_shout

NSPROTO_PE

SO_SEQNO

产生与众不同的PEXID

Long

NSPROTO_RAW

SO_HEADERS_ON_INPUT

SO_HEADERS_ON_OUTPUT

SP_DEFAULT_HEADERS

SO_ALL_PACKETS

SO_NSIP_ROUTE

在输入时传送IDP报头

在输出时传送IDP报头

为输出包设置缺省IDP报头

把所有的IDP包送给用户

为XNS指定IP路由

Int

Int

S_idp

Int

S_nsip_rep

SOL_SOCKET

SO_BROADCAST

SO_DEBUG

SO_DONTROUTE

SO_ERROR

SO_KEEPACTIVE

SO_LENGER

SO_OOBINLINE

SO_RCVBUF

SO_SNDBUF

SO_RCVLOWAT

SO_SNDLOWAT

SO_RCVTIMEO

SO_SNDTIMEO

SO_REUSEADDR

SO_TYPE

SO_USELOOPBACK

允许发送广播报文

查错信息记录

只用接口地址

取出出错状态并清除

保持连接

如有数据,延迟关闭

将收到的加急数据存入队列

接收缓存长度

发送缓存长度

接收低水标志

发送低水标志

接收超时

发送超时

允许重新使用本地地址

获取管套类型

可能时旁路硬件

Int

Int

Int

Int

Int

S_linger

Int

Int

Int

Int

Int

Int

Int

int


4737528

最重要的属性

Level

optname

说明

SOL_SOCKET

SO_BROADCAST

允许广播

SO_SNDBUF

发送缓冲区大小

SO_TYPE

获得套接字类型

IPPROTO_IP

IP_OPTIONS

IP头部选项

IP_TTL

IP头部TTL字段

IPPROTO_TCP

TCP_MAXSEG

TCP_NODELAY

TCP_MAXRT

TCP最大报文尺度非延迟

TCP最大重发时间


4737528

//允许调试输出#define SO_DEBUG 0x0001 /* turn on debugging info recording */

//是否监听模式#define SO_ACCEPTCONN 0x0002 /* socket has had listen() */

//套接字与其他套接字的地址绑定#define SO_REUSEADDR 0x0004 /* allow local address reuse */

//保持连接#define SO_KEEPALIVE 0x0008 /* keep connections alive */

//不要路由出去#define SO_DONTROUTE 0x0010 /* just use interface addresses */

//设置为广播#define SO_BROADCAST 0x0020 /* permit sending of broadcast msgs */

//使用环回不通过硬件#define SO_USELOOPBACK 0x0040 /* bypass hardware when possible */

//当前TIME_WAIT值#define SO_LINGER 0x0080 /* linger on close if data present */

套接字的属性


Setsockopt 1

Nagle算法(TCP_NODELAY选项)

Nagle算法通过将未确认的数据存入缓冲区直到蓄足一个包一起发送的方法,以减少主机发送零碎小数据包的数目。但对于某些应用来说,这种算法将降低系统性能(时延大)。可使用TCP_NODELAY选项来将此算法关闭。

禁用Nagle算法:

int bNodelay = 1;

setsockopt( socket, IPPROTO_TCP, TCP_NODELAY,

(char *)&bNodelay, sizoeof(bNodelay));

Setsockopt应用实例1


4737528

Socket通信流程


Tcp client server

socket()

bind ()

listen()

socket()

accept()

connect()

send() /recv()

send() /recv()

close()

close()

TCP Client-Server

TCP Server

将一个本地地址关联到一个套接字上

创建一个新的socket

创建一个新的socket

实时侦听客户连接;给出队列大小

阻塞,直到有客户企图连接上来

TCP Client

主动尝试建立一个连接

发送/接收数据

connection establishment

3-way handshake

发送/接收数据

blocks until

connection from client

释放连接

释放连接

end-of-file notification


Udp client server

UDP Client-Server

UDP Server

socket()

UDP Client

bind()

socket()

recvfrom()

blocks

data (request)

sendto()

process request

data (reply)

sendto()

recvfrom()

close()

close()


A tcp daytime client becomes an sctp daytime client

下面的例子给出最常用的客户端socket编程框架;

同时说明socket除了支持常用的IP/TCP等协议外,还支持SCTP等协议.

sockfd = socket(AF_INET, SOCK_STREAM, IP_PROTO_SCTP); /* SCTP socket */

A TCP daytime client becomes an SCTP daytime client...

Note: 0 implies IP_PROTO_TCP

int sockfd, n;

char recvline[MAXLINE + 1]; /* read buffer*/

struct sockaddr_in servaddr;

sockfd = socket(AF_INET, SOCK_STREAM, 0); /* create TCP socket */

/* fill in socket address structure */

servaddr.sin_family = AF_INET; servaddr.sin_port = htons(13);

inet_pton(AF_INET, argv[1], &servaddr.sin_addr); /*dot dec to n.b.o.*/

connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr));

while ( (n = recv(sockfd, recvline, MAXLINE,0)) > 0 )

{

recvline[n]=0; /* null terminate */

fputs(recvline, stdout);

}

close (sockfd);


Socket9

见linux_socket编程实例讲稿

SOCKET编程实例


Example2 echo client server

Example2: Echo Client-Server


Include s

#include <stdio.h> /* for printf() and fprintf() */

#include <sys/socket.h> /* for socket(), connect(),

sendto(), and recvfrom() */

#include <arpa/inet.h> /* for sockaddr_in and

inet_addr() */

#include <stdlib.h> /* for atoi() and exit() */

#include <string.h> /* for memset() */

#include <unistd.h> /* for close() */

#include <netdb.h>/* Transform the ip address

string to real uint_32 */

#define ECHOMAX 255 /* Longest string to echo */

#include’s


Echoclient cpp variable declarations

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

{

int sock; /* Socket descriptor */

struct sockaddr_in echoServAddr; /* Echo server address */

struct sockaddr_in fromAddr; /* Source address of echo */

unsigned short echoServPort =2000; /* Echo server port */

unsigned int fromSize; /* address size for recvfrom() */

char *servIP=“172.24.23.4”; /* IP address of server */

char *echoString=“I hope this works”; /* String to send to echo server */

char echoBuffer[ECHOMAX+1]; /* Buffer for receiving echoed string */

int echoStringLen; /* Length of string to echo */

int respStringLen; /* Length of received response */

EchoClient.cpp -variable declarations


Echoclient c creating the socket

/* Create a datagram/UDP socket and error check */

sock = socket(AF_INET, SOCK_DGRAM, 0);

if(sock <= 0){

printf("Socket open error\n");

exit(1);

}

EchoClient.c - creating the socket


4737528

/* Construct the server address structure */

memset(&echoServAddr, 0, sizeof(echoServAddr));/* Zero out structure */

echoServAddr.sin_family = AF_INET; /* Internet addr family */

inet_pton(AF_INET, servIP, &echoServAddr.sin_addr); /* Server IP address */

echoServAddr.sin_port = htons(echoServPort); /* Server port */

/* Send the string to the server */

echoStringLen = strlen(echoString);

sendto(sock, echoString, echoStringLen, 0, (struct sockaddr *) &echoServAddr, sizeof(echoServAddr);

EchoClient.cpp – sending


Echoclient cpp receiving and printing

/* Recv a response */

fromSize = sizeof(fromAddr);

recvfrom(sock, echoBuffer, ECHOMAX, 0, (struct sockaddr *) &fromAddr, &fromSize);

/* Error checks like packet is received from the same server*/

/* null-terminate the received data */

echoBuffer[echoStringLen] = '\0';

printf("Received: %s\n", echoBuffer); /* Print the echoed arg */

close(sock);

exit(0);

} /* end of main () */

EchoClient.cpp – receiving and printing


Echoserver cpp creating socket

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

{

int sock; /* Socket */

struct sockaddr_in echoServAddr;/* Local address */

struct sockaddr_in echoClntAddr;/* Client address */

unsigned int cliAddrLen; /* Length of incoming message */

char echoBuffer[ECHOMAX]; /* Buffer for echo string */

unsigned short echoServPort =2000;/* Server port */

int recvMsgSize; /* Size of received message */

/* Create socket for sending/receiving datagrams */

sock = socket(AF_INET, SOCK_DGRAM, 0);

if(sock <= 0){

printf("Socket open error\n");

exit(1);

}

EchoServer.cpp – creating socket


4737528

/* Construct local address structure*/

memset(&echoServAddr, 0, sizeof(echoServAddr)); /* Zero out structure */

echoServAddr.sin_family = AF_INET; /* Internet address family */

echoServAddr.sin_addr.s_addr =htonl(INADDR_ANY);

echoServAddr.sin_port = htons((uint16_t) echoServPort); /* Local port */

/* Bind to the local address */

int error_test = bind(sock, (struct sockaddr *) &echoServAddr, sizeof(echoServAddr));

if(error_test < 0){

printf("Binding error\n");

exit(1);

}

EchoServer.cpp – binding


4737528

for (;;) /* Run forever */

{

cliAddrLen = sizeof(echoClntAddr);

/* Block until receive message from a client */

recvMsgSize = recvfrom(sock, echoBuffer, ECHOMAX, 0,

(struct sockaddr *) &echoClntAddr, &cliAddrLen);

printf("Handling client %s\n", inet_ntoa(echoClntAddr.sin_addr));

/* Send received datagram back to the client */

sendto(sock, echoBuffer, recvMsgSize, 0,

(struct sockaddr *) &echoClntAddr, sizeof(echoClntAddr);

}

} /* end of main () */

Error handling is must

EchoServer.cpp – receiving and echoing


4737528

了解常用程序设计语言(VB,VC,C++,Delphi等)提供了那些SOCKET通信编程接口。

思考题


Delphi internet

根据TCP/IP协议,在传输层有两种应用协议:一种是面向连接的TCP协议,另一种是无连接的UDP协议。

与这两种协议相对应,在Delphi的组件板上有三个基本的网络元件,它们是基于TCP套接字Winsock连接的TClientSocket、TServerSocket和基于UDP连接的NMUDP控件。

用于数据传送的基本控件是位于Internet页上的TClientSocket和TServerSocket,主要的属性包括address,host,port等,主要的事件包括onRead,onWrite,onConnected,onAccept等。

NMFTP,NMHTTP,NMPOP3和NMSMTP等,可以方便地完成文件传送、Web和Mail等标准服务。

Delphi基本Internet编程控件


4737528

设计一个基于某一种I/O方法的socket (winsock)通信程序,要求具备下列基本功能:

系统配置界面:能通过界面配置通信双方的IP地址、端口号

通过界面输入待发送的数据,按“确定”键后发送

通过界面显示收到的数据

设置一个退出按钮,按下该按钮后退出程序的运行

课程设计2


Thank you

Thank You!

Thanks!


  • Login