网络编程——TCP(1)

来源:互联网 发布:oracle 清除表数据 编辑:程序博客网 时间:2024/06/15 22:48

本篇博客将会实现TCP的编程流程,在实现TCP编程之前,先将TCP/IP的四层协议做以简单的介绍。

一、TCP/IP四层协议

TCP/IP的四层协议自底向上分别是数据链路层网络层运输层应用层。很多人也有了解过OSI七层协议,同样的,自底向上分别是物理层数据链路层网络层运输层会话层表示层应用层。而我们主要来说一下TCP/IP四层协议。

1、数据链路层

a.数据链路层实现了网卡接口的网络驱动程序,以处理数据在物理媒介上的传输。
b.常用协议:ARP协议、RARP协议。

2、网络层

a.网络层实现数据包的选路和转发。
b.常用协议:IP协议、ICMP协议。

3、运输层

a.运输层为两台主机实现了端到端的服务。
b.常用协议:TCP协议、UDP协议、SCTP协议。

4、应用层

a.应用层负责处理应用程序的逻辑。
b.常用协议:DNS协议(当然应用层不止这一个协议,这里只说这一个)

二、使用TCP协议完成网络编程

1、完成网络编程需要客户端和服务器端,简单说一下客户端和服务器端。

客户端:请求服务
服务器端:提供服务

2、协议选择(本篇博客选择TCP,之后专门更一篇UDP的)

TCP协议:它是一种面向连接的,可靠的,流式服务。
UDP协议:它是一种无连接,不可靠的,数据报服务。

3、TCP的编程流程:

ser(服务器端):socket、bind、listen、accept、recv/send、close
cli(客户端):socket、(bind)、connect、recv/send、close

接下来说一下用到的函数

a. int socket( int domain, int type, int protocol);
domain:告诉系统选择哪个底层协议族, AF_INET是PF_INET协议族所对应的地址族.
type:选择协议 SOCK_STREAM(TCP), SOCK_UGRAM(UDP).
protocol:这个值一般我们都把它设置为0,表示使用默认协议。本来这个参数是在前两个参数的基础上,再选择一个具体的协议,但是前两个参数已经完全决定了它的值,所以这个参数一般置为0.

b. int bind(int sockfd, const struct sockaddr* addr, int addrlen )
addr:指定IP地址和端口号.
addrlen:指定IP地址和端口号的长度.

c. int listen(int sockfd, int backlog)
backlog:内核监听队列的最大长度,典型参数值是5.

d. int accept(int sockfd, struct sockaddr * addr, int addrlen)
addr:记录客户端的 IP 地址和端口号
addrlen:记录的客户端的 IP 地址和端口号的长度.

注意:bind和accept的addr不是同一个。

e. int connect(int sockfd, struct sockaddr* addr, int addrlen);
addr: 要连接的服务器的 IP 地址和端口号.
addrlen:要连接的服务器的 IP 地址和端口号的长度.

f. revc/send
revc(int sockfd, void *buf, int len, int flags);//读数据
send(int sockfd, const void *buf, int len, int flags);//写数据
buf:指定读/写缓冲区位置
len:指定读/写缓冲区大小
flags:置为0即可

接下来就是代码了

ser.c

#include<stdio.h>#include<stdlib.h>#include<string.h>#include<assert.h>#include<unistd.h>#include<sys/types.h>#include<sys/socket.h>#include<arpa/inet.h>void main(){    int sockfd=socket(AF_INET,SOCK_STREAM,0);    assert(sockfd!=-1);    struct sockaddr_in ser,cli;    ser.sin_family=AF_INET;    ser.sin_port=htons(6000);    ser.sin_addr.s_addr=inet_addr("127.0.0.1");    int res =bind(sockfd,(struct sockaddr *)&ser,sizeof(ser));    assert(res!=-1);    listen(sockfd,5);    while(1)    {        int len=sizeof(cli);        int c=accept(sockfd,(struct sockaddr*)&cli,&len);        assert(c!=-1);        char buff[128]={0};        recv(c,buff,127,0);        printf("recv::%s\n",buff);        send(c,"I know",strlen("I know"),0);    close(c);    }    close(sockfd);}

cli.c

#include<stdio.h>#include<stdlib.h>#include<string.h>#include<assert.h>#include<unistd.h>#include<sys/types.h>#include<sys/socket.h>#include<arpa/inet.h>void main(){    int sockfd=socket(AF_INET,SOCK_STREAM,0);    assert(sockfd!=-1);    struct sockaddr_in ser,cli;    ser.sin_family=AF_INET;    ser.sin_port=htons(6000);    ser.sin_addr.s_addr=inet_addr("127.0.0.1");    int res=connect(sockfd,(struct sockaddr*)&ser,sizeof(ser));    assert(res!=-1);    send(sockfd,"hello world",strlen("hello world"),0);    char buff[128]={0};    recv(sockfd,buff,127,0);    printf("recv::%s\n",buff);    close(sockfd);}

运行结果

这里写图片描述

注意:应该先运行服务器端,再运行客户端,当服务器在运行的时候,它会等待客户端给他发送请求,然后响应客服端的请求。要是先运行客户端,再运行服务器端,此时的服务器端是接收不到客户端的请求的,有兴趣的可以试一下,我自己在这上面踩得坑不少。

原创粉丝点击