Linux应用程序之socket网络编程解决思路

Linux应用程序之socket网络编程
网络编程—服务器/客户机                                             
1、 计算机网络体系结构模式
所有的网络通信方式分为两种:线路交换和包交换;线路交换是在数据传输时,在发送
端和接受端建立一条特定的线路连接,数据就在这条线路上传输,电话就是采用这种方式。
计算机网络则采用的是包交换的方式,数据的发送端将要发送的数据分为块,每个数据块经过处理形成一个数据包,其中包括有接受端的地址等信息,每个包都是单独传输。
2、 服务器端软件设计算法
⑴面向连接的服务器算法
① 服务器接受客户端的连接请求;
② 通过这个连接进行相关的通信;
③ 服务器在完成交互后关闭请求。
面向连接的设计方法要求每个连接都要创建一个套接口(socket),这样当请求客户过多
的时候,服务器可能因为资源耗尽而停止运行。
⑵无连接的服务器算法
无连接的设计方法中,一个套接口可以与多个主机通信,不会出现资源耗尽的问题。但是它的可靠性相对而言较差。
⑶迭代服务器的算法
在一个时刻只能处理一个客户请求的服务器叫做迭代服务器
① 创建一个套接口,并将它绑定在众所周知的服务器端口上;
② 从套接口上取下一个连接请求,并获得该请求的套接字
③ 重复读取请求队列中的连接请求,构造一个响应,按照响应的协议向客户发回响应
④ 当与特定的客户完成交互之后关闭连接,并接受新的连接请求。
⑷并发服务器的算法
并发服务器就是就是同一时刻可以处理多个客户请求的服务器。大多数的并发服务器使
用多进程来实现并发性,一个主进程最先开始运行,使用套接口在众所周知的端口监听连接
请求,并为每一个请求创建一个服务器进程,由一个从进程来处理一个客服的通信。
① 创建UDP套接口并与众所周知的端口绑定;
② 调用recefrom()来接受客户端的请求,并创建一个新的进程来处理响应;


3、 socket基础
socket接口是TCP/IP网路的API,socket接口定义了许多函数或者例程,程序员可以
用它们来开发网络应用程序。网络socket数据传输是一种特殊的I/O,socket也是一种文件
描述符。Socket也具有一个类似打开文件的函数调用socket(),该函数返回一个socket
文件描述符句柄。随后连接的建立,数据的传输等操作都是通过该句柄来实现的。
⑴套接字的基本类型
① 数据流套接字(SOCK_STREAM):数据流套接字是一种面向连接的套接字,采用的面
向连接的TCP服务应用。Eg telnet 、www浏览器使用的Http协议
② 数据报套接字(DGRAM):数据报套接字是一种无连接的套接字,对应于无连接的UDP
服务应用。
4、socket网络编程
Linux采用了socket套接字,与套接字相关的函数包含在头文件sys/socket.h中。
Socket相当网络上的通信节点,即IP和端口号组成。
网络程序的设计可以采用TCP和UDP两种协议,TCP是一种可靠的、面向连接的协
议,而UDP是一种不可靠的无连接的协议。 
4.1采用TCP协议进行网络程序设计
⑴在进行网络程序设计的时候,一般按照客户端和服务器端分别进行设计,其设计流程
是不一样的,下图为TCP网络程序设计的设计流程。
   
Linux应用程序之socket网络编程解决思路
 
图4-1 TCP程序设计流程
⑵ socket相关的函数
①socket()函数
表头文件 #include<sys/socket.h> 
定义函数: int socket(int family,int type,intprotocol);
函数说明: socket() 函数用来生成一个套接口描述字,也称为套接字,指定协议族和套接口。参数: family 指定协议族(在网络程序中只能设置为:AF_INET), type 指明套接字的类型(SOCK_STREAM为数据流套接字,采用TCP建立连接,而SOCK_DGRAM数据报套接字,采用的是UDP协议),而 protocol 一般为 0
③ bind()函数
表头文件 #include<sys/types.h>  #include<sys/socket.h>
定义函数 int bind(int sockfd, (struct sockaddr *) my_addr, int addrlen);
函数说明 bind() 用于将本机的地址与创建的套接字绑定。其中sockfd为套接字号,
my_addr为指向地址结构的指针,addrlen为地址结构sockaddr_in的长度。成功返回0,失
败返回-1,把错误原因放在在errno中。
与套接字绑定的地址因为协议族的不同(family)而不同,在网络程序中因为family只能取AF_INET,故sockaddr的结构定义为:
struct sockaddr_in{
sa_family_t sin_family;
unsigned short int sin_port;/*端口号*/
struct in_addr sin_addr;    /*IP地址*/
}
struct in_addr
{
uint32_t s_addr;
};
由于IP地址为xx.xx.xx.xx的字符形式地址结构体中的IP地址为无符号整型,因此需要
采用下面的函数进行转换:
unsigned long inet_addr(const char * string);
而端口号的获取需要使用下面的两个函数,因为一般intel架构的处理器采用的是小端格式,而motorola和sun公司的机器采用的是大端格式,为了消除这个差别,必须采用下面的两个函数设置端口号;
unsigned long htonl(unsigned long  hostlong);
unsigned long htons(unsigned long  hostshort);
eg  struct sockaddr_in adr_srvr;
adr_srvr.sin_addr.s_addr=inet_addr(“192.168.1.105”);
    adr_srvr.sin_port=htons(8000);
④ listen()函数
头文件:#include<sys/socket.h>
函数原型:int listen(int sockfd, int backlog);
在套接字创建之后,它是一个主动连接的套接字,也就是此时系统假设用户会对这个套
接字调用connect函数,期待它主动与其它进程连接,然而在服务器编程中,用户希望这
个套接字可以接受外来的连接请求,也就是被动等待用户来连接。因此需要通过listen()
函数来使主动套接口变为被动套接口,从而使得一个进程可以接受其它进程的连接请求变为
服务器进程; 参数backlog用来指定最大的连接数,一般设定为5;
⑤accept()函数
头文件:#include<sys/socket.h>
函数原型:int accept(int sockfd, struct sockaddr_in *addr, int addrlen);
当服务器端接受到客户端的连接请求的时候,会把连接请求方在连接队列中,接着用
accept()函数处理并接受队列中的请求。accept默认会阻塞进程,直到有一个客户连接
建立后返回,它返回的是一个新可用的套接字,这个套接字是连接套接字。"阻塞"是一个术
语,它使程序运行暂时"停留"在这个地方,直到一个会话产生,然后程序继续;
通常"阻塞"是由循环产生的。此时我们需要区分两种套接字,一种套接字正如
accept的参数sockfd,它是监听套接字,在调用listen函数之后,一个套接字
会从主动连接的套接字变身为一个监听套接字;而accept返回是一个连接套接字,
它代表着一个网络已经存在的点点连接。
参数sockfd:
参数sockfd就是上面解释中的监听套接字,这个套接字用来监听一个端口,当有一个
客户与服务器连接时,它使用这个一个端口号,而此时这个端口号正与这个套接字关联。
当然客户不知道套接字这些细节,它只知道一个地址和一个端口号。
参数addr:
这是一个结果参数,它用来接受一个返回值,这返回值指定客户端的地址,当然这个地
址是通过某个地址结构来描述的,用户应该知道这一个什么样的地址结构。如果对客户的地
址不感兴趣,那么可以把这个值设置为NULL。
参数len:
如同大家所认为的,它也是结果的参数,用来接受上述addr的结构的大小的,它指明
addr结构所占有的字节个数。同样的,它也可以被设置为NULL。如果accept成功返回,
则服务器与客户已经正确建立连接了,此时服务器通过accept返回的套接字来完成与客户
的通信。

 
⑥connect()函数
头文件:#include<sys/socket.h>
函数原型:int connect(int sockfd,struct sockaddr *addr,int addrlen);
  connect函数是客户端使用的函数,当用创建一个套接口后,可以调用connect为这
个套接字指明远程端的地址;如果是字节流套接口,connect就使用三次握手建立一个连
接;如果是数据报套接口,connect仅指明远程端地址,而不向它发送任何数据。第一个
参数是socket函数返回的套接口描述字(套接字);第二和第三个参数分别是一个指向服
务器端套接口地址结构的指针和该结构的大小。
这些地址结构的名字均已“sockaddr_”开头,并以对应每个协议族的唯一后缀结束。
以IPv4套接口地址结构为例,它以“sockaddr_in”命名,定义在头文件<netinet/in.h>;
以下是结构体的内容:
struct in_addr { 
in_addr_t s_addr;/* IPv4地址 */
};
struct sockaddr_in { 
uint8_t sin_len; /* 无符号的8位整数 */
sa_family_t sin_family;
/* 套接口地址结构的地址簇,这里为AF_INET */
in_port_t sin_port; /* TCP或UDP端口 */
struct in_addr  sin_addr; 
char sin_zero[8]; 
};
⑦ send()函数
头文件:#include<sys/socket.h>
函数原型:int send(int sockfd, const char *buf,  int len,  int flags);