网络编程如TCP socket编程 位于应用层跟传输层之间, 通过socket编程让人们解脱与传输层到网络层跟链路层的传输, 我们只需要设置好socket编程接口就好,
1.socket 建立socket编程对象, 设置好是IPV4 还是IPV6 以及是TCP 还是UDP 传输等属性
2.bind 为我们建好的socket绑定好IP地址(用来区分计算机) 、端口号(用来区分到底是目标计算机的那个应用程序,数据传输过来时先被目标电脑的NIC卡网络接口卡接收,里面含有端口号,一个套接字对于一个端口号这样才可以找到应用程序,端口号是16位构成 0-65535,但0-1023一般系统用了。虽然不同TCP 连接端口号不能重复,但是TCP跟UDP传输可以用同一个端口号),计算机想网络通信必须要有自己的唯一IP地址,交换机连接局域网(LAN)内多个节点,用于局域网内的数据交换,也就是组建局域网用。 路由器则是连接局域网和广域网(WAN)的,用于不同网段之间的数据交换,局域网内各节点都是通过路由器和外网进行信息交换的。现在的简易路由器通常都还带有交换机(确切讲是集线器)的功能(1个WAN口 + 4个LAN口)如宿舍常用的TPLink。 一般我们用系统自带的设置好IP跟Port,
CPU 在内存保存数据时 有大端序 高位字节存放到低位地址。 小端序 高位字节存放到高位地址 俩中方式。 网络传输约定用大端序。
字节序转换, htons ntohs n代表network网络 h待变host主机 h to n short htonl ntohl
根据数据传输方式不同,TCP 流传输,面向连接的传输。 UDP 面向信息的传输。 TCP/IP协议族设计到理论7层实际4层 由上到下很多层面的知识,为了传输每层实现规范化的接口编程。
链路层 定义实现网卡跟LAN WAN 等网络标准。 IP层 面向消息不可靠传输自动规划路线进行传输,
TCP/UDP层 IP层只是负责数据传输但是无安全跟顺序保证 简言之就是只管努力送到,为了保证安全引入了 TCP 三次握手四次挥手 数据重传
应用层 上面的三层都是在套接字内部实现的,网络编程大部分内容也是关于应用层的编写实现。
3.listen 进入等待连接请求状态, 服务器端 调用了listen 客户端才能调用connect函数, 服务器处于等待连接请求状态 4.accept 接收client端socket请求,accept()函数返回值为自动创建的socket 用来进行数据I/O 操作,
有时为了服务器端一直可以接收数据可以用while(1)循环 accept后面部分, 实现循环回声客户端,但由于是Client发给server端 要等待server端传回来数据,等待多久? 我们可以先接收数据的大小 然后没接收全的时候 一直接受ing。
TCP (流控制是关键 一对一) 会自动控制数据流大小,有滑动窗口协议,我们write时 是把数据输到缓冲区 。TCP三次握手四次挥手 SYN ACK 。而关于UDP传输没有listen 跟accept 步骤。用sendto 跟recvfrom实现数据传输。TCP时client端用connect实现IP port的连接,UDP的时在sendto 设置IP port 如果没有则自动分配, UDP传输发几次就要收几次。UDP 虽然没有listen跟accept但是每次用时都要给套接字注册IP Port 然后传输data 删除注册的IP Port 。 为了减少耗时我们可以自己给UDP设置创建已经连接UDP 套接字。
Linux 调用close Windows调用closesocket 都是直接把socket建立的双向通道断开了,为了优雅断开引入Half-close半关闭也就是Server端发送完数据了 如果client直接close client发送的感谢言语都无法再传给server, 而如果我们只调用半关闭 让server无法发数据可以接收 还可以收到感谢信。半关闭函数为shutdown 函数 可以指定socket跟关闭方式。
DNS是IP地址和域名进行相互转换的系统,核心是DNS服务器。ping www.baidu.com 出现IP 浏览器输入IP 也可以到达百度首页,域名不可变一般 而IP地址可以变化。生活中用域名方便仅此而已, 用域名获得IP gethostbyname(char* hostname)返回hostent(域名对应的结构体) 同时还有一个gethostbyaddr 根据IP获取域名。先阶段写的程序都是创建套接字后直接使用,但是有时我们可以对套接字的特性进行一些操作。
Socket详解
网络编程就是通过编程让电脑跟电脑之间实现连接通信,一般情况下操作系统会提供套接字socket 这样的部件 来实现网络编程 我们按照框架填写参数就好。
网络编程中最基础的就是socket的操作,这里记录一下socket的基础操作有哪些,分别是什么作用,最后以一个简单的客户端和服务端例子收尾。
socket是什么?
socket起源于Unix,秉承着一切皆文件的思想,socket也是 打开 -读写- 关闭 这样的模式的一个实现。socket用于不同主机间进程的通信,而每个进程由 所使用的协议,Ip,端口号,三者决定,有兴趣的可以百度一下 多路分解和多路复用。
让我们看一下socket在TCP/IP中所处的位置
可以看到,socket位于应用层与传输层之间,作为一层抽象层把tcp/IP的复杂操作抽象成了一些简单的接口供应用层来调用。
socket的基本操作
主要介绍如下几个函数
int socket(int domain , int type, int protocol)
该函数创建一个socket套接字,返回一个socket描述符,相当于对该套接字的引用,在接下来的函数中将会用到它。我们来看看各个参数,这里只介绍一些楼主使用过的
domain : 协议族,常用的就是AF_INET,AF_INET6,AF_LOCAL等几种(还有不少),它最关键的作用是指定了地址格式,比如说AF_INET(事实上楼主至今也只用了它..),它决定了该socket套接字的地址必须使用 32位的ipv4地址和16位的port号
type :类型,当然指的就是创建的socket类型了,常见的有如下两种,SOCK_STREAM,SOCK_DGRAM(还有不少),分别对应TCP套接字和UDP套接字,至于tcp和udp协议,大家可以去看一下我之前的博文关于tcp和udp的一些事。
protocol: 协议,有IPPROTO_TCP,IPPROTO_UDP等几种(还有不少),显然就是对应于传输层TCP和UDP协议,这里我经常用0,当传入0的时候,会使用socket类型对应的默认传输协议
int bind(int sockfd, const struct sockaddr* addr, socklen_t addrlen)
该函数用于给socket绑定一个地址,之前说过了不同的协议族有对应不同的地址,我们以AF_INET,即ipv4+port来解释,先看看sockaddr结构体有哪些成员
struct sockaddr {
sa_family_t sa_family; /* address family, AF_xxx */
char sa_data[14]; /* 14 bytes of protocol address */
}共16个字节
但是我们传入的参数一般是 sockaddr_in结构体,我们来看看sockaddr_in结构体的成员有哪些
struct sockaddr_in
{
__SOCKADDR_COMMON (sin_);
in_port_t sin_port; /* Port number. */
struct in_addr sin_addr; /* Internet address. */
/* Pad to size of `struct sockaddr'. */
unsigned char sin_zero[sizeof (struct sockaddr) -
__SOCKADDR_COMMON_SIZE -
sizeof (in_port_t) -
sizeof (struct in_addr)];
/* 字符数组sin_zero[8]的存在是为了保证结构体struct sockaddr_in的大小和结构体struct sockaddr的大小相等 */
};
其实也就是方便我们使用,可以直接指定ip和port(当然这里有主机字节序和网络字节序的问题),而不需要去操作字符串了
int listen(int sockfd,int backlog)
该函数在服务端编程会用到,用于监听某个端口,至于是哪个就看你传入进去的sockfd设置的是哪一个了,backlog参数用于指定等待连接建立的socket队列的最大长度,成功返回0,失败为-1.
int accept(int sockfd, struct sockaddr* addr,socklen_t addrlen)
该函数返回与客户端建立连接的socket描述符,注意这跟监听socket不同,这是新建的一个用于与客户端通信的socket。该函数会一直阻塞到接收到一个连接为止。
第一个参数是监听socket的描述符,后面2个参数用于接受客户端的地址和地址长度。
上面2个函数都是在服务端编程中用于监听端口,被动创建与客户端的连接时,需要用到的,下面这个函数一般在客户端中需要用到的用于主动创建连接的
int connect(int sockfd,const struct sockaddr* addr,socklen_t addrlen )
第一个参数是客户端的socket描述符,第二个参数是服务器的地址,第三个当然还是地址长度,成功的时候返回0,失败则返回-1,该函数在服务器accept后,数据到达时会返回,具体连接过程,可以百度三次握手,也可以看看我写过的tcp的一些事。
OK,连接已经建立好了,现在要进行通讯了,
常用的函数有如下4个
int read(int sockfd,void* buf ,ssize_t count)
int write(int sockfd,void* buf,ssize_t count)
int recv(int sockfd,void* buf,ssize_t count,int flags)
int send(int sockfd,void* buf,ssize_t count,int flags)
这4个函数如何使用,请参照这篇文章http://blog.csdn.NET/u011408355/article/details/45921541
server端
#include
#include
#include
#include
#include
#include
#include
#include
#define PORT 6888
int main()
{
int serv_sock;
int client_sock;
struct sockaddr_in server; //设置sock地址信息
if(-1==(serv_sock=socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))) // 创建SOCKET 判定用何种协议族, 用TCP/UDP类型, 针对第二个参数设置不同协议 电话购买
{
printf("Create Socket Error\n");
}
memset(&server,0,sizeof(server));
server.sin_family=AF_INET;
server.sin_addr.s_addr=inet_addr("127.0.0.1");
server.sin_port=htons(PORT);
if(-1==bind(serv_sock, (struct sockaddr*)&server, sizeof(server))) //链接IP PORT 该函数用于给socket绑定一个地址 电话号码分配
{
printf("Bind Error\n");
}
printf("Start Listen\n");
if( -1==listen(serv_sock, 5)) //监听 该函数在服务端编程会用到,用于监听某个端口,至于是哪个就看你传入进去的第一个参数设置的是哪一个了,
//第二个参数用于指定等待连接建立的socket队列的最大长度 电话线路连接
{
printf("Listen Error\n");
exit(0);
}
while(1)
{
if( -1==(client_sock=accept(serv_sock,NULL,NULL) )) //接收 该函数返回与客户端建立连接的socket描述符,注意这跟监听socket不同,
//这是新建的一个用于与客户端通信的socket。该函数会一直阻塞到接收到一个连接为止。
{
printf("Accept Error\n");
continue;
}
char buf[2048+1];
memset(buf,0, sizeof(buf));
int n = recv(client_sock ,buf, 2048, 0); //接受客户端信息 把信息放入到buf中, 返回收到的字符长度
printf("Recv msg form cilent, %d byte: ",n);
printf("%s\n",buf);
memset(buf ,0, sizeof(buf)); //初始化数组
strcpy(buf,"The Server has get the message");
send(client_sock, buf, strlen(buf), 0); //将信息送给客户端
}
close(client_sock);
close(serv_sock);
return 0;
}
#include
#include
#include
#include
#include
#include
#include
#include
#define PORT 6888
int main()
{
int conndfd;
struct sockaddr_in serverAddr;
memset(&serverAddr,0,sizeof(serverAddr));
serverAddr.sin_family=AF_INET;
serverAddr.sin_addr.s_addr=inet_addr("127.0.0.1");
serverAddr.sin_port=htons(PORT);
while(1)
{
if(-1==(conndfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))) //客户端 创建
{
printf("Create Socket Error\n");
}
if(-1==connect(conndfd,(struct sockaddr*)&serverAddr,sizeof(serverAddr))) //客户端建立连接
{
printf("Connetc Error\n");
exit(0);
}
char buf[2048+1];
memset(buf, 0, sizeof(buf));
char Message[256];
printf("please input message:");
scanf("%s",Message);
printf("Connect Success Lets communicate!\n");
strcpy(buf, Message);
int n=send(conndfd, buf, strlen(buf),0); //将数据发送给服务器端
printf("Send %d byte to Server\n",n);
memset(buf,0,sizeof(buf));
n=recv(conndfd, buf, 2048, 0); //接受服务器端 发过来的信息
printf("Recv %d byte from Server:",n);
printf("%s\n",buf);
close(conndfd);
}
return 0;
}