Linux之—网络通信(TCP)
来源:互联网 发布:c语言修改文件内容 编辑:程序博客网 时间:2024/05/17 09:35
TCP的上一层是应用层,TCP向应用层提供可靠的面向对象的数据流传输服务,TCP数据传输实现了从一个应用程序到另一个应用程序的数据传递。它能提供高可靠性通信(即数据无误、数据无丢失、数据无失序、数据无重复到达的通信。),应用程序通过向TCP层提交数据接发送/收端的地址和端口号而实现应用层的数据通信。
通过IP的源/目的可以惟一地区分网络中两个设备的连接,通过socket的源/目的可以惟一地区分网络中两个应用程序的连接。
三次握手
TCP是面向连接的,所谓面向连接,就是当计算机双方通信时必需先建立连接,然后进行数据通信,最后拆除连接三个过程。TCP在建立连接时又分三步走:
第一步(A->B):主机A向主机B发送一个包含SYN即同步(Synchronize)标志的TCP报文,SYN同步报文会指明客户端使用的端口以及TCP连接的初始序号;
第二步(B->A):主机B在收到客户端的SYN报文后,将返回一个SYN+ACK的报文,表示主机B的请求被接受,同时TCP序号被加一,ACK即确认(Acknowledgement)。
第三步(A->B):主机A也返回一个确认报文ACK给服务器端,同样TCP序列号被加一,到此一个TCP连接完成
TCP/IP核心协议
三次握手
UDP
UDP即用户数据报协议,是一种面向无连接的不可靠传输协议,不需要通过3次握手来建立一个连接,同时,一个UDP应用可同时作为应用的客户或服务器方。
由于UDP协议并不需要建立一个明确的连接,因此建立UDP应用要比建立TCP应用简单得多。UDP比TCP协议更为高效,也能更好地解决实时性的问题,如今,包括网络视频会议系统在内的众多的客户/服务器模式的网络应用都使用UDP协议。
TCP/IP核心协议
协议选择
协议的选择应该考虑到数据可靠性、应用的实时性和网络的可靠性。
对数据可靠性要求高的应用需选择TCP协议,而对数据的可靠性要求不那么高的应用可选择UDP传送。
TCP协议中的3次握手、重传确认等手段可以保证数据传输的可靠性,但使用TCP协议会有较大的时延,因此不适合对实时性要求较高的应用;而UDP协议则有很好的实时性。
网络状况不是很好的情况下需选用TCP协议(如在广域网等情况),网络状况很好的情况下选择UDP协议可以减少网络负荷。
Socket是一个网络接口,不同的协议之间的差异性操作,则交给socket自行解决
是一种特殊的IO接口,是一种文件描述符;
是一种常用的进程之间的通信,本地,不同主机之间通信;
Socket可用网络地址结构
{协议,本地地址,本地端口}表示
套接字类型:
1.流式套接字SOCK_STREAM
流式的套接字可以提供可靠的、面向连 接的通讯流。它使用了TCP协议。TCP保证了数据传输的正确性和顺序性;
2. 数据报套接字SOCK_DGRAM
数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证可靠,无差错,它使用数据报协议UDP。
3. 原始套接字SOCK_RAM
原始套接字允许对低层协议如IP或ICMP直接访问,主要用于新的网络协议的测试等。
对底层协议进行访问,不方便,对一些协议开发。
地址结构:
struct sockaddr_in
{
short int sin_family; /* Internet地址族 */
unsigned short int sin_port; /* 端口号 */
struct in_addr sin_addr; /* IP地址 */
unsigned char sin_zero[8]; /* 填0 */
};
编程中一般并不直接针对sockaddr数据结构操作,而是使用与sockaddr等价的sockaddr_in数据结构
现在让我们来看一下一个基于TCP/IP的服务器的流程
1. 创建一个socket,用函数socket()
2. 绑定IP地址、端口等信息到socket上,用函数bind()
3.设置允许的最大连接数,用函数listen()
4.接收客户端上来的连接,用函数accept()
5.收发数据,用函数send()和recv(),或者read()和write()
6.关闭网络连接
基于TCP/IP的客户端
1.创建一个socket,用函数socket()
2.设置要连接的对方的IP地址和端口等属性
3.连接服务器,用函数connect()
4.收发数据,用函数send()和recv(),或者
read()和write()
5.关闭网络连接
1、socket()
函数的作用:创建一个socket()套接字
函数的头文件:#include <sys/socket.h>
函数的原型:int socket(int domain, int type, int protocol);
函数的参数:
Domain:表示使用何种地址类型
AF_INET:IPV4网络络协议
AF_INET6:IPV6网络协议
Type:SOCK_STREAM,TCP:面向数据流的
SOCK_DGRAM,UDP:使用不连续不可信赖的数据包连接
SOCK_RAW 提供原始网络协议
Protocol:指定传输协议编号,一般设为0即可
函数的返回值:
成功:返回套接字描述符
出错:-1
2、bind()
函数的作用:绑定IP地址
函数的原型:int bind(int sockfd, struct sockaddr * hostaddr, int addrlen)
函数的头文件:#include <sys/types.h>、#include <sys/socket.h>
函数的参数:
Sockfd:socket套接字描述符
Hostaddr:主机的地址
Addrlen:sockaddr的地址长度
Struct sockaddr_in{
Unsigned short sin_family; 网络协议
Uint6_t sin_port; 端口号
Struct in_addr sin_addr; 地址
Unsigned char size_zero[8]; 未使用
};
3、listen()
函数的作用:监听网络、等待连接
函数的原型:int listen(int sockfd, int backlog)
函数的参数:
Sockfd:套接字描述符
Backlog:允许接入的客户端的数目
函数的返回值:
成功:返回0,
出错:返回 -1
*注意:listen并没有连线,只是设置socket的listen模式,真正连线的是accept。
4、accept()
函数的作用:接受网络连接,客户端的连接(这是三次握手的地方)
函数的原型:int accept(int sockfd, struct sockaddr *addr, int *addrlen);
函数的参数:
Sockfd:套接字描述符
Addr:连接成功、填充远端客户机的地址
Addrlen:struct sockaddr的长度
函数的返回值:
成功:返回新的套接字描述符(new_sockfd)....与之前的描述符不同
出错:返回 -1
5、send()
函数的作用:经过socket传送数据,向对方发送数据
函数的原型:int send(int new_sockfd, const void *msg, int len, unsigned int flags)
函数的参数:
New_sockfd:由accept建立起来的socket连接描述符,连接远方的IP地址
Msg:发送的数据
Len:发送的数据长度
Flags:一般设为0
函数的返回值:
成功:实际传送出去的字节数
出错:返回 -1.
6、recv()
函数的作用:经过socket接受数据
函数的原型:int recv(int new_sockfd, void *buf, int len, unsigned flags)
函数的参数:
New_sockfd:accept以后的socket套接字描述符
Buf:存放地址
Len:接收数据的长度
Flags:一般设为0
函数的返回值:
成功:返回接收的字节数
出错:返回 -1.
7、connect()
函数的作用:建立socket连接,通常客户端连接服务器使用
函数的原型:int connect(int sockfd, struct sockaddr *serv_addr, int addrlen)
函数的参数:
Sockfd:socket套接字描述符
Serv_addr:表示要连接的服务器的地址
Addrlen:struct sockaddr的长度
函数的返回值:
成功:返回0
出错:返回 -1
8、sendto()
函数的作用:传送socket数据,一般UDP使用
函数的原型:int sendto(int sockfd, const void *msg, int len, unsigned int flags, const struct sockaddr *todaar, int tolen)
函数的参数:
Sockfd:套接字描述符
Msg:发送的消息内存
Len:消息长度
Toaddr:要发送消息的目的地址
Tolen:sizeof(struct sockaddr)
函数的返回值:
成功:返回实际传送的字节数
出错:返回 -1
9、recvfrom()
函数的作用:从socket接收数据
函数的原型:int recvfrom(int sockfd, void *buf, int len, unsigned int flags, struct sockaddr * fromaddr, int fromlen)
函数的参数:同sendto 一样
函数的返回值:
成功:返回实际接收的字节数
出错:返回 -1.
常用的字节序的转化函数:
函数的头文件:#include <arpa/inet.h>
从主机发送到网络:
Uint32_t htonl(uint32_t hostint32); 32位数据传送,从主机到网络。
Uint16_t htons(uint16_t hostint16); 16位数据传送,从主机到网络.
从网络到主机:
Uint32_t ntohl(uint32_t netint32); 32位的数据接收,从网络到主机
Uint16_t ntohs(uint16_t netint16); 16位的数据接收,从网络到主机
实例代码:
/**********TCPserver.c***************************/
#include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <netdb.h> #include <sys/types.h> #include <netinet/in.h> #include <sys/socket.h> #define portnumber 3333int main(int argc, char *argv[]) { int sockfd,new_fd; struct sockaddr_in server_addr; struct sockaddr_in client_addr; int sin_size; int nbytes;char buffer[1024]; //1. socketif((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1) // AF_INET:IPV4;SOCK_STREAM:TCP{ fprintf(stderr,"Socket error:%s\n\a",strerror(errno)); exit(1); } //2.bindbzero(&server_addr,sizeof(struct sockaddr_in)); // 初始化,置0server_addr.sin_family=AF_INET; // Internetserver_addr.sin_addr.s_addr=htonl(INADDR_ANY); // (将本机器上的long数据转化为网络上的long数据)和任何主机通信 //INADDR_ANY 表示可以接收任意IP地址的数据,即绑定到所有的IP//server_addr.sin_addr.s_addr=inet_addr("192.168.1.1"); //用于绑定到一个固定IP,inet_addr用于把数字加格式的ip转化为整形ipserver_addr.sin_port=htons(portnumber); // (将本机器上的short数据转化为网络上的short数据)端口号 // server_addr.sin_addr.s_addr=inet_addr("192.168.1.199");//;htonl(INADDR_ANY);/* 捆绑sockfd描述符到IP地址 */ if(bind(sockfd,(struct sockaddr *)(&server_addr),sizeof(struct sockaddr))==-1) { fprintf(stderr,"Bind error:%s\n\a",strerror(errno)); exit(1); } //3.listen if (listen(sockfd, 5)==-1) { printf("listen error\n"); } while(1) { sin_size=sizeof(struct sockaddr_in); printf("1\n"); //4.accept if( new_fd = accept(sockfd, (struct sockaddr *)(&client_addr), &sin_size) <0) {printf("accept error.\n"); } fprintf(stderr,"Server get connection from %s\n",inet_ntoa(client_addr.sin_addr)); //5.recv/sendif((nbytes=read(new_fd,buffer,1024))==-1) { fprintf(stderr,"Read Error:%s\n",strerror(errno)); exit(1); } buffer[nbytes]='\0';printf("Server received %s\n",buffer);/* 这个通讯已经结束 */ close(new_fd); /* 循环下一个 */ } /* 结束通讯 */ close(sockfd); exit(0); }
/********************TCPclient.c****************************/
#include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <netdb.h> #include <sys/types.h> #include <netinet/in.h> #include <sys/socket.h> #define portnumber 3333int main(){ int sockfd; struct sockaddr_in serv_addr; int addlen=sizeof(struct sockaddr); char buf[512]="hello world"; //1.socket if ((sockfd = socket(AF_INET, SOCK_STREAM, 0))==-1) {printf("socket error"); } bzero(&serv_addr,sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(portnumber); serv_addr.sin_addr.s_addr=inet_addr("192.168.1.199"); //2.connect if(connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr)) <0) {printf("connect error\n"); } //3.send if( send(sockfd, buf, strlen(buf), 0 ) <0) { printf("send error\n"); } //4.close close(sockfd);}
- Linux之—网络通信(TCP)
- Linux之—网络通信(TCP)
- Linux之—网络通信(TCP/IP)
- linux网络通信之tcp
- Linux网络编程之TCP通信
- Linux编程网络之TCP通信
- 网络通信之TCP
- Linux网络编程——TCP通信
- Linux网络通信TCP/IP
- linux TCP网络通信过程
- linux网络通信-----TCP协议
- linux c之网络编程之TCP(服务器和和客户端基础通信)
- linux 网络编程之最简单的tcp通信服务端
- linux 网络编程之最简单的tcp通信客户端
- 网络编程之TCP通信
- 网络通信之tcp编程
- linux网络编程之用socket实现简单客户端和服务端的通信(基于TCP)
- Linux学习(二十七):TCP/IP网络编程之本地通信
- POJ 1739 Tony's Tour
- kNN(K-Nearest Neighbor)最邻近规则分类
- ubuntu 14.04可用源
- 使用pip更新whl 报错Fatal error in launcher: Unable to create process using '"'
- 类型转换
- Linux之—网络通信(TCP)
- jQuery实现表格隔行变色
- 剑指Offer(面试题47、48)
- 洛谷1525 关押罪犯
- 业内公认难题,如何解决小文件存储
- Android学习之路之Android基本知识
- 欢迎使用CSDN-markdown编辑器
- Linux的基础
- AngularJS学习笔记