socket学习笔记

这篇博客详细介绍了Socket编程的模式,包括面向连接服务端和客户端的步骤。涉及的关键函数有socket(), bind(), listen(), accept(), connect(), send(), recv()等,并讨论了阻塞与非阻塞模式的选择。此外,还提到了IP头的数据结构。" 54926344,5840397,Echarts实现省市地图数据跳转详解,"['Echarts', '数据可视化', '地图展示', 'JavaScript', '数据处理']
摘要由CSDN通过智能技术生成

socket编程模式:
1.定义参数,sockaddr_in ,sockaddr是系统提供的描述socket信息的结构体,需要用户来赋初值.
sockaddr_in和sockaddr均是保存socket信息的类型,但sockaddr_in结构更为方便,sin_zero(填充0 以保持与struct sockaddr同样大小以便互相转换使用bzero() 或 memset() 来全部置零.)
上述结构体如sockaddr_in ,sockaddr均已在系统头文件sys/socket.h中定义好了.
2.给上述结构体实例赋值
3.具体过程:

需要用到的函数介绍:
inet_addr("132.241.5.10"):将IP地址从点数格式转换成无符号长整型.

bzero(&(my_addr.sin_zero)) 或 memset(&(my_addr.sin_zero)):将struct sockaddr_in中的sin_zero置零.

htons(): "Host to Network Short"将本机字节顺序转换成网络字节顺序.

fcntl(sockfd, F_SETFL, O_NONBLOCK),设置阻塞.

gethostbyname(“www.google.com”)得到远地主机的地址信息.

select(int numfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout):多路同步函数调用,参数int numfds为同步运行的socket最大数目,fd_set *readfds为指向存储多路同步读socket描述符集合的指针.同理fd_set *writefds为指向存储多路同步写socket描述符集合的指针.在调用select()之前必须调用宏FD_SET(int fd, fd_set *set)描述符添加到集合中.

 

面向相连接服务端:
必须得到本机ip,本机socket描述符,本机端口号.
a.socket()建立socket指名使用协议,类型(数据流还是数据报).返回值:int(socket描述符)
b.bind()绑定本机端口号,本机iP地址.返回值:int
c.listen()监听是否有客户端接入,设置连接等待上限.返回值:int
d.accept()同意接受客户端的接入,同时存贮接入的客户端的struct sockaddr_in型的结构体(包含客户端所要连接的端口号,客户端的ip地址等).该函数返回另一个socket描述符,即new_fd=accept()为新的socket描述符.用于send()recv()传输数据,原来的socket描述符仍旧用来监听客户端的连接.返回值:int(新的socket描述符)
e.recv()其中*buf是要读取信息的缓冲的指针,即对方发来消息的暂存区,使用新的socket描述符.
f.send()其中*msg是要发送信息的缓冲的指针,使用新的socket描述符.
g.close().
上述返回值主要起着判断错误的作用


面向连接客户端:
必须有服务端ip或网址,服务端连接的端口号,本机socket描述符.
a.socket()建立socket指名使用协议,类型.返回值:int
b.bind()可有可无.
c.gethostbyname()得到服务端的ip地址(该函数是一个域名解析函数,输入服务端的可记忆地址如:http://www.xxxx.com即可返回该服务端的信息).返回值:hostent类型的结构体指针.该结构体实例存贮了返回的服务端的主机名,ip地址(在h_addr字段).
d.connect()与服务端连接,该连接指定本机的socket描述符,将要连接的服务端的地址结构及长度.返回值:int.
e.send()
f.recv()
g.close()


综述:
1.socket为两台主机的连接点,每台主机都有一个或多个socket用于连接传输数据(用户自行创建).用户可以根据需要选择socket连接点.任何连接都要建立在socket所建立的管道上.所以函数listen()accept()connect()send()recv()等都要绑定socket描述符.socket描述符可看作是每台机器的socket的id,该id在该机器上是唯一的,因此可用于标示该socket.
2.两台机器使用socket连接传输时需要一个全相关即(协议,本地地址,本地端口号,远地地址,远地端口号).协议在创建socket时由用户指定,本机地址和端口号可由服务程序自行填充和分配或由用户调用bind()自行填充和分配.远地地址和远地端口号同样由远地主机创建,通过connect(),accept(),gethostbyname()传送或取得.


socket(),accept()等函数为阻塞函数,即如果接受不到数据就阻碍整个程序的执行,除非出现异常.利用fcntl()函数可以将socket()设置为非阻塞即
int newsocket;
newsocket = socket(PF_INET, SOCK_STREAM, 0 );
fcntl( newsocket, F_SETEL, O_NONBLOCK );
以后只要绑定newsocket的accept()等函数就不会阻塞.但查询该socket连接信息时会浪费大量资源.比如绑定非阻塞的socket的非阻塞的accept()函数,服务器使用recv()接受信息时会不停的询问accept(),系统也会不停调用accept(),从而浪费大量cpu资源.如果accept()设置为阻塞则当recv()询问accept()时,由于accept()挂起,recv()也挂起,直到accept()接到客户端connect()来的信息为止.


ip头的数据结构:
[StructLayout(LayoutKind.Explicit)]
 public struct IPHeader
 {
  [FieldOffset(0)] public byte    ip_verlen;        //4位首部长度+4位IP版本号
  [FieldOffset(1)] public byte    ip_tos;            //8位服务类型TOS
  [FieldOffset(2)] public ushort  ip_totallength; //16位数据包总长度(字节)
  [FieldOffset(4)] public ushort  ip_id;             //16位标识
  [FieldOffset(6)] public ushort  ip_offset;       //3位标志位
  [FieldOffset(8)] public byte    ip_ttl;            //8位生存时间 TTL
  [FieldOffset(9)] public byte    ip_protocol;    //8位协议(TCP, UDP, ICMP, Etc.)
  [FieldOffset(10)] public ushort ip_checksum; //16位IP首部校验和
  [FieldOffset(12)] public uint   ip_srcaddr;     //32位源IP地址
  [FieldOffset(16)] public uint   ip_destaddr;   //32位目的IP地址
 }
注:StructLayout(LayoutKind.Explicit)类通过设置FieldOffset()字段,可以让用户控制类或结构体的布局.FieldOffset(n)代表该成员离类或结构体头部的长度为n个字节(8位).

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值