大多数操作系统均使用系统调用(system call)的机制在应用程序和操纵系统之间传递控制权,网络编程亦是如此,网络中的进程是通过socket来进行通信的,那什么是socket呢?
Socket,中文名套接字,起源于Unix,而Unix/Linux基本哲学之中的一个就是“一切皆文件”。都能够用“打开open –> 读写write/read –> 关闭close”模式来操作。
笔者的理解,即Sockect就是网络编程中“应用进程与操作系统”之间的系统调用接口的集合,即应用进程与操作系统之间网络通信的中间件,如下图所示:
通信过程如上图所示,当应用进程需要使用网络进行通信时,就会发出一种系统调用,请求操作系统为其建立“Socket套接字”,这样操作系统就会利用该模式下的中间件->Socket接口,将网络通信所需要的系统资源分配给该应用进程。这些资源的总和由Socket封装起来。
通信完毕后,应用进行再通过一个关闭套接字的系统调用通知操作系统回收此Socket的所有资源。通信过程结束。
Socket 的出现,使得程序员可以很方便的访问 TCP/IP,从而开发各种网络应用的程序
在 OSI 的各层所使用的协议:
1.应用层:Telnet,FTP,HTTP,DNS,SMTP,POP3
2.传输层:TCP,UDP
TCP:面向连接的可靠的传输协议,通信前建立三次握手,握手成功后才能通信, 对数据准确性要求较高的场合使用,如从网上载的安装文件,不能缺少任何信息
UDP:是无连接的,不可靠的传输协议,不需要建立连接,也没有重传和确认的机制,在实时性要求较高,但对数据准确度要求不是很高的场合使用,如视频会 议,在线观看电影,当中丢失个别数据包并不影响整体的效果。
3.网络层:IP
端口Port
1.为了标识通信实体中进行通信的进程(应用程序),TCP/IP 协议提出了协议 端口的概念
2.端口是一种抽象的软件结构(包括一些数据结构和 I/O 缓冲区)。应用程序 通过系统调用和某端口建立连接(binding)后,传输层传给该端口的数据都被 相应的进程所接收,相应进程发给传输层的数据都通过该端口输出
3.端口用一个整数型标识符来表示,即端口号。端口号跟协议相关,TCP/IP 传 输层的两个协议 TCP 和 UDP 是完全独立的的两个软件模块,因此各自的端口号 也相互独立
4.端口使用一个 16 位的数字来表示,它的范围是 0~65535,1024 以下的端 口号保留给预定义的服务,例如,http 使用 80 端
Socket系统调用顺序:
TCP连接
UDP连接
基于TCP的Socket编程
Server端顺序:
1.加载套接字库
对于windows系统,加载“winsock.h ”,同时要链接其静态库“ws2_32.lib”,在调用Socket函数之前还要调用WSAStartup函数完毕对Winsock服务的初始化,如
#include "winsock2.h"
#pragma comment(lib, "Ws2_32.lib")
int main()
{
WSADATA wdata;
if ( WSAStartup(MAKEWORD(2,2), &wdata) !=0 )
{
return INVALID_SOCKET;
}
...
return 0;
}
对于嵌入式系统,如用到lwip协议栈
#include <lwip/sys.h>
#include <lwip/sockets.h>
2.创建套接字(socket)
SOCKET sServer; //服务器套接字
sServer = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
3.将套接字绑定到本地地址和端口上(bind)
//首先定义服务器地址参数
SOCKADDR_IN addrServ;
addrServ.sin_family = AF_INET;
addrServ.sin_port = htons(1234);
addrServ.sin_addr.S_un.S_addr = INADDR_ANY
//绑定套接字和服务器套接字地址
retVal = bind(sServer, (sockaddr*)&addrServ, sizeof(SOCKADDR_IN));
4.将套接字设为监听模式,准备接收客户请求(listen)
//开始监听,设置等待接受连接的队列长度
retVal = listen(sServer, 1);
5.等待客户请求的到来;当请求带来后,接受连接请求,返回一个新的对应于此次连接的套接字(accept)
//accept函数处于阻塞状态,直到接受了一个客户端请求。当该函数返回时,新建一个套接字,同时返回该客户端的地址
//如果调用失败,则返回INVALID_SOCKET
sClient = accept(sServer, (sockaddr*)&addrClient, &addrClientlen);
6.用返回的套接字和客户端进行通信(send/recv)
retVal = send(sClient, struct_buf, sizeof(struct_buf), 0);
retVal = recv(sClient, buf, sizeof(buf), 0);
7.返回,等待另一个客户请求
8.关闭套接字(closesocket)
客户端顺序:
1.加载套接字库
2.创建套接字(socket)
3.向服务器发送连接请求(connect)
4.和服务器端进行通信(send/receive)
5.关闭套接字(closesocket)
基于UDP的Socket编程
服务器端顺序:
1.加载套接字库
2.创建套接字(socket)
3.将套接字绑定到一个本地地址和端口上(bind)
4.等待接收数据(recvfrom)
5.关闭套接字(closesocket)
客户端顺序:
1.加载套接字库
2.创建套接字(socket)
3.向服务器发送数据(sendto)
4.关闭套接字(closesocket)