基于TCP的服务端/客户端

下载地址:https://github.com/baitxaps/EasySocket

基于TCP的服务端/客户端

 根据数据传输方式不同,基于网络协议的套接字一般分为TCP套接字(也叫基于流stream的套接字)和UDP套接字。

 

 TCP/IP协议栈(Stack,层)eg:

            应用层

            /   \

          TCP   UDP

           \     /

            IP层

             |

            链路层

 

 各层可能通过操作系统等软件实现,也可能通过类似NIC的硬件设备实现

0>开放系统

 以多个标准为依据设计的系统称为开放系统,TCP/IP协议栈也属于其中之一

 优点:路由器来完成IP层交互任务,网卡:网卡制造商都会遵守链路层的协议标准

 

 1>链路层:是物理链接领或标准的结果,也是最基本的领域,专门定义LAN,WAN,MAN等网络标准

 2>IP层:准备好物理连接后就要传输数据。为了在复杂的网络中传输数据,首先需要考虑路径的选择,向目标传输

 数据需要经过哪条路径?解决此问题就是IP层,该层使用的协议就是IP。IP本身是面向消息的,不可靠的协议。每次

 传输数据时会帮我们选择路径,但并不一致。如果传输中发生路径错误,则选择其他路径;但如果发生数据丢失或

 错误,则无法解决--IP协议无法应对数据错误

 3>TCP/UDP层:IP层解决数据传输中的路径选择问题,只需照此路径传数据即可。TCP和UDP层以IP层提供的路径信息为

 基础完成实际数据传输--传输层。UDP比TCP简单

 TCP与IP层二者关系:

 IP层只关注1个数据包(数据传输的基本单位)的传输过程。因此,即使传输多个数据包,每个数据包也是由IP层实际传输的

 ,也就是说传输本身是不可靠的。若只利用IP层传输数据,则有可能导致后传输的数据包B比先传输的数据包A提早到达。另外

 ,传输的数据包A/B/C中有可能收到A和C,甚至收到的C可能已损毁

 TCP如果数据交换过程中可以确认对方已收到数据,并重传丢失的数据,那么即便IP层不保证数据传输,这类通信也是可靠的

 4>应用层:

 上述内容是套接字通信过程中自动处理的。选择数据传输路径,数据确认过程都被隐藏到套接字内部。而与其说是“隐藏”,倒不

 如“使程序员从这些细节中解放出来”的表达更为准确。程序员编程时无需考虑这些过程,但这并不意味着不用掌握这些短识。只有

 掌握了这些理论,才能编写出符合需求的网络程序

 总之,向各位提供的工具就是套接字,只需利用套接字编出程序即可。编写软件的过程中,需要根据程序特点决定服务器和客户端之

 间的数据传输规则---应用层协议。

 网络编程的大部份内容就是设计并实现应用层协议

 

 8.1 实现基于TCP的服务器端/客户端

 TCP的服务器端默认的函数调用顺序:

 socket() 创建套接字

    |

    V

 bind()分配套接字地址

    |

    V

 listen()等待连接请求状态

    |

    V

 accept() 充许连接

    |

    V

 read()/write() 数据交换

    |

    V

 close() 断开连接

 

 socket,bind前面已说明,bind()(给套接字分配了地址,接下来就要通过调用listen()进入等待连接请求状态

 只有调用了listen(),客户端才能进入可发出连接请求的状态----这时客户端才能调用connect()(若提前调用将发生错误)

 int listen(int sock,int backlog);// 成功时返回0,失败时返回-1

 sock:希望进入等待连接状态的套接字文件描述符,传递的描述符套接字参数为服务端套接字(监听套接字)

 backlog:连接请求等待队列(Queue)的长度,若为5,则队列长度为5,表示最多使5个连接请求进入队列。(与服务端的特性有关,

 像频繁接收请求的Web服务端至少应为15.另外,连接请求队列的大小始终根据实验结果而定。)

 

 int accept(int sock,struct sockaddr *addr,socklen_t *addrlen);//成功返回创建的套接字文件描述符,失败返回-1;

 sock:服务器套接字的文件描述符

 addr:保存发起连接请求的客户端地址信息的变量地址值,调用函数后向传递来的地址变量参数填充客房端地址信息

 addrlen:第二个参数addr结构体的长度,但时存有长度的变量地址。函数调用完成后,该变量即被填入客户端地址长度 

 

 accept()受理连接请求等待队列中待处理的客户端连接请求。函数调用成功时,accetp()内部将产生用于数据I/O的套接字,并返回其

 文件描述符。需要强调的是,套接字是自动创建的,并自动与发起连接请求的客户端建立连接。

调用accept()从队头取1个连接请求与客户端建立连接,并返回创建的套接字文件描述符。另外,调用accetp()时若等待队列为空,则

 accept()不会返回,直到队列中出现新的客户端连接

 

 8.2 TCP客户端的默认函数调用顺序

 socket() 创建套接字

    |

    V

 connect()请求连接

    |

    V

 read()/write() 数据交换

    |

    V

 close() 断开连接

 

 与服务端相比,区别就在于"请求连接“,它是创建客户端套接字后向服务器端发起的连接请求。服务器端调用

 listen()创建连接请求等待队列,之后客户端即可请求连接。

 

 int connect(int sock, struct sockaddr *servaddr, socklen_t addrlen);//成功时返回0,失败时返回-1;

 sock:客户端套接字文件描述符

 servaddr:保存目标服务器端地址信息的变量地址值

 addrlen:以字节为单位传递已传递给第二个结构体参数servaddr的地址变量找度

 

 客户端调用connect(),发生以下情况之一才会返回(完成函数调用)

 1>服务器端接收连接请求

 2>发生断网等异常情况而中断连接请求

 需要注意,所谓的“接收连接”并不意味着服务器端调用accept(),其实是服务端把连接请求信息记录到等待队列。因此connect()

 返回后并不立即进行数据交换

 

 3>实现服务端必经过过程之一就是给套接字分配IP和端口号。但客户实现过程中并未出现套接字地址分配,而是创建套接字后立即调用

 connect(). 客户端调用connect()时,操作系统,更准确地说是在内核中,IP用计算机(主机)的IP,端口随机分配地址---客户端

 的IP和端口调用connect()时自动分配,无需调用标记的bind()进行分配

 

 8.3 基于TCP的服务端/客户端函数调用关系

 前面讲解TCP服务器/客户端的实现顺序,实际上二者并非相互独立

    服务器

 socket() 创建套接字

    |

    V

 bind()分配套接字地址                     客户端

    |                                   socket() 创建套接字

    V                                         |

 listen()等待连接请求状态                        V

    |   <---------------------------—--connect()请求连接

    V                      or       /         |

 accept() 充许连接                   /

    |   <------------------------  /          |

    V                                         V

 read()/write() 数据交换  <------->     read()/write()数据交换

    |                                         |

    V                                         V

 close() 断开连接   <------------->      close()断开连接

 

 

 总体流程整理:服务器端创建套接字后连续调用bind,listen函数进入等待状态,客户端通过调用connect()

 发起连接请求.需要注意的是,客户端只能等到服务端调用listen()后才能调connect().同时要清楚,客户端调用

 connect()前,服务器端有可能率先调用accept().当然,此时服务端在调用accept()时进入阻塞(blocking)状

 态,直到客户端调用connect()为止

 

 8.4实现迭代服务器端/客户端

 1>实现迭代服务器端

 编写回声(echo)服务端/客户端----服务端将客户端传输的字符串数据原封不动地传回客户端,就像回声一样。

 之前的hello world服务端处理完发1个客户端连接请求即退出,连接请求等待队列实际没有太大意义。

 设置好等待队列的大小后,应向所有客户疫提供服务。如果想继续受理后续的客户疫连接请求,最简单的办法就是

 插入循环语句反复调用accetp()

 

 socket() 创建套接字

    |

    V

 bind()分配套接字地址

    |

    V

 listen()等待连接请求状态

    | <--------------------

    V                     |

 accept() 充许连接          |

    |                     |

    V                     |

 read()/write() 数据交换    |

    |---------------------

    V

 close() 断开连接

 调用accept()后,紧接着调用I/O相关的read(),write(),然后调用close(),这并非针对服务端套接字,而

 是针对accept()调用时创建的套接字。调用close()就意味着结束了针对某一客户端的服务,此时如果还想服务于

 其他客户端,就要重新调用accept()

 

2>实现迭代服务器端/客户端

 前面讲的就是迭代服务器端。妈即使服务端以迭代方式运转,客户端代码亦无太大区别。

 程序的基本运行方式:

 .服务端在同一时刻只与一个客户端相连,并提供回声服务

 .服务端依次向5个客户端提供服务并退出

 .客户端接收用户输入的字符串并发送到服务端

 .服务端将接收到的字符串数据传回客户端,即“回声”

 .服务端与客户端之间的字符串回声一直执行到客户端输入Q为止

 // 服务端

 #define BUF_SIZE 1024

 int echo_serverc(int argc,char *argv[]) {

 int serv_sock,clnt_sock;

 char message[BUF_SIZE];

 int str_len,i;

 

 struct sockaddr_in serv_adr,clnt_adr;

 socklen_t clnt_adr_sz;

 

 if (argc!=2) {

 printf("usege:%s<port>\n",argv[0]);

 exit(1);

 }

 serv_sock = socket(PF_INET, SOCK_STREAM, 0);

 if (serv_sock == -1) {

 error_handling("socket() error");

 }

 memset(&serv_adr, 0, sizeof(serv_adr));

 serv_adr.sin_family = AF_INET;

 serv_adr.sin_addr.s_addr= htonl(INADDR_ANY);

 serv_adr.sin_port= htons(atoi(argv[1]));

 if (bind(serv_sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr))== -1) {

 error_handling("bind() error");

 }

 

 if (listen(serv_sock, 5) == -1) {

 error_handling("listen() error");

 }

 clnt_adr_sz = sizeof(clnt_adr);

 for ( i = 0; i < 5; i ++) {

 clnt_sock = accept(serv_sock, (struct sockaddr *)&clnt_adr, &clnt_adr_sz);

 if (clnt_sock == -1) {

 error_handling("accept() error");

 }else {

 printf("Connected client %d \n",i + 1);

 }

 while ((str_len = (int)read(clnt_sock, message, BUF_SIZE))!=0) {

 write(clnt_sock, message, str_len);

 }

 close(serv_sock);

 }

 

 return 0;

 }

 

 // 客户端

 #define BUF_SIZE 1024

 int echo_clientc(int argc,char *argv[]) {

 int sock;

 char message[BUF_SIZE];

 long str_len;

 struct sockaddr_in serv_adr;

 if (argc != 3) {

 printf("Usage :%s <IP> <Port> \n",argv[0]);

 exit(1);

 }

 

 sock = socket(PF_INET, SOCK_STREAM, 0);

 if (sock == -1) {

 error_handling("socket error");

 }

 memset(&serv_adr, 0, sizeof(serv_adr));

 serv_adr.sin_family = AF_INET;

 serv_adr.sin_addr.s_addr = inet_addr("127.0.0.1");

 serv_adr.sin_port = htons(atoi("9190"));

 if (connect(sock, (struct sockaddr *)&serv_adr, sizeof(serv_adr))== -1) {

 error_handling("connect error");

 }else {

 puts("Conneted...");

 }

 while (1) {

 fputs("input messae(Q to quit):", stdout);

 fgets(message,BUF_SIZE,stdin);

 

 if (!strcmp(message, "q\n") || !strcmp(message, "Q\n")) {

 break;

 }

 write(sock, message, strlen(message));

 str_len = read(sock, message, BUF_SIZE-1);

 

 message[str_len]= 0;

 printf("message from server:%s",message);

 }

 close(sock);

 

 return 0;

 }

 ...

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: TCP/IP是一种通信协议,它包含了客户端服务端两个角色。客户端服务端通过TCP/IP协议进行通信,实现数据的传输和交互。 客户端是指发起连接请求的一方。当客户端需要与服务端进行通信时,它会首先向服务端发起连接请求。客户端在发送请求时,需要提供服务端的地址和端口号。一旦连接建立成功,客户端可以通过发送请求报文来向服务端发送数据请求。客户端还可以接收服务端响应的数据,以便进行后续处理。在数据传输结束后,客户端可以选择关闭连接,释放资源。 服务端是指接收并处理连接请求的一方。服务端监听指定的端口,并等待客户端的连接请求。当服务端接收到连接请求后,会建立一个与客户端的连接。一旦连接建立,服务端可以接收客户端发送的请求数据,并进行相应的处理。处理完成后,服务端会生成响应数据,并发送给客户端服务端客户端可以通过多次的请求响应交互来进行数据的传输和处理。最后,服务端可以主动关闭连接,释放资源。 TCP/IP客户端服务端之间的通信具有可靠性和顺序性。TCP协议保证了数据传输的可靠性,即在传输过程中不会丢失数据或产生错乱。而IP协议则负责将数据包传输到目标地址。客户端服务端之间的通信是基于可靠的连接进行的,通过TCP协议进行数据传输。数据按照顺序在客户端服务端之间传输,确保了数据的有效性和完整性。 总之,TCP/IP客户端服务端通过可靠的连接和顺序传输实现数据的交互和传输。这种通信方式在互联网上广泛应用,例如浏览器与服务器之间的通信、远程登录等。 ### 回答2: TCP/IP客户端服务端是指在TCP/IP网络中进行通信的两个角色。TCP/IP是互联网所使用的通信协议,它提供了可靠的、无连接的通信。 客户端是指请求并接收服务的一方。客户端通常由应用程序或者用户发起,它向服务器发起请求,并等待服务器的响应。客户端主动与服务器建立连接,并发送请求报文给服务器。在建立连接后,客户端可以将请求数据发送给服务器,并等待服务器的响应。客户端可以是个人电脑、手机、平板电脑等设备,通过应用程序与服务端进行通信。 服务端是指提供服务的一方。服务端运行在服务器上,并等待客户端的连接请求。一旦接收到客户端的请求,服务端会根据请求内容进行相应的处理,并将处理结果返回给客户端服务端可以是网络服务器、数据库服务器、邮件服务器等,它通过应用程序接收和处理客户端请求,并返回相应的结果给客户端TCP/IP客户端服务端之间的通信过程分为三个阶段:建立连接阶段、数据传输阶段和连接关闭阶段。在建立连接阶段,客户端发起连接请求,服务端接受请求并与客户端建立连接。在数据传输阶段,客户端可以将需要发送的数据通过连接发送给服务端服务端接收并处理数据,并将处理结果返回给客户端。在连接关闭阶段,客户端服务端可以选择关闭连接,结束通信。 总之,TCP/IP客户端服务端是互联网通信的两个主要角色,客户端发起请求并接收服务,而服务端接受请求并提供相应的服务。通过TCP/IP协议,客户端服务端可以进行可靠的、无连接的通信。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值