zoukankan      html  css  js  c++  java
  • (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实现

      

      

  • 相关阅读:
    HDU Problem 1811 Rank of Tetris【拓扑排序+并查集】
    POJ Problem 2367 Genealogical tree【拓扑排序】
    HDU Problem 2647 Reward【拓扑排序】
    HDU Problem 1285 确定比赛名次【拓扑排序】
    HDU Problem HDU Today 【最短路】
    HDU Problem 3665 Seaside【最短路】
    HDU Problem 一个人的旅行 【最短路dijkstra】
    HDU Problem 1596 find the safest road【最短路dijkstra】
    Beyond Compare文本合并进行内容替换要注意什么
    用这些工具都可以比较代码的差异
  • 原文地址:https://www.cnblogs.com/yjds/p/8597364.html
Copyright © 2011-2022 走看看