专题 15 TCP套接字编程
概述
存在三种套接字:流式套接字(SOCK_STREAM)、数据报套接字(SOCK_DGRAM)和原始套接字(SOCK_RAW)。
TCP套接字工作流程:
首先,服务器端启动进程,调用Socket创建一个基于TCP协议的流套接字描述符。
其次,服务进程调用bind命名套接字,将套接字描述符绑定到本地地址和本地端口上。
再次,服务器端调用listen,开始侦听客户端的Socket连接请求。
接下来,客户端创建套接字描述符,并且调用connect向服务器端提交连接请求。服务器端接收到客户端连接请求后,调用accept,接受并创建一个新的套接字描述符与客户端建立连接,然后原套接字描述符继续侦听客户端的连接请求。
客户端与服务器端新套接字进行数据传输,调用write或send向对方发送数据,调用read或recv接收数据。
在数据交流完毕后,双方调用close或者shutdown关闭套字。
socket的创建
函数原型:
intsocket(int domain, int type, int protocol);
PS:在实际编程中,我们只使用AF_INET协议,如果需要与本地主机进程建立连接,只需把远程地址设定为“127.0.0.1”即可。
创建套接实例:
创建AF_INET协议族上的流套接字描述符。
socket(AF_INET,SOCK_STREAM, 0)
或
socket(AF_INET,SOCK_STREAM, TCP)
创建AF_INET协议族上的数据报套接字描述符。
socket(AF_INET,SOCK_DGRAM, 0)
或
socket(AF_INET,SOCK_DGRAM, UDP)
socket的命名
函数bind命名一个套接字,它为该套接字描述符分配一个半相关属性,其原型如下:
intbind(int s, const struct sockaddr* name, int namelen);
套接字地址属性结构:
structsockaddr
{
u_shortsa_family; /*协议族*/
charsa_data[14] /*最多14字节的协议地址*/
}
每个协议族都定义了自己套接字属性结构,协议族AF_INET使用结构sockaddr_in描述套接字地址信息。
structsockaddr_in
{
shortsin_family; /*16位的地址协议族(AF_INET)*/
u_shortsin_port; /*16位的端口地址*/
structin_addr sin_addr; /*32位的IP地址*/
charsin_zero[8]; /*预留,保持sockaddr_in与结构sockaddr的长度相同*/
};
structin_addr
{
u_longs_addr;
}
IP地址转换:
unsignedlong inet_addr(char *ptr);
intinet_aton(char *ptr, struct in_addr *addrptr);
char*inet_ntoa(struct in_addr inaddr);//将IP地址的整数形式转换为字符串格式输出
字节顺序转换:
u_longhtonl(u_long hostlong);
u_shorthtons(u_short hostshort);
u_longntohl(u_long netlong);
u_shortntohs(u_short netshort);
“h”代表“host”,代表主机字节序
“n”代表“network”,代表网络字节序。
“l”代表“long”,代表32位整数
“s”代表“short”,代表16位整数
例子:命名套接字描述符s的协议为AF_INET,地址由系统自动指定,端口号为1000.
structsockaddr_in sockaddr1; /*申请地址结构空间*/
memset(&sockaddr,0, sizeof(sockaddr)); /*清空空间*/
sockaddr1.sin_family= AF_INET; /*指定为AF_INET协议族*/
sockaddr1.sin_addr.s_addr= htonl(INADDR_ANY); /*任何IP地址*/
sockaddr1.sin_port= htons(10000); /*端口号*/
bind(s,(struct sockaddr *)&sockaddr1, sizeof(sockaddr1));
socket的侦听
函数原型:
intlisten(int s, int backlog);
PS:listen仅在流套接字中使用。上述参数分别表示套接字描述符和套接字接受连接的最大数目。
例:服务器端套接字s进入侦听,最多只能接收客户端的10条申请。
listen(s,10);
/*TCP服务器端套接字准备函数*/
intCreateSock(int *pSock, int nPort, int nMax)
{
structsockaddr_in addrin;
structsockaddr *paddr = (struct sockaddr*)&addrin;
ASSERT(pSock!= NULL && nPort > 0 && nMax > 0);
memset(&addrin,0, sizeof(addrin));
addrin.sin_family= AF_INET;
addrin.sin_addr.s_addr= htonl(INADDR_ANY);
addrin.sin_port= htons(nPort);
ASSERT((*pSock=socket(AF_INET,SOCK_STREAM,0)) > 0);
if(VERIFY(bind(*pSock,paddr,sizeof(addrin))>= 0)&& VERIFY(listen(*pSock,nMax) >=0))
return0;
VERIFY(close(*pSock)== 0);
return1;
}
Socket的连接处理
函数原型:
intaccept(int s, struct sockaddr *addr, int *addrlen);
/*TCP服务器端接收连接函数*/
intAcceptSock(int *pSock, int nSock)
{
structsockaddr_in addrin;
intlSize;
ASSERT(pSock!= NULL && nSock > 0);
while(1)
{
lSize= sizeof(addrin);
memset(&addrin,0, sizeof(addrin));
if((*pSock= accept(nSock,(structsockaddr*)&addrin,&lSize))> 0)
return0;
elseif(errno == EINTR)
continue;
else
ASSERT(0);
}
}
socket的关闭
函数原型:
intshutdown(int s, int how);
函数shutdown可以全部关闭或者部分关闭套接字描述符s的连接,参数how指定了套接字关闭的方式,取值含义如下:
-
How取值
描述
0
套接字不可读,系统将自动丢弃接收到的数据和留在缓冲区中的数据,进程不能再从套接字中接收通信数据
1
套接字不可写,系统将写缓冲区的数据发送完毕后关闭套接字写操作,进程不能再从套接字中发送通信数据
2
彻底关闭套接字的连接
shutdown是强制性关闭全部套接字连接,而函数close只将套接字访问计数器减1,当且仅当计数器值为0时,系统才真正地关闭套接字通信。
PS:利用close的这个特性,可以建立Socket通信的服务器端的并发管理:父进程首先创建侦听套接字,一旦接收到连接请求则创建新的套接字与客户连接,再fork子进程,随后父进程调用close关闭新创建的套接字后继续侦听,子进程则调用close关闭侦听套接字,全权负责与客户端的通信。
Socket的连接申请
函数原型:
intconnect(int s, const struct sockaddr *name, int namelen);
/*********TCP客户端函数**********/
intConnectSock(int *pSock, int nPort, char *pAddr)
{
structsockaddr_in addrin;
longlAddr;
intnSock;
ASSERT(pSock!= NULL && nPort > 0 && pAddr != NULL);
/***创建TCP套接字描述符***/
ASSERT((nSock= socket(AF_INET, SOCK_STREAM, p)) > 0);
/***协议地址组包*****/
memset(&addrin,0, sizeof(addrin));
addrin.sin_family= AF_INET;
addrin.sin_addr.s_addr= inet_addr(pAddr);
addrin.sin_port= htons(nPort);
if(VERIFY(connect(nSock,(struct sockaddr *)&addrin, sizeof(addrin)) > = 0))
{
/***连接成功,返回套接字描述符***/
*pSock= nSock;
return0;
}
close(nSock);
return1;
}
TCP数据的发送
套接字一旦连接上,就可以发送数据。
函数原型:
intsend(int s, const void *msg, int len, int flags);
flags标志:
MSG_OOB:发送外带数据
MSG_DONTROUTE:通知远程IP就在本地局域网内,消息中不加入跌幅消息
TCP数据的接收
函数原型:
intrecv(int s, void *buf, int len, int flags);
flags标志:
MSG_OOB:接收外带数据
MSG_PEEK:以窥视方式接收数据,即只接收而不从缓冲区中删除数据,下一次调用recv或read仍然可以接收这些数据
MSG_WAITALL:函数阻塞直到读取len字节数为止。不过本地标志并并非完全阻塞,当进程接收到信号,套接字出错、连接中断或指定了MSG_PEEK等情况出现时函数仍然会提前返回
其他一些相关函数
structhostent *gethostbyname(const char *name);
structhostent *gethostbyaddr(const char *addr, int len, int type);
voidherror(const char *string);//不能使用perror
structservent *getservbyname(const char *name, const char *proto);
structservent *getservbyport(int port, const char *proto);
intgetsockname(int s, struct sockaddr *name, int *namelen);
intgetpeername(int s, struct sockaddr *name, int *namelen);
套接字选项
函数原型:
intgetsockopt(int s, int level, int optname, void *optval, int *optlen);
intsetsockopt(int s, int level, int optname, const void *optval, intoptlen);
相关选项说明:
SO_DEBUG
本选项只支持TCP协议,当选项打开时,系统内核跟踪套接字发送和接收的全部TCP数据记录。
SO_REUSEADDR和SO_REUSEPORT
当尝试将一个套接绑定到某个端口时,如果该端口已经被占用了,一般情况下,绑定会失败,如果设置了此选项,系统能够让套接字bind到正在使用的地址或端口上。
SO_KEEPALIVE
本选项是周期性地测试套接字连接是否依然存在。
SO_DONTROUT
本选项控制发送消息是否越过协议的路由机制,打开时套接字将绕过协议的路由机制发送的数据。
SO_LINGER
当缓冲区中还有数据尚未发送时,套接字的默认关闭机制是系统将这些数据立即发送对方,但不等待对方确认接收马上关闭套接字。SO_LINGER可以改变这个设置,它的数据传递通过结构linger完成。linger的结构定义如下:
structlinger
{
intl_onoff;//选项开关
intl_linger;//延时时间
}
结构linger成员取值含义如下:
-
L_onoff
L_linger
关闭机制
0
忽略
默认关闭机制
非0
0
直接丢弃缓冲区,立即关关套接字
非0
非0
等待缓冲区数据发送完毕或者延时l_linger秒后关闭套接字。如果套接字非阻塞选项打开,则立即关闭
SO_BROADCAST
控制能否发送套接字数据广播,只用于数据报套接字和支持广播信息的网络上。
SO_OOBINLINE
此选项打开时允许外带数据留在输入队列中,此时函数read和recv可以在不指明MSG_OOB标志的情况下读取外带数据。
SO_SNDBUF和SO_RCVBUF
每个套接都有一个发送缓冲区和接收缓冲区,这两个缓冲区由底层的协议使用,选项SO_SNDBUF可以改变发送缓冲区的大小,选项SO_RCVBUF可以改变接收缓冲区的大小。客户端必须在connect前设置SO_RCVBUF选项,服务器端必须在listen前设置SO_RCVBUF选项,才可以有效地更改缓冲区的容量大小。
SO_SNDLOWAT和SO_RCVLOWAT
这两个选项分别可以改变最小发送数据量和最小接收数据量。
SO_SNDTIMEO和SO_RCVTIMEO
这两个选项用于设置发送数据和接收数据设置一个超时时间。超时时间采用结构timeval描述。
SO_TYPE
调用本选项可以获取套接字的类型,返回值为SOCK_STREAM、SOCK_DGRAM或SOCK_RAW等,此选项不能用于setsockopt中。
SO_ERROR
调用本选项可以获取并且清除套接字错误,不能应用于函数setsockopt中。
转载于:https://my.oschina.net/fuyajun1983cn/blog/263937
专题 15 TCP套接字编程相关推荐
- 套接字编程---2(TCP套接字编程的流程,TCP套接字编程中的接口函数,TCP套接字的实现,TCP套接字出现的问题,TCP套接字多进程版本,TCP套接字多线程版本)
TCP模型创建流程图 TCP套接字编程中的接口 socket 函数 #include <sys/types.h> /* See NOTES */ #include <sys/sock ...
- tcp套接字编程模型
1. tcp套接字编程 用下面的一张图可以清楚表示: 下面的python实现也很清晰: server: def tcplink(sock, addr):print 'Accept new connec ...
- 《计算机网络:自顶向下方法(原书第6版)》一2.7 TCP套接字编程
本节书摘来华章计算机<计算机网络:自顶向下方法(原书第6版)>一书中的第2章 ,第2.7节,(美)James F.Kurose Keith W.Ross 著 陈 鸣 译 更多章节内容可以访 ...
- 《网络编程》基本 TCP 套接字编程
在进行套接字编程之前必须熟悉其地址结构,有关套接字的地址结构可参考文章<套接字编程简介>.基于 TCP 的套接字编程的所有客户端和服务器端都是从调用socket 开始,它返回一个套接字描述 ...
- 计算机网络实验4 - TCP套接字编程 - 点对点聊天 - 代码实现
客户端 package chat;import java.io.IOException; import java.io.PrintStream; import java.net.Socket; imp ...
- 《UNIX网络编程 卷1:套接字联网API》学习笔记——基本TCP套接字编程
UNIX网络编程--基本TCP套接字编程 socket 函数 connect 函数 bind 函数 listen 函数 accept 函数 fork 和 exec 函数 并发服务器 close 函数 ...
- UNIX环境编程(c语言)--套接字--基本TCP套接字编程
目录 准备知识 字节序 字节序转换函数 字节操纵函数 地址转换函数 地址结构 基本TCP套接字编程 概要 socket函数 bind函数 listen函数 accept函数 connect函数 通信函 ...
- Experiment 0x1:TCP套接字编程
Experiment 0x1:TCP套接字编程 文章目录 Experiment 0x1:TCP套接字编程 0x0 说明 0x1 要求 0x2 实现 0x3 源码 1- TCP服务端源码 2- TCP客 ...
- TCP套接字编程(C语言)
本篇主要内容介绍: 介绍TCP套接字基本概念 介绍TCP套接字编程流程 基本TCP套接字函数介绍 1.TCP套接字基本概念(非官方解释,个人总结) 套接字是一种网络API,提供一种进程间的通信方法,使 ...
最新文章
- SQL Server Integration Services 包的开发与部署初探
- 爬虫请求库 requests
- c++ 或者 vc++中判断程序实例是否运行
- 原则,策略,规范也是构架的一部分
- CentOS,重启的常用命令
- 如何高效率学Web前端 怎么规划前端学习路线
- AOS编排语言系列教程(三):创建子网Subnet
- Kaggle新手入门之路(完结)
- C#面向对象 基础概念二十五个 (很基础,必须掌握的知识)
- Top 10 Security Issue Solution
- 微软卸载工具msicuu2(附带资源)
- 软件开发延期引发纠纷-律师随笔
- 华为RH5885H V3服务器采集日志
- 装了mysql电脑黑屏怎么办_电脑黑屏的原因,教你解决黑屏
- 【英语:语法基础】C4.日常对话-餐饮专题
- JAVA系列 IO流 知识结构整理 建立合理的知识框架逻辑 输入输出流 理顺你的思维架构Fileoutput inputStream FileWriter FIleReader结构整理
- 计算机病毒考试及答案,计算机病毒类考试题目以及参考答案.doc
- MATLAB中的一些函数
- 声音发生器、pwm、占空比
- python画生日_使用PYTHON制作一个生日查看器