• (2)linux下的简单的socket通信实例


        学习网络编程入手还是比较难的,看到后来发现还是没有想象中的那么难。如果你是新手,开始着手写代码,不写时不行的。看100遍也不如写一遍来的清楚。敲完之后带着问题去看书,这样会更有针对性。提高的速度是飞快的。

        写这个博客之前,发现别人写的代码都是吧所有的代码以粘贴,并没有讲解每个函数的功能。我甚至不知道哪个函数是哪个头文件下的。造成我对函数很不理解。下面我会对每一个函数的功能,和它的头文件以及函数原型写出来,让大家参考。

    clientc.c

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    #include <stdio.h>
      #include <sys/socket.h>
      #include <sys/types.h>
      #include <stdlib.h>
      #include <netinet/in.h>
      #include <errno.h>
      #include <string.h>
      #include <arpa/inet.h>
      #include <unistd.h>
      #define MAXLINE 1024
      int main(int argc,char **argv)
      {
      char *servInetAddr = "127.0.0.1";
      int socketfd;
      struct sockaddr_in sockaddr;
      char recvline[MAXLINE], sendline[MAXLINE];
      int n;
     
      )
      {
      printf("client <ipaddress> ");
      exit();
      }
     
      socketfd = socket(AF_INET,SOCK_STREAM,);
      memset(&sockaddr,,sizeof(sockaddr));
      sockaddr.sin_family = AF_INET;
      sockaddr.sin_port = htons();
      inet_pton(AF_INET,servInetAddr,&sockaddr.sin_addr)    {
      printf("connect error %s errno: %d ",strerror(errno),errno);
      exit();
      }
     
      printf("send message to server ");
     
      fgets(sendline,,stdin);
     
      )) < )
      {
      printf("send mes error: %s errno : %d",strerror(errno),errno);
      exit();
      }
     
      close(socketfd);
      printf("exit ");
      exit();
      }

    执行:gcc client.c -o client 后启动 ./client 客户端程序 ,启动前先启动 ./server。

    server.c

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    #include <stdio.h>
      #include <sys/socket.h>
      #include <sys/types.h>
      #include <string.h>
      #include <netinet/in.h>
      #include <stdlib.h>
      #include <errno.h>
      #include <unistd.h>
      #include <arpa/inet.h>
     
      #define MAXLINE 1024
      int main(int argc,char **argv)
      {
      int listenfd,connfd;
      struct sockaddr_in sockaddr;
      char buff[MAXLINE];
      int n;
     
      memset(&sockaddr,,sizeof(sockaddr));
     
      sockaddr.sin_family = AF_INET;
      sockaddr.sin_addr.s_addr = htonl(INADDR_ANY);
      sockaddr.sin_port = htons();
     
      listenfd = socket(AF_INET,SOCK_STREAM,);
     
      bind(listenfd,(struct sockaddr *) &sockaddr,sizeof(sockaddr));
     
      listen(listenfd,);
     
      printf("Please wait for the client information ");
     
      for(;;)
      {
      )
      {
      printf("accpet socket error: %s errno :%d ",strerror(errno),errno);
      continue;
      }
     
      n = recv(connfd,buff,MAXLINE,);
      buff[n] = '';
      printf("recv msg from client:%s",buff);
      close(connfd);
      }
      close(listenfd);
      }

    执行:gcc server.c -o server 后启动 ./server 服务端程序

    1、代码展示:功能介绍

        上面的这个简单的socket通信的代码要实现的功能:从客户端发送一条消息后,服务端接受这个消息,并在服务端显示。

    1. #include <sys/socket.h> 

    2. int socket(int family, int type, int protocol);   //指定期望的通信协议类型,返回的文件描述符和套接字描述符类似,我们成为套接字描述符,简称sockfd 

    family协议族:

    family 说明
    AF_INET IPv4协议 
    AF_INET6 IPv6
    AF_LOCAL Unix域协议(15章)
    AF_ROUTE  路由套接字(18章)
    AF_KEY 密钥套接字(19章)

    type:套接字的类型:

    type 说明
    SOCK_STREAM(常用) 字节流套接字
    SOCK_DGRAM 数据报套接字
    SOCK_SEQPACKET  有序分组套接字
    SOCK_RAW 原始套接字

    protocol:协议类型的常量或设置为0,以选择给定的family和type组合的系统默认值:

    protocol 说明
    IPPROTO_TCP TCP传输协议
    IPPROTO_UDP UDP传输协议
    IPPROTO_SCTP SCTP传输协议

    1. #include<arpa/inet.h>

    2. int inet_pton(int family,const char *strptr,void *addrptr);//成功返回1,格式不对返回0,出错返回-1//作用:p代表表达式 n代表数值  以后所写的所有代码中都有可能会需要这个函数,所以这个函数很重要//将char所指向的字符串,通过addrptr指针存放//他的反函数:  inet_ntop()作用相反。可以百度查阅这个函数的功能。因为例子里我们没有涉及到,就不介绍了。以后用到的时候再说//需要注意的是:当他发生错误的时候,errno的值会被置为EAFNOSUPPORT 关于errno值我们一会儿介绍。

    1. #include <sys/socket.h>

    2. int connect(int sockfd,const struct sockaddr* servaddr,socklen_t addrlen);//用connect函数来建立与TCP服务器的连接

    1. #include<unistd.h>

    2. int close(int sockfd);//关闭socket,并终止TCP连接

    1. #include <sys/socket.h>

    2. int bind(int sockfd,const struct* myaddr,socklen_t addrlen);//把本地协议地址赋予一个套接字。也就是将32位的IPv4或128位ipv6与16位的TCP或者UDP组合。

    1. #include<sys/socket.h>

    2. int listen(int sockfd,int backlog)//成功返回0,失败返回-1     listen函数仅由TCP服务器调用//listen函数将会做两件事://1:我们在创建套接字的时候使用了socket函数,它创建的套接字是主动套接字,bind函数的功能就是通过这个将主动套接字,变成被动套接字,告诉内核应该接受指向这个套接字的请//求,CLOSED状态变成LISTEN状态//2:本函数的第二个参数规定了内核要为该套接字排队的最大连接个数。

    1. #include <sys/socket.h>

    2. int accept(int sockfd,struct sockaddr* cliaddr,socklen_t *addrlen);//成功返回描述符,失败返回-1//1、如果第二三个参数为空,代表了,我们对客户的身份不感兴趣,因此置为NULL;//2、第一个参数为socket创建的监听套接字,返回的是已连接套接字,两个套接字是有区别的,而且非常重要。区别:我们所创建的监听套接字一般服务器只创建一个,并且一直存在。而内核会为每一个服务器进程的客户连接建立一个连接套接字,当服务器完成对某个给定客户的服务时,连接套接字就会被关闭。

    关于连接三次握手和TCP连接关闭时候的分组交换

      三次握手:

      为了更好的理解connect、bind、close三个函数,了解一下TCP连接的建立和终止是很有必要的。(请务必理解理解上面的所有的函数后,再看这节)。

      1、服务器首先必须被打开,等待准备接受外来的连接。我们上面的例子用到了socket、bind、listen这3个函数。之后,我们称为服务端被被动打开了。

      2、客户端是通过connect发起主动打开。

      

          3、主动打开后,客户TCP发送了一个SYN(同步)分节,它告诉服务器客户将在连接中只发送的数据的初始序列号,SYN分节不携带数据。它发送的IP数据报,只有一个IP首部、一个TCP首部以及TCP选项。

      4、服务器必须确认(ACK)客户的SYN,同时自己也发送一个SYN分节,它含有服务器将在同一连接中发送的数据的初始序列号。服务器在单个分节中发送SYN和对客户SYN的ACK确认(+1)。

      5、客户必须确认服务器的SYN分节。

      上面的过程称为TCP的三次握手。

        注:SYN(synchronous)是TCP/IP建立连接时使用的握手信号。在客户机和服务器之间建立正常的TCP网络连接时,客户机首先发出一个SYN消息,服务器使用SYN+ACK应答表示接收到了这个消息,最后客户机再以ACK消息响应。这样在客户机和服务器之间才能建立起可靠的TCP连接,数据才可以在客户机和服务器之间传递

      TCP连接终止

      终止一个连接需要4个分节。

      1、通过调用close,我们执行主动关闭,TCP发送一个FIN(finish,表示结束),表示数据发送完毕。

      2、对端接收到FIN后,执行被动关闭。

        3、一段时候后,接收到文件结束符的应用进程,将调用close关闭它的套接字。于是套接字也发送一个了FIN。

      4、确认这个FIN ACK+1   下图很清楚的表达了。  

      5、我们也称它为TCP四次握手。

      

          > 4、IPv4、IPv6套接字的地址结构

      IPv4地址结构: 

    1. struct in_addr {

    2.    in_addr_t  s_addr;

    3. };

    4.  

    5. struct sockaddr_in {

    6.    uint8_t sin_len; //无符号8位整型

    7.    sa_family_t sin_famliy;  /*AF_INET*/

    8.    in_port_t  sin_port;      9    struct in_addr sin_addr;   /*32位 IPv4 地址*/

    9.   ];      /*unuse*/

    10. 11 }; //头文件 #include <sys/types.h> //sa_family_t和socklen_t 头文件 #include <sys/socket.h> //in_addr_t in_port_t 头文件 #include <netinet/in.h>

      IPv6地址结构:

    1. struct in6_addr {

    2.   uint8_t  s6_addr[];

    3. };

    4.  

    5. #define SIN6_LEN

    6.  

    7. struct sockaddr_in6 {

    8.   uint8_t sin6_len;

    9.   sa_family_t sin6_famliy;

    10.   in_port_t  sin6_port;

    11.  

    12.   uint32_t sin6_flowinfo;

    13.   struct in6_addr sin6_addr;

    14.  

    15.   uint32_t sin6_scope_id;

    16. };

      > 5、一些好的学习网站总结

      1、关于51CTO上的这个视频http://edu.51cto.com/course/course_id-903.html,我买了,但是讲的非常烂,建议大家不要购买。教课的老师也就是照着书念,还不如自己。浪费钱。

      2、http://www.cnblogs.com/skynet/archive/2010/12/12/1903949.html

           http://blog.csdn.net/hguisu/article/details/7445768/

         http://www.oschina.net/code/snippet_97047_675

    这几篇博客不错,能带你入门。

      > 6、代码下载

      Githubhttps://github.com/micwu/Demo

      > 7、总结

       学习之路是很蛮长的。想要学好,非常难,需要长期的积累。我也正在学习中。经过了很多的挫折,但是有理想,就一定能成功。希望大家想走Linux下服务器编程的同志们,一起加油吧。

      > 8、echo实现

      

      

  • 相关阅读:
    Linux中使用expect脚本实现远程机器自动登录_linux shell
    Linux: ssh命令 远程登录
    总结:PgSql备份pg_dump与还原pg_restore
    停电后,在UPS电源下服务器自动关机脚本
    总结:修改相关postgres用户密码
    问题解决:psql: could not connect to server: No such file or directory Is the server running locally and accepting connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"?
    delphi 播放mp3
    delphi 获取文件路径函数
    Delphi XE2 使用dbExpress连接MySQL数据库
    kbmmemsql 查询
  • 原文地址:https://www.cnblogs.com/yjds/p/8597364.html
Copyright © 2020-2023  润新知