27TCP
TCP通信流程步骤:
服务端: 等待(被动)接收发送
1: 创建 socket: socket()
2: 绑定端口: bind()
3: 监听端口: listen()
4: 接受连接: accept()
5: 读取消息: read()
6: 发送消息: write()
7: 关闭套接字: close()
客户端:主动发送接收
1: 创建 socket: socket()
2: 连接服务端: connect()
3: 发送数据: write()
4: 接受结果: read()
5: 关闭套接字: close()
TCP通信流程图:


什么是套接字?
简单点,就是IP地址+端口号。
注意:IP地址决定往哪个主机发送,端口决定哪个程序接受
优点:
1.像操作文件描述符一样操作套接字
2.双向通信接口,起源于管道
3.比管道功能更强,应用更广泛
4.支持 read , write 等操作用于收发数据
服务端具体函数解析:
创建套接字
#include<sys/socket.h>
int socket(int domain, int type, int protocol)
返回值: 套接字
参数:
domain 协议族
type 套接字类型
protocol 套接字协议
第一参数参考值:
socket 创建套接字 --- domain
PF_UNIX, PF_LOCAL 本地通信
PF_INET IPV4协议
PF_INET6 IPV6协议
PF_IPX Novell 公司的 IPC 通信协议
PF_NETLINK 与内核间的接口
PF_X25 ITU-T X.25 / ISO-8208
PF_AX25 无线 AX.25 协议
PF_ATMPVC 访问原始ATM的PVC(永久虚连接)
PF_APPLETALK 苹果公司的 Appletalk 协议
PF_PACKET 底层包接口
第二参数参考值:
socket 创建套接字 --- type
SOCK_STREAM 提供面向连接的,有序的,可靠数据流 TCP
SOCK_DGRAM 支持数据报 UDP
SOCK_SEQPACKET 提供基于连接的有序的,可靠数据报通信
SOCK_RAW 对原始网络协议访问
SOCK_RDM 提供可靠的数据报层,不保证有序性
SOCK_PATET 已废弃
注意:
套接字 SOCKET 类型
SOCK_STREAM
流套接字:使用TCP协议。提供面向连接的,有序的,可靠的数据通信流。如 telnet, http 等
SOCK_DGRAM
数据报套接字:使用 UDP 协议。 提供无连接的,无序的,不保证可靠性的数据通信流。如 tftp, bootp 等
SOCK_RAW
原始流套接字:收发原始数据包,应用于底层协议开发,进行底层操作
只有 root 用户才有权限创建这个 socket
第三参数参考值:
socket 创建套接字 --- protocol
<netinet/in.h>
IPPROTO_IP (0): 接受所有IP数据包
IPPROTO_ICMP: ICMP 协议 (ping)
IPPROTO_TCP: TCP 协议
IPPROTO_UDP: UDP协议
IPPROTO_RAW: RAW 只能发送包,且需要自己填写IP头,计算校验和
绑定套接字
int bind(int sockfd, struct sockaddr *myAddr, socklen_t addrLen)
sockfd 套接字描述符
myAddr 主机地址
addrLen sockaddr 结构体大小
实现本机地址(协议族+IP+端口) 与 套接字绑定,收发消息即读写套接字文件描述符
sockaddr 结构体:
套接字地址
struct sockaddr
{
u_short sa_family; //协议族
char sa_data[14]; //协议地址
}
对于不同的协议族,协议地址 sa_data[14] 有不同的描述方式
AF_INET 协议族的协议地址
struct sockaddr_in
{
short sin_family; /*地址族 AF_INET*/
u_short sin_port; /*端口号*/
struct in_addr sin_addr; /*32位IP地址*/
char sin_zero[8]; /*预留*/
}
注意:一般定义sockaddr_in,填写信息,再将sockaddr_in转成sockaddr再使用在bind函数。
IP地址转换
#include<arpa/inet.h>
ulong inet_addr(char* pAddr)
字符串 IP 转 整数 IP
int inet_aton(char* pAddr, struct in_addr *pInAddr)
字符串 IP 转 整数 IP
char* inet_ntoa(struct in_addr inAddr)
整数 IP 转 字符串 IP
字节序转换
网络字节序: 大字节序
主机字节序:
x86: 小字节序
ppc: 大字节序
主机序转网络序
ulong htonl(ulong host)
ushort htons(ushort host)
网络序转主机序
ulong ntohl(ulong net)
ushort ntohs(ushort net)
监听端口
int listen(int sockfd, int backlog)
sockfd 监听的套接字
backlog 套接字接收的最大连接数,超出则向客户端发出 ECONNEREFUSED 错误
连接处理
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
阻塞,等待接收客户端连接申请
接收成功,则创建套接字,用于发送消息给客户端
addr 获取到客户端地址
addrlen 获取到客户端地址大小
sockfd设置为非阻塞的情况,未接收到连接申请,则返回错误
关闭套接字
1: close(sockfd)
同文件操作
关闭时,只是将套接字访问计数器 -1, 计数器为0时真正关闭
用于创建子进程进行并发管理
2:shutdown(sockfd, how) 按需关闭套接字
SHUT_RD (0): 关闭读功能,丢弃接收到的数据
SHUT_WR(1): 关闭写功能,不能发送数据
SHUT_RDWR(2): 彻底关闭套接字连接
服务端具体函数解析:
-----------------------创建套接字与服务端一样,参考上面--------------
int connect(int sockfd, struct sockaddr *srvaddr, int addrlen)
sockfd 创建的用于通信套接字
srvaddr 接收端地址
addrlen 地址结构体大小
发送数据
write(int sockfd, void *buf, size_t len)
send(int sockfd, void *msg, int len, int flags)
flags 标志一般填0, 特殊情况下用法如下
MSG_CONFIRM
通知链路层,即将收到回应,链路层未收到回应,则会定期探测邻居消息 (只能用于 SOCK_DGRAM 和 SOCK_RAW)
MSG_DONTROUTE
不通过网关发送数据,只发送到同一子网计算机
MSG_DONTWAIT
使用非阻塞操作,阻塞则返回EAGAIN错误
MSG_EOR
结束记录(SOCK_SEQPACKET 时使用)
MSG_MORE
还有后续数据要发送,效果相当于套接字使用了TCP_CORK属性。通知内核,这个帧的数据还没发完,后续数据发送后,这个数据才能发送出去。
MSG_OOB
发送带外数据。提高优先级,先于其他数据进行发送
接收消息
read(int sockfd, void *buf, size_t len)
recv(int sockfd, void *buf, size_t len, int flags)
flags标志一般填0, 特殊情况下用法如下
MSG_DONTWAIT
非阻塞操作,阻塞则返回EAGAIN错误
MSG_OOB
接收带外数据
MSG_PEEK
只查看消息,不从缓冲区删除数据
MSG_TRUNC
返回包的真实长度(只用于流套接字)
MSG_WAITALL
等待接收到的数据长度为len后才返回
例子:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<unistd.h>
#define SRV_PORT 8888
#define CLT_PORT 6666
void Tcp_server()
{
int fd;
int iRet;
struct sockaddr_in addr;
socklen_t addrlen = sizeof(addr);
//创建套接字
fd = socket(PF_INET, SOCK_STREAM, 0);//IPPROTO_IP
if (fd < 0)
{
perror("Socket failed!");
return;
}
//协议、端口、ip地址等属性
addr.sin_family = AF_INET;//use IPV4 address
addr.sin_port = htons(SRV_PORT);
addr.sin_addr.s_addr = htonl(INADDR_ANY);//INADDR_ANY=0L;
//把套接字和IP、端口号进行绑定
iRet = bind(fd, (struct sockaddr*)&addr, addrlen);
if (iRet)
{
perror("Bind failed!");
close(fd);
return;
}
//同时监听一个套接字
iRet = listen(fd, 1);
if (iRet)
{
perror("Listen failed!");
close(fd);
return;
}
int clientfd;
struct sockaddr_in srcaddr;
char szTip[] = "Welcome!";
char szBuf[1024]={};
char szMsg[1024];
//接受信息,确认连通,再创建通信套接字,包含客户端的IP和端口号
clientfd = accept(fd, (struct sockaddr*)&srcaddr, &addrlen);
if (clientfd < 0)
{
perror("Accept failed!");
return ;
}
else
{
printf("Connect form %s[%d]\n", inet_ntoa(srcaddr.sin_addr), ntohs(srcaddr.sin_port));
//send(clientfd, szTip, strlen(szTip), 0);
write(clientfd, szTip, strlen(szTip)); //send data to client
while(1)
{
//接受
memset(szBuf, 0, sizeof(szBuf));
//iRet = recv(clientfd, szBuf, 1024, 0);
iRet = read(clientfd, szBuf, sizeof(szBuf));
if (iRet < 0)
{
perror("Fail to read!");
break;
}
printf("\nRecv: %s", szBuf);
//发送
fprintf(stderr, "Send:");
memset(szMsg, 0, sizeof(szMsg));
read(STDIN_FILENO, szMsg, sizeof(szMsg));
write(clientfd, szMsg, strlen(szMsg));
}
}
close(clientfd);
close(fd);
return ;
}
void Tcp_client()
{
char szDestIp[16];
int port;
fprintf(stderr, "Connect to:");
scanf("%s%d", szDestIp, &port);
int fd;
int iRet;
struct sockaddr_in srvaddr;
socklen_t addrlen = sizeof(srvaddr);
fd = socket(PF_INET, SOCK_STREAM, 0);
if (fd < 0)
{
perror("Socket failed!");
return;
}
srvaddr.sin_family = AF_INET;
srvaddr.sin_port = htons((short)port);
srvaddr.sin_addr.s_addr = inet_addr(szDestIp);
iRet = connect(fd, (struct sockaddr*)&srvaddr, addrlen);
if (iRet)
{
perror("Connect failed!");
return;
}
char szBuf[1024];
char szMsg[1024];
while(1)
{
memset(szBuf, 0, sizeof(szBuf));
iRet = read(fd, szBuf, sizeof(szBuf));//receive message
if (iRet < 0)
{
perror("Read failed!");
break;
}
printf("Recv: %s\n", szBuf);
fprintf(stderr, "Send:");
memset(szMsg, 0, sizeof(szMsg));
read(STDIN_FILENO, szMsg, sizeof(szMsg));//get message
write(fd, szMsg, strlen(szMsg)); //send message
}
close(fd);
}
int main(int argc, char** argv)
{
if (argc!=2
|| (strcmp(argv[1], "s") && strcmp(argv[1], "c"))
)
{
printf("Usage: %s [ c | s ]\n", argv[0]);
printf("\t c : For start tcp client\n");
printf("\t s : For start tcp server\n");
return 0;
}
if (argv[1][0] == 's')
{
Tcp_server();
}
else if (argv[1][0] == 'c')
{
Tcp_client();
}
return 0;
}
TCP搜索端口:

搜素端口与ping的区别:
ping只能确认某IP地址是否存在,当知道IP地址,不知道端口号也无法进行通信。
27TCP的更多相关文章
- 27Tcp文件传输
前面介绍了TCP和UDP的通信,只是文体通信,只能传送文字.本次介绍文件传输,也就是文件读写和TCP通信的结合. 解析:根据之前的TCP通信,建立彼此的连接.服务器选择文件,首先将文件的基本信息发送给 ...
随机推荐
- MyBatis 3.2.6插入时候获取自增主键方法
MyBatis 3.2.6插入时候获取自增主键方法有二 以MySQL5.5为例: 方法1: <insert id="insert" parameterType="P ...
- 漫游Kafka实战篇之搭建Kafka运行环境(2)
接下来一步一步搭建Kafka运行环境. Step 1: 下载Kafka 点击下载最新的版本并解压. > tar -xzf kafka_2.9.2-0.8.1.1.tgz > cd kafk ...
- SQL语句:语法错误(操作符丢失)在查询表达式中
所谓操作符丢失,应该是你在拼接SQL语句是少了关键词或者分隔符,导致系统无法识别SQL语句.建议:1.监控SQL语句,看看哪里出现问题:断点看下最后的sql到底是什么样子就知道了,另外你可以把这段sq ...
- hdu 4003(树形dp)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4003 思路:dp[i][j]表示以i为根选择j个机器人的最小花费,然后就是背包了:dp[u][i]=m ...
- Activity 5秒 Broadcast 10秒 Service 20秒
第一:什么会引发ANR? 在Android里,应用程序的响应性是由Activity Manager和WindowManager系统服务监视的 .当它监测到以下情况中的一个时,Android就会针对特定 ...
- Jenkins publish over ssh 上传 文件夹 配置方式
需要把编译后 目录下的 lib文件夹下的jar包都上传到服务器上 1.配置 2.完成截图 3.服务器截图 注意: 真实脚本 建议写 全路径 注意:多条命令时 添加 英文分号
- 1358 棋盘游戏[状压dp]
1358 棋盘游戏 时间限制: 1 s 空间限制: 64000 KB 题目等级 : 大师 Master 题解 查看运行结果 题目描述 Description 这个游戏在一个有10*10 ...
- 【BZOJ2699】更新 动态规划
[BZOJ2699]更新 Description 对于一个数列A[1..N],一种寻找最大值的方法是:依次枚举A[2]到A[N],如果A[i]比当前的A[1]值要大,那么就令A[1]=A ...
- 170323、Spring 事物机制总结
spring两种事物处理机制,一是声明式事物,二是编程式事物 声明式事物 1)Spring的声明式事务管理在底层是建立在AOP的基础之上的.其本质是对方法前后进行拦截,然后在目标方法开始之前创建或者加 ...
- 160401、关于cronExpression的介绍
关于cronExpression的介绍: 每一个字段都有一套可以指定有效值,如 Seconds (秒):可以用数字0-59 表示, Minutes(分) :可以用数字0-59 表 ...