Linux 网络编程是一个基于客户端/服务器(即:client/server)的套接字编程结构(即:socket 编程)。
在Linux网络编程的过程中,使用到的协议主要有:TCP/IP(基于连接的协议)、UDP(基于无连接的协议)、ICMP(通常我们在 DOS 状态下通过使用 ping 命令,检查网络的通断,就是依靠该协议)。
在Linux系统的套接字编程中,有标准的 socket( )、client( ) 代码的编写风格。涉及到的主要库函数有:bind( )、listen( )、accept( )、read( )、write( ) 等。
至于说要想学习详细的Linux网络编程技术实现细节,你可以参考《TCP/IP详解》一书。一套共三本。
⑵ Windows和Linux下的网络编程方法的异同
Linux下的网络编程与Windows下采用底层的API类似,但是也有区别:区别一:Windows下需加上WSAStartup()函数区别二:关闭socket:Linux为close(),Windows为closesocket()windows下采用上层的API,一般有CSocket和CAsynSocket这两种类型的类这种情况以下socket函数一般的首字母大写。而底层的API不管是windows下的还是linux下的socket函数首字母都是小写的。
⑶ linux网络编程,为什么要将文件描述符设置成非阻塞模式
非阻塞IO 和阻塞IO:
在网络编程中对于一个网络句柄会遇到阻塞IO 和非阻塞IO 的概念, 这里对于这两种socket 先做一下说明:
基本概念:
阻塞IO::
socket 的阻塞模式意味着必须要做完IO 操作(包括错误)才会
返回。
非阻塞IO::
非阻塞模式下无论操作是否完成都会立刻返回,需要通过其他方
式来判断具体操作是否成功。(对于connect,accpet操作,通过select判断,
对于recv,recvfrom,send,sendto通过返回值+错误码来判断)
IO模式设置:
SOCKET
对于一个socket 是阻塞模式还是非阻塞模式的处理方法::
方法::
用fcntl 设置;用F_GETFL获取flags,用F_SETFL设置flags|O_NONBLOCK;
同时,recv,send 时使用非阻塞的方式读取和发送消息,即flags设置为MSG_DONTWAIT
实现
fcntl 函数可以将一个socket 句柄设置成非阻塞模式:
flags = fcntl(sockfd, F_GETFL, 0); //获取文件的flags值。
fcntl(sockfd, F_SETFL, flags | O_NONBLOCK); //设置成非阻塞模式;
flags = fcntl(sockfd,F_GETFL,0);
fcntl(sockfd,F_SETFL,flags&~O_NONBLOCK); //设置成阻塞模式;
并在接收和发送数据时:
将recv, send 函数的最后有一个flag 参数设置成MSG_DONTWAIT
recv(sockfd, buff, buff_size,MSG_DONTWAIT); //非阻塞模式的消息发送
send(scokfd, buff, buff_size, MSG_DONTWAIT); //非阻塞模式的消息接受
普通文件
对于文件的阻塞模式还是非阻塞模式::
方法1、open时,使用O_NONBLOCK;
方法2、fcntl设置,使用F_SETFL,flags|O_NONBLOCK;
消息队列
对于消息队列消息的发送与接受::
//非阻塞 msgsnd(sockfd,msgbuf,msgsize(不包含类型大小),IPC_NOWAIT)
//阻塞 msgrcv(scokfd,msgbuf,msgsize(**),msgtype,IPC_NOWAIT);
读
阻塞与非阻塞读的区别: //阻塞和非阻塞的区别在于没有数据到达的时候是否立刻返回.
读(read/recv/msgrcv):
读的本质来说其实不能是读,在实际中, 具体的接收数据不是由这些调用来进行,是由于系统底层自动完成的。read 也好,recv 也好只负责把数据从底层缓冲 到我们指定的位置.
对于读来说(read, 或者recv) ::
阻塞情况下::
在阻塞条件下,read/recv/msgrcv的行为::
1、如果没有发现数据在网络缓冲中会一直等待,
2、当发现有数据的时候会把数据读到用户指定的缓冲区,但是如果这个时候读到的数据量比较少,比参数中指定的长度要小,read 并不会一直等待下去,而是立刻返回。
read 的原则::是数据在不超过指定的长度的时候有多少读多少,没有数据就会一直等待。
所以一般情况下::我们读取数据都需要采用循环读的方式读取数据,因为一次read 完毕不能保证读到我们需要长度的数据,
read 完一次需要判断读到的数据长度再决定是否还需要再次读取。
非阻塞情况下::
在非阻塞的情况下,read 的行为::
1、如果发现没有数据就直接返回,
2、如果发现有数据那么也是采用有多少读多少的进行处理.
所以::read 完一次需要判断读到的数据长度再决定是否还需要再次读取。
对于读而言:: 阻塞和非阻塞的区别在于没有数据到达的时候是否立刻返回.
recv 中有一个MSG_WAITALL 的参数::
recv(sockfd, buff, buff_size, MSG_WAITALL),
在正常情况下recv 是会等待直到读取到buff_size 长度的数据,但是这里的WAITALL 也只是尽量读全,在有中断的情况下recv 还是可能会被打断,造成没有读完指定的buff_size的长度。
所以即使是采用recv + WAITALL 参数还是要考虑是否需要循环读取的问题,在实验中对于多数情况下recv (使用了MSG_WAITALL)还是可以读完buff_size,
所以相应的性能会比直接read 进行循环读要好一些。
注意:: //使用MSG_WAITALL时,sockfd必须处于阻塞模式下,否则不起作用。
//所以MSG_WAITALL不能和MSG_NONBLOCK同时使用。
要注意的是使用MSG_WAITALL的时候,sockfd 必须是处于阻塞模式下,否则WAITALL不能起作用。
写
阻塞与非阻塞写的区别: //
写(send/write/msgsnd)::
写的本质也不是进行发送操作,而是把用户态的数据 到系统底层去,然后再由系统进行发送操作,send,write返回成功,只表示数据已经 到底层缓冲,而不表示数据已经发出,更不能表示对方端口已经接收到数据.
对于write(或者send)而言,
阻塞情况下:: //阻塞情况下,write会将数据发送完。(不过可能被中断)
在阻塞的情况下,是会一直等待,直到write 完,全部的数据再返回.这点行为上与读操作有所不同。
原因::
读,究其原因主要是读数据的时候我们并不知道对端到底有没有数据,数据是在什么时候结束发送的,如果一直等待就可能会造成死循环,所以并没有去进行这方面的处理;
写,而对于write, 由于需要写的长度是已知的,所以可以一直再写,直到写完.不过问题是write 是可能被打断吗,造成write 一次只write 一部分数据, 所以write 的过程还是需要考虑循环write, 只不过多数情况下一次write 调用就可能成功.
非阻塞写的情况下:: //
非阻塞写的情况下,是采用可以写多少就写多少的策略.与读不一样的地方在于,有多少读多少是由网络发送的那一端是否有数据传输到为标准,但是对于可以写多少是由本地的网络堵塞情况为标准的,在网络阻塞严重的时候,网络层没有足够的内存来进行写操作,这时候就会出现写不成功的情况,阻塞情况下会尽可能(有可能被中断)等待到数据全部发送完毕, 对于非阻塞的情况就是一次写多少算多少,没有中断的情况下也还是会出现write 到一部分的情况.
⑷ 如何在linux下进行网络编程
呵呵,你问对人啦,我就是学习了C语言的基础知识(谭浩强的那本书),然后学习了网络编程。现在在做linux云计算 你需要找到《UNIX网络编程第1卷:套接口API》 看这个书的同时,你从网上找些最简单的网络通讯程序小例子看看
⑸ 请比较Linux与Windows在网络编程方面的特点
找了一段,大致涉及到了您的问题:
一、socket的模式
socket一般有两种模式:同步和异步(windows网络编程技术中也可叫锁定和非锁定,Linux网络编程叫阻塞和非阻塞)。
二、socket的类型
socket一般有三种类型,基于TCP的流式套接字,基于UDP的数据报套接字和原始套接字。
三、socket的IO模型
socket
的IO模型是编程中使用socket两种模式的策略,它们适用的场合不同,在不同的操作系统上支持的模型也不同,例如windows从NT版本才开始支持
完成端口模型。Linux和Windows所支持的模型也有区别,当然也有相同的地方,可能叫法不一样,但大致思路是一样的,下面分别介绍windows
和Linux的IO模型
1、 Windows下的套接字IO模型:
A、 Select(选择)模型
用于同步socket的状态检测模型,又叫(Linux)多路复用,可以同时检测多个socket的状态
B、 WSAAsyncSelect(异步选择)模型
用于异步socket的异步事件设置,它是基于Windows消息的模型,必须先打开一个窗口,然后把窗口和socket的消息绑定,这样,在socket有消息通知时,操作系统便通知窗口,然后在窗口进行处理。
C、 WSAEventSelect(异步事件)模型
用
于异步socket的异步事件,它是基于网络事件的模型,先使用CreateEvent创建一个事件,然后使用WSAEventSelect进行事件绑
定,然后可以使用WaitForMultipleObject(Event)进行事件监听,可以同时监听多个事件,不光是socket的,比如可以监听使
用CreateWaitableTimer创建的Timer等。
D、 重叠IO模型
用
于异步socket,在创建socket时需要在创建函数WSASocket中使用WSA_FLAG_OVERLAPPED标志,然后在投递IO请求的时
候将一个Overlapped结构体指针赋给投递函数,可以使用WSAWaitForMultipleObject来监听事件,然后使用
WSAGetOverlappedResult来获取IO的状态,也可以在Overlapped结构体中使用完成例程来处理,即在投递函数中把完成例程赋
给投递函数。
E、 完成端口模型
它
是迄今为止最复杂的一种IO模型,当应用程序需要管理众多的套接字并且希望随着系统内安装的CPU数目的增多,应用程序的性能也可以线性增加,就可以使用
这种模型,它的原理是每个CPU可以单独负责一个线程的执行,避免线程的频繁切换。使用这种模型往往可以达到最佳的系统性能。
首
先需要使用CreateIOCompletePort来创建完成端口,然后将IO句柄和此端口绑定,绑定也是使用此函数,当然也可以一次完成。接着是创建
工作者线程,工作者线程会使用GetQueuedCompletionStatus进入完成端口维护的线程池,当有完成事件时,会激活一个线程。
2、 Linux下的IO模型
A、阻塞IO
B、非阻塞IO
C、IO多路复用(选择)
D、信号驱动
用于异步socket,首先设定信号处理函数,然后使用fcntl函数设定socket的拥有者,像windows下使用WSAAsncSelect设定socket的窗口一样。使用这种模型,当内核操作可以被操作的时候通知我们的应用程序
E、异步IO
当内核在所有操作完成后才会通知应用程序
四、socket的一些使用上的优化
A、缓冲区的优化,可以考虑让应用程序使用比较小的缓冲区,但同时使用多个WSARecv
B、使用socket选项SO_SNDBUF和SO_RCVBUF设置socket缓冲区大小,如果设为0,操作体系统会使用应用程序的缓冲区,这样避免了从系统缓冲区向用户区复制的开销
五、注意这些IO模型有些不光是针对socket的,其他的IO操作也可以使用,最常用使用的是WriteFile,ReadFile等函数。
其它查考网址:
http://blog.163.com/tianle_han/blog/static/6617826200821522743948/
http://blog.csdn.net/yibulianhua/article/details/5374317
⑹ linux socket网络编程怎样收发包
1.send函数
ssize_t send( SOCKET s, const char *buf, size_t len, int flags );
(1)send先比较待发送数据的长度len和套接字s的发送缓冲的长度, 如果len大于s的发送缓冲区的长度,该函数返回SOCKET_ERROR;
(2)如果len小于或者等于s的发送缓冲区的长度,那么send先检查协议是否正在发送s的发送缓冲中的数据,如果是就等待协议把数据发送完,如果协议还没有开始发送s的发送缓冲中的数据或者s的发送缓冲中没有数据,那么send就比较s的发送缓冲区的剩余空间和len;
(3)如果len大于剩余空间大小,send就一直等待协议把s的发送缓冲中的数据发送完;
(4)如果len小于剩余空间大小,send就仅仅把buf中的数据到剩余空间里(注意并不是send把s的发送缓冲中的数据传到连接的另一端的,而是协议传送的,send仅仅是把buf中的数据到s的发送缓冲区的剩余空间里)。
注意:
(1)如果send函数数据成功,就返回实际的字节数,如果send在数据时出现错误,那么send就返回SOCKET_ERROR;如果send在等待协议传送数据时网络断开的话,那么send函数也返回SOCKET_ERROR。
(2)要注意send函数把buf中的数据成功到s的发送缓冲的剩余空间里后它就返回了,但是此时这些数据并不一定马上被传到连接的另一端。如果协议在后续的传送过程中出现网络错误的话,那么下一个Socket函数就会返回SOCKET_ERROR。(每一个除send外的Socket函数在执行的最开始总要先等待套接字的发送缓冲中的数据被协议传送完毕才能继续,如果在等待时出现网络错误,那么该Socket函数就返回 SOCKET_ERROR)
(3)在Unix系统下,如果send在等待协议传送数据时网络断开的话,调用send的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。
2.recv函数
ssize_t recv(int s, char *buf, size_t len, int flags);
(1)recv先等待s的发送缓冲中的数据被协议传送完毕,如果协议在传送s的发送缓冲
中的数据时出现网络错误,那么recv函数返回SOCKET_ERROR。
(2)如果s的发送缓冲中没有数据或者数据被协议成功发送完毕后,recv先检查套接字s的接收缓冲区,如果s接收缓冲区中没有数据或者协议正在接收数 据,那么recv就一直等待,直到协议把数据接收完毕。当协议把数据接收完毕,recv函数就把s的接收缓冲中的数据到buf中。(注意:协议接收到的数据可能大于buf的长度,所以 在这种情况下要调用几次recv函数才能把s的接收缓冲中的数据完。recv函数仅仅是数据,真正的接收数据是协议来完成的)
(3)recv函数返回其实际的字节数。如果recv在时出错,那么它返回SOCKET_ERROR;如果recv函数在等待协议接收数据时网络中断了,那么它返回0。
注意:在Unix系统下,如果recv函数在等待协议接收数据时网络断开了,那么调用recv的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。
Q&A:
(1)两次send一次recv会发生什么?
一次性读取两次send的内容。
(2)recv之后,接收缓冲区会被清空吗?
是的。
⑺ linux网络编程中如何实现服务器端多个read()和客户端write( )
TCP通信的模式如下图,比较固定,对着图编代码就可以了:
因为客户端没有指定IP地址和端口,所以其IP和端口都是内核随机分配的。
⑻ linux tcp/ip 网络通信编程
/*************************************
文件名:server.c
linux下socket网络编程简例-服务端程序
服务器端口设为0x8888(端口和地址可根据实际情况更改,或者使用参数传入)
服务器地址设为192.168.1.104
作者:kikilizhm#163.com(将#换为@)
*/
#include<stdlib.h>
#include<sys/types.h>
#include<stdio.h>
#include<sys/socket.h>
#include<linux/in.h>
#include<string.h>
intmain()
{
intsfp,nfp;/*定义两个描述符*/
structsockaddr_ins_add,c_add;
intsin_size;
unsignedshortportnum=0x8888;/*服务端使用端口*/
printf("Hello,welcometomyserver! ");
sfp=socket(AF_INET,SOCK_STREAM,0);
if(-1==sfp)
{
printf("socketfail! ");
return-1;
}
printf("socketok! ");
/*填充服务器端口地址信息,以便下面使用此地址和端口监听*/
bzero(&s_add,sizeof(structsockaddr_in));
s_add.sin_family=AF_INET;
s_add.sin_addr.s_addr=htonl(INADDR_ANY);/*这里地址使用全0,即所有*/
s_add.sin_port=htons(portnum);
/*使用bind进行绑定端口*/
if(-1==bind(sfp,(structsockaddr*)(&s_add),sizeof(structsockaddr)))
{
printf("bindfail! ");
return-1;
}
printf("bindok! ");
/*开始监听相应的端口*/
if(-1==listen(sfp,5))
{
printf("listenfail! ");
return-1;
}
printf("listenok ");
while(1)
{
sin_size=sizeof(structsockaddr_in);
/*accept服务端使用函数,调用时即进入阻塞状态,等待用户进行连接,在没有客户端进行连接时,程序停止在此处,
不会看到后面的打印,当有客户端进行连接时,程序马上执行一次,然后再次循环到此处继续等待。
此处accept的第二个参数用于获取客户端的端口和地址信息。
*/
nfp=accept(sfp,(structsockaddr*)(&c_add),&sin_size);
if(-1==nfp)
{
printf("acceptfail! ");
return-1;
}
printf("acceptok! Serverstartgetconnectfrom%#x:%#x ",ntohl(c_add.sin_addr.s_addr),ntohs(c_add.sin_port));
/*这里使用write向客户端发送信息,也可以尝试使用其他函数实现*/
if(-1==write(nfp,"hello,welcometomyserver ",32))
{
printf("writefail! ");
return-1;
}
printf("writeok! ");
close(nfp);
}
close(sfp);
return0;
}
/*************************************
文件名:client.c
linux下socket网络编程简例-客户端程序
服务器端口设为0x8888(端口和地址可根据实际情况更改,或者使用参数传入)
服务器地址设为192.168.1.104
作者:kikilizhm#163.com(将#换为@)
*/
#include<stdlib.h>
#include<sys/types.h>
#include<stdio.h>
#include<sys/socket.h>
#include<linux/in.h>
#include<string.h>
intmain()
{
intcfd;/*文件描述符*/
intrecbytes;
intsin_size;
charbuffer[1024]={0};/*接受缓冲区*/
structsockaddr_ins_add,c_add;/*存储服务端和本端的ip、端口等信息结构体*/
unsignedshortportnum=0x8888;/*服务端使用的通信端口,可以更改,需和服务端相同*/
printf("Hello,welcometoclient! ");
/*建立socket使用因特网,TCP流传输*/
cfd=socket(AF_INET,SOCK_STREAM,0);
if(-1==cfd)
{
printf("socketfail! ");
return-1;
}
printf("socketok! ");
/*构造服务器端的ip和端口信息,具体结构体可以查资料*/
bzero(&s_add,sizeof(structsockaddr_in));
s_add.sin_family=AF_INET;
s_add.sin_addr.s_addr=inet_addr("192.168.1.104");/*ip转换为4字节整形,使用时需要根据服务端ip进行更改*/
s_add.sin_port=htons(portnum);/*这里htons是将short型数据字节序由主机型转换为网络型,其实就是
将2字节数据的前后两个字节倒换,和对应的ntohs效果、实质相同,只不过名字不同。htonl和ntohl是
操作的4字节整形。将0x12345678变为0x78563412,名字不同,内容两两相同,一般情况下网络为大端,
PPC的cpu为大端,x86的cpu为小端,arm的可以配置大小端,需要保证接收时字节序正确。
*/
printf("s_addr=%#x,port:%#x ",s_add.sin_addr.s_addr,s_add.sin_port);/*这里打印出的是小端
和我们平时看到的是相反的。*/
/*客户端连接服务器,参数依次为socket文件描述符,地址信息,地址结构大小*/
if(-1==connect(cfd,(structsockaddr*)(&s_add),sizeof(structsockaddr)))
{
printf("connectfail! ");
return-1;
}
printf("connectok! ");
/*连接成功,从服务端接收字符*/
if(-1==(recbytes=read(cfd,buffer,1024)))
{
printf("readdatafail! ");
return-1;
}
printf("readok REC: ");
buffer[recbytes]='