Linux C 网络编程(一)

OSI七层网络模型

建立七层网络模型的目的是为了让不同的制造商在网络上通信,比如我A主机是苹果公司生产,操作系统是苹果,B主机是IBM公司生产,操作系统是windows,在AB主机软硬件都不同的情况下通过七层网络模型就可以互相通信了。
下面阐述一下各层的作用:

  • 1 应用层 就是应用软件,定义一些网络通信和数据传输的接口。
    2表示层 掩盖系统中数据格式的不同性,制定独立结构的数据传输格式,差不多跟格式转换工程类似。
    3会话层 管理用户会话和对话,报告上一层得错误。
    4 传输层 管理网络中端到端的信息传送,通过错误纠正和流控制机制提高可靠有序的数据包传送,典型的有TCP/UDP协议。
    5网络层 就是网络中如何传送数据,传到那里去? 比如IP协议,ARP等等。
    6数据链路层 封装数据包
    7物理层 就是用什么方式通信啊,光纤?封装数据包为数据帧。

这7层都是层层相扣的,假如A主机想发信息给B主机,A主机的信息首先发生到A主机的应用层,然后表示层…最后通过物理层(比如网线)到B主机的数据链路层,到应用层,最后到终端,B主机用户就能看见A主机所发的信息。A主机在发送信息的过程每层都封装了B主机的信息。数据在各层传送,每层都封装了上层的头和尾,协议头保护层与层直接的通信机制,比如传输层TCP就封装了IP协议。

  • 关于传输层TCP协议
    TCP(Transmission Control Protocol)功能:对建立网络上用户进程之间的对话负责,它确保进程之间的可靠通信,所提供的功能如下:
  1. 监听输入对话建立请求
  2. 请求另一网络站点对话
  3. 可靠的发送和接收数据
  4. 适度的关闭对话
    UDP(User Datagram Protocol)提供不可靠的非连接型传输层服务,它允许在源和目的地之间传送数据,而不必在传送数据之前建立对话。它主要用于那些非连接型的应用程序,如:视频点播。
    IP协议 IP的主要目的是为数据输入/输出网络提供基本算法,为高层协议提供无连接的传送服务。这意味着在IP将数据递交给接收站点以前不在传输站点和接收站点之间建立对话。它只是封装和传递数据,但不向发送者或接收者报告包的状态,不处理所遇到的故障。IP包由IP协议头与协议数据两部分构成。协议头主要存放一些源和目的的IP等信息。
    TCP和UDP的区别:TCP提高的是可靠的交换,目的机接受到信息将发送确认信息给源机。UDP提高无连接,不可靠的传输服务,目的机接收到信息不会发生确认信息给源机。即使发送错信息也不会向发送方报错,另外UDP传输比TCP传输更快。
  • Linux中的网络编程通过Socket(套接字)接口实现,Socket是一种文件描述符。

    地址结构:
    struct sockaddr
    {
    u_short sa_family;
    char sa_data[14];
    }
    sa_family 地址族,采用“AF_xxx”的形式,如:AF_INET。
    Sa_data:14字节的特定协议地址。

编程中一般并不直接针对sockaddr数据结构操作,而是使用与sockaddr等价的sockaddr_in数据结构

struct sockaddr_in
{
short int sin_family; /* Internet地址族*/
unsigned short int sin_port; /* 端口号*/
struct in_addr sin_addr; /* IP地址*/
unsigned char sin_zero[8]; /* 填0 */
}
struct in_addr
{
unsigned long s_addr;
}

S_addr: 32位的地址。
在网络上标识一台机器可以用IP,也可以使用主机名。

struct hostent *gethostbyname(const char *hostname)
struct hostent
{
char *h_name; /* 主机的正式名称*/
char *h_aliases; /* 主机的别名*/
int h_addrtype; /* 主机的地址类型AF_INET*/
int h_length; /* 主机的地址长度*/
char **h_addr_list; /* 主机的IP地址列表*/
}
#define h_addr h_addr_list[0] /* 主机的第一个IP地址*/

基于TCP网络程序设计的算法如下:

server机
利用socket函数创建一个套接字
利用bind函数绑定server地址
利用listen函数设置最大可服务的服务器数目。            
利用accept函数接受client的数据。
接受数据/发送数据
client机
利用socket函数创建一个套接字
利用bind函数绑定server地址
利用connect函数与server机连接
发送数据/接受数据。

下面是一个基于tcp协议的程序

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define NUMBER_PORT 3333
int main(void)
{
    int socket_fd,new_fd;
    char buf[1024];
    int sin_size,nbytes,i;
    struct sockaddr_in server_addr,client_addr;
    socket_fd=socket(AF_INET,SOCK_STREAM,0);
    bzero(&server_addr,sizeof(struct sockaddr_in));
    server_addr.sin_family=AF_INET;
    server_addr.sin_port=htons(NUMBER_PORT);
    server_addr.sin_addr.s_addr=htonl(INADDR_ANY);
    bind(socket_fd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr));
    listen(socket_fd,5)
    while(1)
{
    sin_size=sizeof(struct sockaddr_in);
    new_fd=accept(socket_fd,(struct sockaddr *)(&client_addr),&sin_size);
    fprintf(stderr,"Server get connection from %s\n",inet_ntoa(client_addr.sin_addr));
    nbytes=read(new_fd,buf,1024);
    printf("receive %s \n",buf);
    for(i=0;i<nbytes;i++)
    buf[i]=toupper(buf[i]);
    write(new_fd,buf,1024);
    close(new_fd);
}
    close(socket_fd);
   return 0;
}
 
/*client*/
include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define NUMBER_PORT 3333
int main(int argc,char *argv[])
{    
  struct sockaddr_in server_addr;
    int socket_fd;
    char buf[1024];
    socket_fd=socket(AF_INET,SOCK_STREAM,0);
    bzero(&server_addr,sizeof(struct sockaddr_in));
    host=gethostname(argv[1]);
    server_addr.sin_family=AF_INET;
    server_addr.sin_port=htons(NUMBER_PORT);
    server_addr.sin_addr=*(struct in_addr *)host->h_addr;
    connect(socket_fd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr));
     printf("please inpuy char : \n");
    fgets(buf,1024,stdin);
    write(socket_fd,buf,strlen(buf));
    read(socket_fd,buf,1024);
    printf("recevie %s from server \n",buf);
    close(socket_fd);
    exit(0);   
}

程序运行结果;
基于UDP协议算法如下

server机
1 利用socket创建一个套接字
2利用bind函数绑定server地址 
3循环接收数据,用函数recvfrom(),发送数据用sendto();
4关闭连接
client机
1 利用socket创建一个套接字
2利用bind函数绑定server地址 
3循环接收数据,用函数recvfrom(),发送数据用sendto();
4关闭连接

因为UDP是无连接协议,所以算法简单。
下面是一个简单的UDP程序

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <arpa/inet.h>
#define SERVER_PORT 8888
int main(void)
{
  int socket_fd,sin_size,i;
    struct sockaddr_in server_addr,client_addr;
    char buf[1024];
    sockfd=socket(AF_INET,SOCK_DGRAM,0);
    bzero(&addr,sizeof(struct sockaddr_in));
     addr.sin_family=AF_INET; 
     addr.sin_addr.s_addr=htonl(INADDR_ANY); 
     addr.sin_port=htons(SERVER_PORT);
    bind(socket_fd,(struct sockaddr *)&server_addr,sizeof(struct soockaddr));
    sin_size=sizeof(struct sockaddr);
    n=recvfrom(socket_fd,buf,1024,0,(struct sockaddr *)&client_addr,&sin_size);
    printf("receive %s from client \n",buf);
    for(i=0;i<n;i++)
    buf[i]=toupper(buf[i]);
    n=sendto(socket_fd,buf,1024,0,(struct sockaddr *)&client_addr,&sin_size);
    close(socket_fd);
}
 
/*client*/
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/types.h>
#include <arpa/inet.h>
#define SERVER_PORT 8888
#define MAX_BUF_SIZE 1024
void udpc_requ(int sockfd,const struct sockaddr_in *addr,int len)
{
 char buffer[MAX_BUF_SIZE];
 int n;
 while(1)
 {
/* 从键盘读入,写到服务端 */
  printf("Please input char:\n");
  fgets(buffer,MAX_BUF_SIZE,stdin);
  sendto(sockfd,buffer,strlen(buffer),0,addr,len);
  bzero(buffer,MAX_BUF_SIZE);
n=recvfrom(sockfd,buffer,MAX_MSG_SIZE,0,(struct sockaddr*)addr,&len); }
}
int main(int argc,char **argv)
{
int sockfd;
 struct sockaddr_in addr;
 if(argc!=2)
 {
 fprintf(stderr,"Usage:%s server_ip\n",argv[0]);
  exit(1);
 }
 sockfd=socket(AF_INET,SOCK_DGRAM,0);
 if(sockfd<0)
 {
  fprintf(stderr,"Socket Error:%s\n",strerror(errno));
  exit(1);
 }
 /* 填充服务端的资料 */
 bzero(&addr,sizeof(struct sockaddr_in));
 addr.sin_family=AF_INET;
addr.sin_port=htons(SERVER_PORT);
 if(inet_aton(argv[1],&addr.sin_addr)<0)  
{
  fprintf(stderr,"Ip error:%s\n",strerror(errno));
  exit(1);
 }
 udpc_requ(sockfd,&addr,sizeof(struct sockaddr_in));  close(sockfd);
}

分析:通过例子以及程序的调试,可以知道UDP和TCP协议传输数据的区别,TCP协议传输数据时,是点到点的传送,当server和client在通信时,同IP的client不能和server通信,如图:
无法返回数据,说明TCP协议下,如果server与client已经握手,那么同IP的client不能与server通信。
而在UDP协议下传输数据时,当server和client在通信时,假如同IPclient 试图与server通信时,如图:
其中cas是另外一个同IP的client发送的数据,说明UDP是无连接的,而且可以无法确定数据传输的可靠性。
下面是一个TCP协议的并发服务程序:

#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <netdb.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <sys/socket.h>
#define MY_PORT 3333
int main(int argc ,char **argv)
{
 int listen_fd,accept_fd;
 struct sockaddr_in     client_addr;
 int n;
 
 if((listen_fd=socket(AF_INET,SOCK_STREAM,0))<0)
  {
        printf("Socket Error:%s\n\a",strerror(errno));
        exit(1);
  }
 
 bzero(&client_addr,sizeof(struct sockaddr_in));
 client_addr.sin_family=AF_INET;
 client_addr.sin_port=htons(MY_PORT);
 client_addr.sin_addr.s_addr=htonl(INADDR_ANY);
 n=1;
  if(bind(listen_fd,(struct sockaddr *)&client_addr,sizeof(client_addr))<0)
  {
        printf("Bind Error:%s\n\a",strerror(errno));
        exit(1);
  }
  listen(listen_fd,5);
  while(1)
  {
   accept_fd=accept(listen_fd,NULL,NULL);
   if((accept_fd<0)&&(errno==EINTR))
          continue;
   else if(accept_fd<0)
    {
        printf("Accept Error:%s\n\a",strerror(errno));
        continue;
    }
  if((n=fork())==0)
   {
        /* 子进程处理客户端的连接 */
        char buffer[1024];
        close(listen_fd);
        n=read(accept_fd,buffer,1024);
        write(accept_fd,buffer,n);
        close(accept_fd);
        exit(0);
   }
   else if(n<0)
        printf("Fork Error:%s\n\a",strerror(errno));
   close(accept_fd);
  }
}

猜你喜欢

转载自blog.csdn.net/weixin_43667308/article/details/85727011
今日推荐