16 ---TPC的简单编程

一. TCP的编程模型

案例1:

       TCP的服务器(在案例中使用浏览器作为客户程序,访问服务器程序)

/// tcp_server.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

main()
{
	int serverfd;
	int cfd;

	int r;
	
	struct sockaddr_in sadr; // 服务器地址
	struct sockaddr_in cadr; // 用户地址
	socklen_t len;
	
	// 1. socket
	serverfd = socket(AF_INET,SOCK_STREAM,0);//数据为流
	if(serverfd == -1) printf("1:%m\n"),exit(-1);
	printf("建立服务器socket成功!\n");
	// 2. bind
	sadr.sin_family = AF_INET;
	sadr.sin_port = htons(9999); // 端口号
	inet_aton("120.6.20.247",&sadr.sin_addr);
	
	r = bind(serverfd,(struct sockaddr*)&sadr,sizeof(sadr));
	if(r==-1) printf("2:%m\n"),exit(-1);
	printf("服务器地址绑定成功!\n");
	// 3. listen 
	r = listen(serverfd,5); // 队列中可以容纳未处理连接的最大数目
	if(r==-1) printf("3:%m\n"),exit(-1);
	printf("监听服务器成功!\n");
	// 4. accept
	len = sizeof(cadr);
	cfd = accept(serverfd,(struct sockaddr*)&cadr,&len);
	printf("有人连接:%d,IP:%s,d端口:%u\n",
	cfd,inet_ntoa(cadr.sin_addr),ntohs(cadr.sin_port));
	// 5. 处理代理客户描述符号的数据
	while(1)
	{
		char buf[1024+1];
		r = recv(cfd,buf,1024,0);
		//r = recvfrom(cfd,buf,100,0,0,0);
		if(r>0)
		{
			buf[r] = 0;
			printf("::%s\n",buf);
		}
		if(r==0)
		{
			printf("连接断开!\n");
			break;
		}
		if(r==-1)
		{
			printf("网络故障!\n");
			break;
		}
	}
	close(cfd);
	close(serverfd);
}

--------------------------------

$ netstat -tn   ----------查看网络使用情况

打开浏览器

http://120.6.20.247:9999/index.html   -------- 注意这里的IP地址,端口号与程序中要一致。



补充:

      socket 建立服务器的文件描述符号缓冲

      bind 把地址IP地址与端口设置到文件描述符号

      listen 负责根据客户连接的不同IP与端口,生产对应的文件描述符号及其信息。

     accept :一旦listen有新的描述符号产生就返回,否则阻塞

二. TCP通信特点 (相对于UDP)

使用的是TCP协议的特点

              有连接: 只要连接后,发送数据不用指定IP 与 端口

              数据无边界: TCP 数据流, 非数据报文。

             描述符号双工。

              数据准确:TCP 协议保证数据完全正确。

使用TCP发送数据注意

           不要以为固定的数据一定接收正确,要求使用MSG_WATIALL

            recv(),对固定长度的数据使用MSG_WAITALL

            MSG_WATIALL  (将缓冲填满,使程序没有问题,这也是程序容易出现问题的原因)

案例(比较重要的一个案例):

          使用TCP传送文件(首先在TCP传送中数据是没有边界的)

           需要定义文件数据包

                  int   数据的大小

                  char[ ]     数据内容

            传送文件名

            传送数据

             传送0长度的数据表示文件结束。

补充:

(1)send函数


       ssize_t  send(int sockfd,       ///发送socket文件描述符

                             const void *buf,  ///想要发送数据的数据缓冲

                            size_t len,    /// 实际要发送的字节数

                            int flags);    一般置0

(2)IP的一些协议

$cd /etc/

#vi protocols


/// client.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>

main()
{
	int sfd;
	int ffd;
	int size;
	int r;
	char buf[128];
	int len;
	struct sockaddr_in dr; // 保存IP的
	char filename[] = "udp_a.c";
	
/// 1. 建立socket
	sfd = socket(AF_INET,SOCK_STREAM,0);
	if(sfd == -1) printf("socket err:%m\n"),exit(-1);
	printf("建立socket成功!\n");
	
/// 2. 连接到服务器
	dr.sin_family = AF_INET; // 地址协议 
	dr.sin_port = htons(9988); // 端口
	inet_aton("120.6.20.247",&dr.sin_addr);
	r = connect(sfd,(struct sockaddr*)&dr,sizeof(dr));
	if(r==-1) printf("connect err:%m\n"),close(sfd),exit(-1);
	printf("connect 成功!\n");
/// 3. 打开文件
	ffd = open(filename,O_RDONLY); 
	if(ffd == -1) printf("open err:%m\n"),close(sfd),close(ffd),exit(-1);
	printf("open 文件成功!\n");
// 4. 发送文件名
	len = strlen(filename);
	r = send(sfd,&len,sizeof(len),0); /// 发送文件名长度
	r = send(sfd,filename,len,0); // 发送文件名
	if(r == -1) printf("send err:%m\n"),close(ffd),close(sfd),exit(-1);
	printf("发送文件名成功!\n");
// 5. 循环发送数据
	while(1)
	{
		size = read(ffd,buf,128);
		if(size == -1) break;
		if(size == 0)  break;
		if(size > 0)
		{   /// 发送一个数据包时,首先要发送数据包的长度,然后在发送数据,
			r = send(sfd,&size,sizeof(size),0);// 发送数据长度
			 if(r == -1) break;   
			r = send(sfd,buf,size,0); // 发送数据
		}	
	}
// 6. 读取到文件尾,发送0数据包
	size = 0;
	r = send(sfd,&size,sizeof(size),0);
	close(ffd);
	close(sfd);
	printf("OK!\n");
}

// server.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>

main()
{
	int sfd,cfd,ffd;
	int r;
	int len;
	char buf[128];
	char filename[100];
	struct sockaddr_in dr;
	
// 1. 建立服务器socket
	sfd = socket(AF_INET,SOCK_STREAM,0);
	if(sfd == -1) printf("socket err:%m\n"),exit(-1);
	printf("建立服务器成功!\n");
// 2.绑定IP 地址与端口
	dr.sin_family = AF_INET;
	dr.sin_port = htons(9988);
	dr.sin_addr.s_addr = inet_addr("120.6.20.247");
	r = bind(sfd,(struct sockaddr*)&dr,sizeof(dr));
	printf("绑定地址成功\n");
// 3. 监听
	r = listen(sfd,10);
	if( r == -1) printf("listen err:%m\n"),close(sfd),exit(-1);
	printf("监听成功!\n");
// 	4. 接收,连接
	cfd = accept(sfd,0,0);
	if(cfd == -1) printf("accept err:%m\n"),close(sfd),exit(-1);
	printf("开始接收文件名...\n");
// 5. 接收文件名
	r = recv(cfd,&len,sizeof(len),MSG_WAITALL);//接收文件名长度,保存在len中
	printf("文件名长度:%d\n",len);
	r = recv(cfd,filename,len,MSG_WAITALL);//接收文件名,保存在filename
	filename[len] = 0;
	printf("传递的文件是:%s\n",filename);
// 6 创建文件
	ffd = open(filename,O_RDWR | O_CREAT,0666);//创建的文件名就是刚才接收保存在filename中的文件名
	if(ffd == -1) printf("open err:%m\n"),close(sfd),close(ffd),exit(-1);
	printf("创建文件成功!");
// 7. 循环接收文件
	while(1)
	{
		r = recv(cfd,&len,sizeof(len),MSG_WAITALL);// 接收数据包的长度
		if(len == 0) break; // 当长度为0时,接收完毕,退出
		r = recv(cfd,buf,len,MSG_WAITALL);// 接收数据包数据内容
		write(ffd,buf,len);// 把接收的数据包内容写入刚才创建的文件中
	}
// 8. 关闭文件
	close(ffd);
	close(cfd);
	close(sfd);
	printf("接收文件成功!\n");
}

说明:
      开两个终端,可以把服务器程序移到上级目录进行编译运行,

     客户端程序在当前目录运行,可以用当前一个现有的文件进行传输。

注意:

        客户端与服务器的IP地址与端口的一致性,这是能传输的关键因素。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值