TCP套接字编程

Socket编程

应用进程使用传输层提供的服务才能够交换报文,实现应用协议,实现应用【通过层间接口,把报文交下去。】

        TCP/IP:应用进程使用Socket API访问传输服务【在界面中的socket上把报文交下去,以socket API形式,传报文前要建立socket,建立完有效的socket之后借助于这个socket再进行收和发,收发完了之后再把socket关掉】

        地点:界面上的SAP(Socket) 方式:Socket API

Socket:分布式应用进程之间的门,传输层协议提供的端到端服务接口【应用进程是不需要考虑报文是怎么传的,只要借助于socket API,建socket、使用socket传、收、最后把socket关掉就好了】【在下层收到报文之后,这个报文对下层来说就是SDU,收到SDU之后具体怎么去传,是下层要管的事,现在要管的是应用层的东西】

2种传输层服务的socket类型:

  • TCP:可靠的、字节流的服务【连接(在通信之前打个招呼),传下去是什么,对方收到就是什么,不错、不重复、不丢失,原原本本。但是他是字节流的服务,保证按照流来说是对的,但是不保证报文的界限,所以在很多应用协议中都能看到很多报文-报文之间的界限得自己维护的痕迹】
  • UDP:不可靠(数据UDP数据报)服务【无连接(直接通信:如域名解析),】

TCP套接字编程

套接字:应用进程与端到端传输协议(TCP或UDP)之间的门户【socket就是一个整数,上下层之间的一个约定。TCP这样一个整数约定就像一个文件句柄一样,打开一个文件返回一个文件句柄整数,对这个整数的操作,读或写,就是对文件的操作;我对socket操作,就是对会话上的两个应用进程之间的操作。TCP Socket这个整数代表我的IP、我的TCP端口,对方的IP、对方的TCP端口,两个进程之间的连接关系的一个代表,两个端节点的代表。】

TCP服务:从一个进程向另一个进程可靠地传输字节流【传输层不保证报文和报文之间的界限】

服务器首先运行,等待连接建立

   1. 服务器进程必须先处于运行状态

  • 创建欢迎socket【目的是返回一个整数】
  • 和本地端口捆绑【创建的这个整数和本地IP端口捆绑,这个socket我们把他叫做welcome socket】【之后再调用socket API的另一个函数,叫accept,就是接受来自于这个socket、来自于远端的另外一些进程跟他的握手的关系,接受来自于其他人跟他建立TCP连接】
  • 在欢迎socket上阻塞式等待接收用户的连接【调用accept之后如果这时候没有人连接,这函数就阻塞着,就不往下走】

客户端主动和服务器建立连接:

   2. 创建客户端本地套接字(隐式捆绑到本地port)【如果不调用隐式捆绑,那么客户端返回的这个socket整数就跟默认的当前没有用的端口相捆绑】

  • 指定服务器进程的IP地址和端口号,与服务器进程连接【之后调用socket API 的另一个connect连接,指定创建的socket作为一个参数,connect之后也阻塞,除非服务器告诉他同意建立连接,如果不同意或者怎么样(同意的包发出去还没来)就阻塞他】

   3. 当与客户端连接请求到来时

  • 服务器接受来自客户端的请求,解除阻塞式等待,返回一个新的socket(与欢迎socket不一样),与客户端通信【socket API就会返回一个有效值,这时候链接就会建立起来】
    • 允许服务器与多个客户端通信
    • 使用源IP和源端口来区分不同的客户端

   4. 连接API调用有效时,客户端P与服务器建立了TCP连接

【对于服务器来说,前面在accept等待,这时候来了个连接建立请求,他的TCP一方面同意建立他连接请求,同意连接建立请求的时候,客户端他的connect这时候就会解除阻塞,返回一个有效值,另外一方面返回一个新的socket值,这个值叫connection socket,这个socket值仍然是原来的IP服务器的IP和守候端口,但是和客户端的IP和守候的端口相捆绑。所以说这个connect socket和原来的welcome socket都守候在原来的服务器端的IP和相应的端口上,但是connect socket跟服务器端本地的IP和守护的端口、客户端的IP、客户端的端口相捆绑,这时候accept接触阻塞,返回connection socket,之后连接就建立起来了,在这个连接上就可以收和发,连接建立完了之后可以把connection socket关掉,原来的welcome socket仍然在那守候着】

从应用程序的角度——TCP在客户端和服务器进程之间提供了可靠的、字节流(管道)服务

C/S模式的应用样例:

  1. 客户端从标准输入装置读取一行字符,发送给服务器
  2. 服务器从socket读取字符
  3. 服务器将字符转换为大写,然后返回给客户端
  4. 客户端从socket种读取一行字符,然后打印出来

实际上,这里描述了C-S之间交互的动作次序

C/S socket交互:TCP

【首先服务器要运行socket这个函数,socket函数返回一个整数,这个Socket是welcome socket,是一个整形变量。第一步建立一个Socket,操作系统就返回一个整数,端口号、IP地址还没有赋。Socket有一个参数指明是TCP socket还是UDP socket or IP socket。】

【第二步(服务器端)建立一个bind,创建一个整数(welcomeSocket的这个值)和sad(sad就是struct Sockaddr_int这个结构体,表示本地某个IP和端口号)相捆绑】

【第三步(服务器端)建立一个connectionSocket,在welcomeSocket上等待来自客户端的连接建立请求,如果调用accept这个socket API函数之后,没有任何一个用户跟他连接建立请求,那他就阻塞(阻塞的时候还没有connectionSocket表项)】

【第四步(客户端)创建ClientSocket,也是创建一个整数,在这个Socket当中指明是一个TCP socket,返回一个整数。在客户端不需要bind,因为客户端使用的端口号无所谓,服务器端需要bind,因为需要在这个端口等待来自客户端的连接建立请求,如果服务器端不绑定,客户端就不知道找谁】

【第五步,(客户端)有一个隐含的bind,操作系统给的bind,指明cad,struct Sockaddr_int结构体,表示客户端的IP地址和端口号,相当于客户端的ClientSocket和cad相捆绑。】

【ClientSocket还有一个状态值,目前还是一个无效的状态】

【第六步(客户端)调用connect,客户端的ClientSocket和&sad(指明了服务器端的IP地址和端口捆绑关系)相捆绑,目前客户端的表项就有了socket值、自己的IP地址和端口号、对方的IP地址和端口号。调用完了之后就阻塞他。】

【第七步,客户端调用connect完了之后应用层下面的TCP实体向服务器端发出一个连接建立请求,accept的原语就会解除阻塞,connectionSocket返回一个新的、有效的值(包括服务器端和客户端的IP和端口号,状态值是有效的),服务器端给客户端一个应答,connect返回一个有效值(解除阻塞),连接才是被真正建立起来的状态(ClientSocket的状态值变成有效的状态)】

【第八步,客户端发出一个send,客户端借助于ClientSocket作为一个参数,同时把要传送的东西也作为一个参数,之后交到下层,下层封装成一个TCP的报文段给服务器】

【第九步,在第八步之前,服务器端要调用read,用connectionSocket作为一个参数,这样客户端发出send,服务器端就可以接了】

【第十步,服务器端利用write,反转给客户端】

【在9和10之间要把小写变成大写】

【第十一步,(客户端)调用read,把ClientSocket拿来看对方转换的大写的字符串是什么,之后在终端当中显示出来,最后再close掉这个socket】

【然后服务器端close connectionSocket,然后connectionSocket表项就没有了,但是welcomeSocket还有效,等待着用户的下一个连接建立请求】

【Socket() bind() accept() socket() bind() connect() send() 都是Socket API的】

【黑色部分表示TCP交互的过程,红色部分表示应用报文交互的过程】【先发再收还是先收再发是应用协议定义的事情】

数据结构 sockaddr_in

IP地址和port捆绑关系的数据结构(标示进程的端节点)

数据结构 hostent

域名和IP地址的数据结构

作为调用域名解析函数时的参数

返回后,将IP地址拷贝到sockaddr_in的IP地址部分

例子:C客户端(TCP)

【应用程序在启动的时候必须要输入两个参数:服务器域名、服务器对应的端口号】

【第一个参数放在argv[1]当中,第二个参数放在argv[2]中,都是字符串的形式放进来。argv[0]是应用程序本身的名字、执行程序的名字】

【客户端的程序,sad是服务器端的address。意思是我是客户端,我得用一个结构体来描述服务器守护的IP和端口号】

【客户端socket:clientSocket就是一个整数】

【host是字符串,port是整型变量,这里没定义,只是个示例。host放的是服务器的名字、port放的是服务器守候的端口号】

【sad是个结构体变量,然后会被分配个内存,内存当中可能会有其他东西,所以要清0】

【清0完之后对sad的family赋值,赋Internet的地址簇】

【sad的port先把他强制转换成无符号的短整型,再把他变成网络次序(大小端的问题)】

【gethostbyname(host)是函数调用解析器,输入的是主机名,得到的是结构体的一个指针,指针位置放的是有效的IP地址】

【把指针的IP地址copy到结构体的IP地址去】

【sad的结构体变量放了服务器端的IP地址和端口号】

【之后再调用connect函数,bind是隐式的,不调用的话就默认的搞一个当前没有用的端口号、按照一定规则的。connect函数要指明对方的(服务器)IP和对方的端口,函数调用之后,TCP要发出一个连接建立请求,accept才会解除阻塞,然后connect才会返回,返回之后才会返回一个有效的值】

【gets(Sentence)从表中输入设备当中输入用户输入的字符串】

例子:C服务器(TCP)

【服务器端TCP上的应用进程只有一个参数,这个参数就是本地守候的端口号、当前应用进程守候的端口号】

【sad放的是我作为服务器本地的端节点,cad是另外一个进程的端节点的结构体变量,放的都是IP 和 port,只不过一个是服务器端,一个是客户端】

【两个socket都是整数,welcomeSocket只是说本地的IP和端口号有效,对方的IP和端口号没有,会有一个状态指明他是welcomSocket还是connectionSocket。connectionSocket有本地的、对方的IP和端口号,还有PID(进程号)】

【bind把放在sad当中的本地的IP、端口号bind在一起,这两个就有效了】

【如果我在服务别人的时候,有别的请求进来,就放在队列当中——listen,队列长度是10个,超过了10个的话就拒绝了。队列是等待连接建立的。当然也可以用其他方法把它变成多进程的】

参考资料:中科大郑烇、杨坚全套《计算机网络(自顶向下方法 第7版,James F.Kurose,Keith W.Ross)》课程 P20

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值