基于TCP/UDP的socket服务器搭建流程

TCP/UDP概念介绍

一、TCP/UDP对比

  1. TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需 要建立连接
  2. TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付
  3. TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的。UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)
  4. 每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
  5. TCP首部开销20字节;UDP的首部开销小,只有8个字节
  6. TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道

二、端口号的作用

一台拥有IP地址的主机可以提供许多服务,比如Web服务、FTP服务、SMTP服务等。这些服务完全可以通过1个IP地址来实现。那么,主机是怎样区分不同的网络服务呢?显然不能只靠IP地址,因为IP 地址与网络服务的关系是一对多的关系。
实际上是通过IP地址+端口号来区 分不同的服务的。
端口提供了一种访问通道,服务器一般都是通过知名端口号来识别的。
例如,对于每个TCP/IP实现来说,FTP服务器的TCP端口号都是21,每个Telnet服务器的TCP端口号都是23,每个TFTP(简单文件传送协议)服务器的UDP端口号都是69

三、字节序

1、概念

字节序,就是多字节类型的数据在计算机内存中存储或者网络传输时各字节的存放顺序。是在跨平台和网络编程中,时常要考虑的问题

2、分类

字节序经常被分为两类:
1. Big-Endian(大端):高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
2.Little-Endian(小端):低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
3.网络字节序=大端字节序

样例介绍:
在内存中双字0x01020304(DWORD)的存储方式
内存地址
4000&4001&4002&4003
LE 04 03 02 01
BE 01 02 03 04

3、C程序在内存空间的映射

C程序映射中内存的空间布局大致如下:
最高内存地址 0xFFFFFFFF
栈区(从高内存地址,往 低内存地址发展。即栈底在高地址,栈顶在低地址)
堆区(从低内存地址 ,往 高内存地址发展)
全局区(常量和全局变量)
代码区
最低内存地址 0x00000000

socket服务器与客户端开发流程

一、流程介绍

socket服务器客户端开发步骤
详细说明

  1. socket() 创建套接字
  2. bind() 为套接字添加信息(IP地址+端口号)
  3. listen() 监听网络连接(客户端接入)
  4. accept() 监听到有客户端接入,接收一个连接
  5. read/write 数据交互
  6. close() 关闭套接字,断开连接

二、socket服务器搭建API介绍

1、scoket() 创建套接字

//**创建套接字**
int socket(int domain, int type, int protocol);
 - domain:
指明所使用的协议族,通常为AF_INET,表示互联网协议族(TCP/IP协议族)- AF_INET   IPv4因特网域
 - AF_INET6  IPv6因特网域
 - AF_UNIX   Unix域
 - AF_ROUTE  路由套接字
 - AF_KEY    秘钥套接字
 - AF_UNSPEC 未指定

 - type:
 指定socket的类型:
 - SOCK_STREAM:
 流式套接字提供可靠的、面向连接的通信流;它使用TCP协议,从而保证了数据传输的正确性和顺序性
 - SOCK_DGRAM:
数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证是可靠、无差错的。它使用数据报协议UDP。
 - SOCK_RAW:
允许程序使用低层协议,原始套接字允许对低层协议如IP或者ICMP进行直接访问,功能强大但使用较为不便,主要用于一些协议栈的开发。

 - protocol:
通常赋值“0- 0选择type类型对应的默认协议 
 - IPPROTO_TCP   TCP传输协议 
 - IPPROTO_UDP   UDP传输协议
 - IPPROTO_SCTP  SCTP传输协议     
 - IPPROTO_SCTP  TIPC传输协议

2、bind() 将套接字与地址关联

//关联地址和套接字
#include <sys/types.h>    
#include <sys/socket.h>
int bind(int sockfd, const struct sockaddr *addr,
        socklen_t addrlen);
功能:
	用于绑定IP地址和端口号到socketfd
参数:
 - sockfd   是一个socket描述符
 - addr     是一个指向包含有本机IP地址及端口号等信息的sockaddr类型的指针,指向要绑定给
            sockfd的协议地址结构。这个地址结构根据地址创建socket时的地址协议族的不同而不同
 - addrlen  套接字的大小

IPv4对应的是:
	struct sockaddr {
 		unsigned short sa_family;	//协议族
 		char        sa_data[14];	//IP+端口
	 }
同等替换:
	struct sockaddr_in {
 		sa_family_t     sin_family;	/*协议族*/
 		in_port_t	    sin_port;	/*端口号*/
 		struct in_addr  sin_addr;   /*IP地址结构体*/
 		unsigned char   sin_zero[8]; /*填充 没有实际意义,只是为跟sockaddr结构在内存中对齐,这样两者才能相互转换*/
	 }

3、地址转换API

1. 把字符串形式的“192.168.1.123”转为网络能识别的格式
int inet_aton(const char* straddr,struct in_addr *addrp);
功能:
	将一个字符串表示的点分十进制IP地址IP转换为网络字节序存储在addr中,并且返回该网络字节序表示的无符号整数。
参数:
	const char *straddr: 我们输入的点分十进制的IP地址
	struct in_addr* addr: 将IP转换为网络字节序(大端存储)后并保存在addr中
返回值:
	成功;返回点分十进制的IP地址对应的网络字节序表示的无符号整数
	失败;返回0
2. 把网络格式的ip地址转为字符串形式
char* inet_ntoa(struct in_addr inaddr); 
功能:
	将一个网络字节序的IP地址(也就是结构体in_addr类型变量)转化为点分十进制的IP地址(字符串)
参数:
	struct in_addr{    
		in_addr_t s_addr;
	};
	in_addr是一个按网络顺序存储的IP地址
返回值:
	该函数的返回值是一个字符串,这个字符串是点分十进制的IP地址

4、listen() 监听

//监听设置函数
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int listen(int sockfd, int backlog);
功能:
 - 设置能处理的最大连接数,listen()并未开始接收连线,只是设置socket的listen模式,listen函数只用于服务器端,服务器进程不知道要与谁连接,
   因此,它不会主动地要求与某个进程连接,只是一直监听是否有其他客户进程与之连接,然后响应该连接请求,并对它作出处理,一个服务进程可以同时处理多个客户进程的连接。
   主要就两个功能:将一个未连接的套接字转换为一个被动套接字(监听),规定内核为相应套接字排队的最大连接数。
 - 内核为任何一个给定监听套接字维护两个队列:
 	 - 未完成连接队列,每个这样的SYN报文端对应其中一项;已由某个客户端发出并到达服务器,而服务器正在等待完成相应的TCP三次握手过程。这些套接字处于SYN_REVD状态。
 	 - 已完成连接队列,每个已完成TCP三次握手过程的客户端对应其中一项。这些套接字处于ESTABLISHED状态
参数:
	- sockfd:sockfd是socket系统调用返回的服务端socket描述符
	- backlog:backlog指定在请求队列中允许的最大请求数

5、accept 连接

#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
功能:
	accept函数由TCP服务器调用,用于从已完成连接队列队头返回下一个已完成连接。如果已完成连接队列为空,那么进程进入睡眠。
参数:
	sockfd: 是socket系统调用返回的服务器端socket描述符
	addr:   用来返回已连接的对端(客户端)的协议地址
	addrled:客户端地址长度
返回值:
	该函数的返回值是一个新的套接字描述符,返回值是表示已经连接的套接字描述符,而第一个参数是服务器监听套接字描述符。
	一个服务器通常仅仅创建一个监听套接字,它在该服务器的生命周期内一直存在。
	内核为每个由服务器进程接收的客户连接创建一个已连接套接字(表示TCP三次握手已经完成),当服务器完成对某个给定客户的服务时,相应的已连接套接字就会被关闭。

6、数据收发

//字节流读取函数
在套接字通信中进行字节读取函数:read(),write().与I/O中的读取函数略有区别,因为他们输入或输出的字节数可能比请求的少。
ssize_t write(int fd, const void*buf,size_t nbytes);
ssize_t read(int fd,void *buf,size_t nbyte);
/*说明
*函数均返回读或写的字节个数,出错则返回-1
*/
第一个将buf中的nbytes个字节写入到文件描述符fd中,成功时返回写的字节数。
第二个为从fd中读取nbyte个字节到buf中,返回实际所读的字节数。详细应用说明参考使用read、write读写socket(套节字)。
网络I/O还有一些函数,例如:recv()/send(),readv()/writev(),recvmsg()/sendmsg(),recvfrom()/sendto()等。

数据收发常用第二套API
1.在TCP套接字上发送数据函数:有连接
ssize_t send(int s,const void *msg,size_t len,int flags);
//包含3要素:套接字s,待发数据msg,数据长度len
//函数只能对处于连接状态的套接字使用,参数s为已建立好连接的套接字描述符,即accept函数的返回值
//参数msg指向存放待发送数据的缓冲区
//参数len为待发送数据的长度,参数flags为控制选项,一般设置为0
2.在TCP套接字上接收数据函数:有连接
ssize_t recv(int s,void *buf,size_t len,int flags);
//包含3要素:套接字s,待发数据msg,数据长度len
//函数recv从参数s所指定的套接描述符(必须是面向连接的套接字)上接收
//数据并保存到参数buf所指定的缓冲区
//参数len则为缓冲区长度,参数flags为控制选项,一般设置为0

7、connect()

//客户机连接主机
#include <sys/types.h>      
#include <sys/socket.h>
int connect(int sockfd, const struct sockaddr *addr,
                   socklen_t addrlen);
功能:
	该函数用于绑定之后的client端(客户端),与服务器建立连接
参数:
sockfd:是目的服务器的socket描述符
addr:是服务器端的IP地址和端口号的地址结构指针
addrlen:地址长度常设置为sizeof(struct sockaddr)
返回值:
成功返回0,遇到错误时返回-1,并且errno中包含相应的错误码
  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值