操作系统 | 网络编程

网络编程

1. 网络通信结构

1.1 Socket网络通信与IPC机制

  • IPC

消息队列、共享内存、IPC信号量

结构麻烦、不易使用、仅用于同机进程间通信

  • Socket网络通信

标准、规范,基于TCP/IP协议

两种模式:TCP通信(与管道类似)、UDP通信(与消息队列类似)

易于理解、使用,使用广泛

既可用于单机进程间通信,也可用于不同计算机进程间通信

1.2 客户服务器模型

  • 与网络通信一般采取客户服务器模型

在这里插入图片描述

1.3 网络通信结构

  • 分层设计
  • 各层分工明确
  • 可靠性好,性能高

在这里插入图片描述

1.4 套接字编程模型

结构:网卡、TCP协议、套接字(Socket)

类比:网卡(单位门牌号)、TCP/IP协议(收发室)、套接字(信箱号)

套接字:含有进程接收信息的完整地址(Socket地址:IP地址、端口号)

在这里插入图片描述

1.5 因特网连接(TCP连接)

TCP连接:比喻连接通信双方套接字的一条通信线路,通信前建立,通信结束拆除

一条TCP连接实际上就是一个文件描述

可用read/write或send/recv进行数据收发

地址:(cliaddr:cliport, servaddr:servport)
在这里插入图片描述

1.6 因特网连接实例

服务器端口号:规定为80

客户端端口号:随机分配,12345

在这里插入图片描述

2. 套接字地址设置

2.1 地址结构

/* Intenet-style socket address structure */

struct sockaddr_in

{

  short sin_family; //指定地址家族即地址格式

  unsigned short sin_port; //端口号码

  struct in_addr sin_addr; //IP地址

  char sin_zero[8]; //需要指定为0

};

在这个结构中,成员sin_family指定使用该套接字地址的地址家族。在这里必须设置为AF_INET,表示程序所使用的地址家族是TCP/IP。

注意:该结构的最后一个成员并未实际使用,主要是为了与第一个版本的套接字地址结构大小相同而设置。在实际使用时,将这8个字节直接设为0即可。

struct sin_addr {

  unsigned int s_addr; /*network byte order (big-endian)  */

}; 

2.1.1 字节序

整数:unsigned int B[2]={0x12345678,0xabcdef},

void A=(void) B;

unsigned int *K=(int *)malloc(sizeof(int));

*K=0x12345678;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BK8EzXP9-1590747953341)(F:\markdown\网络编程\1590714503(1)].jpg)

2.1.2 主机序、网络序转换

/*host to network long*/
unsigned long int htonl(unsigned long int hostlong);

/*主机字节序转化为网络字节序*/
unsigned short int htons(unsigned short int hostshort);

返回:按照网络字节顺序的值。

unsigned long int ntohl(unsigned long int netlong);

unsigned short int ntohs(unsiged short int netshort); 

h:host

n:network

l:long, int,4字节

s: short, 2字节

#include "wrapper.h"
int main(){
	unsigned int m,n; unsigned short k;	char *a,*b,*c;
	m=0x12345678; n=htonl(0x12345678); 
	a=(char*)&m;  b=(char*)&n; c=(char*)&k;
            c[0]='1';c[1]='2';
			printf("a[]=%2x%2x%2x%2x\n",a[0],a[1],a[2],a[3]);
			printf("b[]=%2x%2x%2x%2x\n",*b,*(b+1),*(b+2),*(b+3));
	printf("k=%x\n",k);
}
/*主机采用小端模式分析*/

2.2 IP地址

  • 32位整数(大端模式,网络序):
struct in_addr IP1=htonl(0x8002c2f2);

80  02  c2  f2

128=0x802=0x02193=0xc2242=0xf2
  • 点分十进制:
128.2.193.242
  • 转换函数:
int inet_aton(const char *cp, struct in_addr *inp);

char *inet_ntoa(struct in_addr in);

/*a: 字符串  n:32位整数*/

2.3 因特网域名

/* DNS host ent ry structure ,位于系统头文件netdb.h */
struct  hostent  {
   char  *h_name;      /* Official domain name of host  */
   char  **h_aliases;    /* Null-terminated array of domain name */
   int   h_addrtype;     /* Host address type(AF_INET  */
   int   h_length;       /* Length of an address, in bytes  */
   char  **h_addr_list;   /* Null-terminated array of in_addr structs */
};

在这里插入图片描述

  • 查询域名和IP地址
  1. 命令
nslookup www.baidu.com
  1. API函数
struct hostent *gethostbyname(const  char  *name);
struct hostent *gethostbyaddr(const char *addr, int len, int type);
  1. c文件(hostinfo.c)
#include "wrapper.h"
int main(int argc, char **argv) 
{
    char **pp;
    struct in_addr addr;
    struct hostent *hostp;
     if (argc != 2) {}
     if (inet_aton(argv[1], &addr) != 0) 
        hostp = Gethostbyaddr((const char *)&addr, sizeof(addr), AF_INET); 
    else                                
        hostp = Gethostbyname(argv[1]); 
    printf("official hostname: %s\n", hostp->h_name);
    for (pp = hostp->h_aliases; *pp != NULL; pp++)
	    printf("alias: %s\n", *pp);
 
    for (pp = hostp->h_addr_list; *pp != NULL; pp++) {
	    addr.s_addr = *((unsigned int *)*pp);
	    printf("address: %s\n", inet_ntoa(addr));
    }
    exit(0);
}
$ ./hostinfo  219.222.191.131
official hostname: sw.dgut.edu.cn
address: 219.222.191.131
$ ./hostinfo  www.dgut.edu.cn
official hostname: www.dgut.edu.cn
address: 219.222.191.1
address: 113.105.128.128

3.网络通信API函数

3.1 编程框架

在这里插入图片描述

3.2 网络通信API函数

3.2.1 客户端

  1. 创建套接字
int socket(int af, int type , int protocol);
int af;
//指定套接字所使用的地址格式,设置为AF_INET    
int type;
//套接字类型, SOCK_STREAM 表示采用TCP 协议通信,采用UDP 协议通信,则为SOCK_DGRAM
int protocol ;
//如果参数type已经指定套接字类型为TCP或UDP,则该参数可以设置为0,e.g:
client_sock = socket(AF_INET , SOCK_STREAM, 0);
  1. connect函数
int connect (
int client_sock,
//套接字句柄
  struct sockaddr *serv_addr,
//将要连接的服务器地址信息结构指针
  int addrlen
//地址信息结构体长度
);
  1. 包装函数open_client_sock
int open_client_sock(char *hostname, int port) 
{    
    int client_sock;     struct hostent *hp;
    struct sockaddr_in serveraddr;
    if ((client_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	    return -1;        /* if error return -1 */
     /* Fill in the server's IP address and port */
    if ((hp = gethostbyname(hostname)) == NULL)
	    return -2;       /* if error return -2  */
    bzero((char *) &serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET;
    bcopy((char *) hp->h_addr_list[0],
	  (char *)&serveraddr.sin_addr.s_addr, hp->h_length);
    serveraddr.sin_port = htons(port); 
    /* Establish a connection with the server */
    if (connect(client_sock, (SA *) &serveraddr, sizeof(serveraddr)) < 0)
	    return -1;
    return client_sock;
}

3.2.2 服务器端

int bind(int serv_sock, struct sockaddr *my_addr , int addrlen);
int listen(int serv_sock, int backlog);
  • open_listen_sock包装函数:
int open_listen_sock(int port) 
{    int listen_sock, optval=1;
    struct sockaddr_in serveraddr;
     /* Create a socket descriptor */
    if ((listen_sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	    return -1; 
    /* Eliminates "Address already in use" error from bind */
    if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, 
		   (const void *)&optval , sizeof(int)) < 0)
	    return -1;
 /* listen_sock is an endpoint for all requests to port received 
from any IP address for this host */
    bzero((char *) &serveraddr, sizeof(serveraddr));
    serveraddr.sin_family = AF_INET; 
    serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); 
    serveraddr.sin_port = htons((unsigned short)port); 
    if (bind(listen_sock, (SA *)&serveraddr, sizeof(serveraddr)) < 0)
	    return -1;
 
    /* convert the the sock to a listening socket ready to accept connection requests */
    if (listen(listen_sock, LISTENQ) < 0)
	    return -1;
    return listen_sock;
}

在这里插入图片描述

3.2.3 数据收发

ssize_t send(int sock, const void *buff, size_t nbytes, int flags);
/*返回: 若成功则为实际发送字节数,若出错则为SOCKET_ERROR.*/
ssize_t recv(int sock, void *buff, size_t nbytes, int flags);
/*返回: 若成功返回实际接收字节数,若出错则为SOCKET_ERROR,如果recv函数在等待协议接收数据时网络中断了,则返回0*/

4. 本文目的

学习操作系统-网络编程板块知识,分享学习过程

5. 资料来源

  • 网络
  • 书籍
  • 课件

版权声明:本文为weixin_41736581原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。