当前位置:首页 » 操作系统 » linuxsocket

linuxsocket

发布时间: 2022-04-24 17:16:25

linux下Socket编程 怎样实现客户端之间互相通信

  1. 网络的Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符。Socket也具有一个类似于打开文件的函数调用Socket(),该函数返回一个整型的Socket描述符,随后的连接建立、数据传输等操作都是通过该Socket实现的。

  2. 下面用Socket实现一个windows下的c语言socket通信例子,这里我们客户端传递一个字符串,服务器端进行接收。

【服务器端】
#include"stdafx.h"
#include<stdio.h>
#include<winsock2.h>
#include<winsock2.h>
#defineSERVER_PORT5208//侦听端口
voidmain()
{
WORDwVersionRequested;
WSADATAwsaData;
intret,nLeft,length;
SOCKETsListen,sServer;//侦听套接字,连接套接字
structsockaddr_insaServer,saClient;//地址信息
char*ptr;//用于遍历信息的指针
//WinSock初始化
wVersionRequested=MAKEWORD(2,2);//希望使用的WinSockDLL的版本
ret=WSAStartup(wVersionRequested,&wsaData);
if(ret!=0)
{
printf("WSAStartup()failed! ");
return;
}
//创建Socket,使用TCP协议
sListen=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(sListen==INVALID_SOCKET)
{
WSACleanup();
printf("socket()faild! ");
return;
}
//构建本地地址信息
saServer.sin_family=AF_INET;//地址家族
saServer.sin_port=htons(SERVER_PORT);//注意转化为网络字节序
saServer.sin_addr.S_un.S_addr=htonl(INADDR_ANY);//使用INADDR_ANY指示任意地址

//绑定
ret=bind(sListen,(structsockaddr*)&saServer,sizeof(saServer));
if(ret==SOCKET_ERROR)
{
printf("bind()faild!code:%d ",WSAGetLastError());
closesocket(sListen);//关闭套接字
WSACleanup();
return;
}

//侦听连接请求
ret=listen(sListen,5);
if(ret==SOCKET_ERROR)
{
printf("listen()faild!code:%d ",WSAGetLastError());
closesocket(sListen);//关闭套接字
return;
}

printf("Waitingforclientconnecting! ");
printf("Tips:Ctrl+ctoquit! ");
//阻塞等待接受客户端连接
while(1)//循环监听客户端,永远不停止,所以,在本项目中,我们没有心跳包。
{
length=sizeof(saClient);
sServer=accept(sListen,(structsockaddr*)&saClient,&length);
if(sServer==INVALID_SOCKET)
{
printf("accept()faild!code:%d ",WSAGetLastError());
closesocket(sListen);//关闭套接字
WSACleanup();
return;
}
charreceiveMessage[5000];
nLeft=sizeof(receiveMessage);
ptr=(char*)&receiveMessage;
while(nLeft>0)
{
//接收数据
ret=recv(sServer,ptr,5000,0);
if(ret==SOCKET_ERROR)
{
printf("recv()failed! ");
return;
}
if(ret==0)//客户端已经关闭连接
{
printf("Clienthasclosedtheconnection ");
break;
}
nLeft-=ret;
ptr+=ret;
}
printf("receivemessage:%s ",receiveMessage);//打印我们接收到的消息。

}
//closesocket(sListen);
//closesocket(sServer);
//WSACleanup();
}
【客户端】
#include"stdafx.h"
#include<stdio.h>
#include<stdlib.h>
#include<winsock2.h>
#defineSERVER_PORT5208//侦听端口
voidmain()
{
WORDwVersionRequested;
WSADATAwsaData;
intret;
SOCKETsClient;//连接套接字
structsockaddr_insaServer;//地址信息
char*ptr;
BOOLfSuccess=TRUE;
//WinSock初始化
wVersionRequested=MAKEWORD(2,2);//希望使用的WinSockDLL的版本
ret=WSAStartup(wVersionRequested,&wsaData);
if(ret!=0)
{
printf("WSAStartup()failed! ");
return;
}
//确认WinSockDLL支持版本2.2
if(LOBYTE(wsaData.wVersion)!=2||HIBYTE(wsaData.wVersion)!=2)
{
WSACleanup();
printf("InvalidWinSockversion! ");
return;
}
//创建Socket,使用TCP协议
sClient=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(sClient==INVALID_SOCKET)
{
WSACleanup();
printf("socket()failed! ");
return;
}
//构建服务器地址信息
saServer.sin_family=AF_INET;//地址家族
saServer.sin_port=htons(SERVER_PORT);//注意转化为网络节序
saServer.sin_addr.S_un.S_addr=inet_addr("192.168.1.127");
//连接服务器
ret=connect(sClient,(structsockaddr*)&saServer,sizeof(saServer));
if(ret==SOCKET_ERROR)
{
printf("connect()failed! ");
closesocket(sClient);//关闭套接字
WSACleanup();
return;
}


charsendMessage[]="hellothisisclientmessage!";
ret=send(sClient,(char*)&sendMessage,sizeof(sendMessage),0);
if(ret==SOCKET_ERROR)
{
printf("send()failed! ");
}
else
printf("clientinfohasbeensent!");
closesocket(sClient);//关闭套接字
WSACleanup();
}

㈡ linux下 socket函数的返回值代表什么

int socket;domain指明所使用的协议族,通常为PF_INET,表示互联网协议族;type参数指定socket的类型:SOCK_STREAM 或SOCK_DGRAM,Socket接口还定义了原始Socket,允许程序使用低层协议;protocol通常赋值"0"。

Socket()调用返回一个整型socket描述符,你可以在后面的调用使用它。 Socket描述符是一个指向内部数据结构的指针,它指向描述符表入口。

调用Socket函数时,socket执行体将建立一个Socket,实际上"建立一个Socket"意味着为一个Socket数据结构分配存储空间。 Socket执行体为你管理描述符表。

(2)linuxsocket扩展阅读:

支持下述类型描述:

SOCK_STREAM 提供有序的、可靠的、双向的和基于连接的字节流,使用带外数据传送机制,为Internet地址族使用TCP。

SOCK_DGRAM 支持无连接的、不可靠的和使用固定大小(通常很小)缓冲区的数据报服务,为Internet地址族使用UDP。

SOCK_STREAM类型的套接口为全双向的字节流。对于流类套接口,在接收或发送数据前必需处于已连接状态。用connect()调用建立与另一套接口的连接,连接成功后,即可用send()和recv()传送数据。当会话结束后,调用close()。带外数据根据规定用send()和recv()来接收。

㈢ 请问linux怎么增大socket连接上限

1、修改用户进程可打开文件数限制
在Linux平台上,无论编写客户端程序还是服务端程序,在进行高并发TCP连接处理时,
最高的并发数量都要受到系统对用户单一进程同时可打开文件数量的限制(这是因为系统
为每个TCP连接都要创建一个socket句柄,每个socket句柄同时也是一个文件句柄)。
可使用ulimit命令查看系统允许当前用户进程打开的文件数限制:
[speng@as4 ~]$ ulimit -n
1024
这表示当前用户的每个进程最多允许同时打开1024个文件,这1024个文件中还得除去
每个进程必然打开的标准输入,标准输出,标准错误,服务器监听 socket,
进程间通讯的unix域socket等文件,那么剩下的可用于客户端socket连接的文件数就
只有大概1024-10=1014个左右。也就是说缺省情况下,基于Linux的通讯程序最多允许
同时1014个TCP并发连接。
对于想支持更高数量的TCP并发连接的通讯处理程序,就必须修改Linux对当前用户的
进程同时打开的文件数量的软限制(soft limit)和硬限制(hardlimit)。其中软限制
是指Linux在当前系统能够承受的范围内进一步限制用户同时打开的文件数;硬限制
则是根据系统硬件资源状况(主要是系统内存)计算出来的系统最多可同时打开的文件数量。
通常软限制小于或等于硬限制。

修改上述限制的最简单的办法就是使用ulimit命令:
[speng@as4 ~]$ ulimit -n
上述命令中,在中指定要设置的单一进程允许打开的最大文件数。如果系统回显
类似于“Operation notpermitted”之类的话,说明上述限制修改失败,实际上是
因为在中指定的数值超过了Linux系统对该用户打开文件数的软限制或硬限制。
因此,就需要修改Linux系统对用户的关于打开文件数的软限制和硬限制。

第一步,修改/etc/security/limits.conf文件,在文件中添加如下行:
speng soft nofile 10240
speng hard nofile 10240
其中speng指定了要修改哪个用户的打开文件数限制,可用’*'号表示修改所有用户的限制;
soft或hard指定要修改软限制还是硬限制;10240则指定了想要修改的新的限制值,
即最大打开文件数(请注意软限制值要小于或等于硬限制)。修改完后保存文件。

第二步,修改/etc/pam.d/login文件,在文件中添加如下行:
session required /lib/security/pam_limits.so
这是告诉Linux在用户完成系统登录后,应该调用pam_limits.so模块来设置系统对
该用户可使用的各种资源数量的最大限制(包括用户可打开的最大文件数限制),
而pam_limits.so模块就会从/etc/security/limits.conf文件中读取配置来设置这些限制值。
修改完后保存此文件。

第三步,查看Linux系统级的最大打开文件数限制,使用如下命令:
[speng@as4 ~]$ cat /proc/sys/fs/file-max
12158
这表明这台Linux系统最多允许同时打开(即包含所有用户打开文件数总和)12158个文件,
是Linux系统级硬限制,所有用户级的打开文件数限制都不应超过这个数值。通常这个系统级
硬限制是Linux系统在启动时根据系统硬件资源状况计算出来的最佳的最大同时打开文件数限制,
如果没有特殊需要,不应该修改此限制,除非想为用户级打开文件数限制设置超过此限制的值。

修改此硬限制的方法是修改/etc/rc.local脚本,在脚本中添加如下行:
echo 22158 > /proc/sys/fs/file-max
这是让Linux在启动完成后强行将系统级打开文件数硬限制设置为22158。修改完后保存此文件。

完成上述步骤后重启系统,一般情况下就可以将Linux系统对指定用户的单一进程允许同时
打开的最大文件数限制设为指定的数值。如果重启后用 ulimit-n命令查看用户可打开文件数限制
仍然低于上述步骤中设置的最大值,这可能是因为在用户登录脚本/etc/profile中使用ulimit -n命令
已经将用户可同时打开的文件数做了限制。由于通过ulimit-n修改系统对用户可同时打开文件的
最大数限制时,新修改的值只能小于或等于上次 ulimit-n设置的值,因此想用此命令增大这个
限制值是不可能的。
所以,如果有上述问题存在,就只能去打开/etc/profile脚本文件,
在文件中查找是否使用了ulimit-n限制了用户可同时打开的最大文件数量,如果找到,
则删除这行命令,或者将其设置的值改为合适的值,然后保存文件,用户退出并重新登录系统即可。
通过上述步骤,就为支持高并发TCP连接处理的通讯处理程序解除关于打开文件数量方面的系统限制。
2、修改网络内核对TCP连接的有关限制
在Linux上编写支持高并发TCP连接的客户端通讯处理程序时,有时会发现尽管已经解除了系统
对用户同时打开文件数的限制,但仍会出现并发TCP连接数增加到一定数量时,再也无法成功
建立新的TCP连接的现象。出现这种现在的原因有多种。

第一种原因可能是因为Linux网络内核对本地端口号范围有限制。此时,进一步分析为什么无法
建立TCP连接,会发现问题出在connect()调用返回失败,查看系统错误提示消息是“Can’t assign requestedaddress”。同时,如果在此时用tcpmp工具监视网络,会发现根本没有TCP连接时客户端
发SYN包的网络流量。这些情况说明问题在于本地Linux系统内核中有限制。
其实,问题的根本原因
在于Linux内核的TCP/IP协议实现模块对系统中所有的客户端TCP连接对应的本地端口号的范围
进行了限制(例如,内核限制本地端口号的范围为1024~32768之间)。当系统中某一时刻同时
存在太多的TCP客户端连接时,由于每个TCP客户端连接都要占用一个唯一的本地端口号
(此端口号在系统的本地端口号范围限制中),如果现有的TCP客户端连接已将所有的本地端口号占满,
则此时就无法为新的TCP客户端连接分配一个本地端口号了,因此系统会在这种情况下在connect()
调用中返回失败,并将错误提示消息设为“Can’t assignrequested address”。
有关这些控制
逻辑可以查看Linux内核源代码,以linux2.6内核为例,可以查看tcp_ipv4.c文件中如下函数:
static int tcp_v4_hash_connect(struct sock *sk)
请注意上述函数中对变量sysctl_local_port_range的访问控制。变量sysctl_local_port_range
的初始化则是在tcp.c文件中的如下函数中设置:
void __init tcp_init(void)
内核编译时默认设置的本地端口号范围可能太小,因此需要修改此本地端口范围限制。
第一步,修改/etc/sysctl.conf文件,在文件中添加如下行:
net.ipv4.ip_local_port_range = 1024 65000
这表明将系统对本地端口范围限制设置为1024~65000之间。请注意,本地端口范围的最小值
必须大于或等于1024;而端口范围的最大值则应小于或等于65535。修改完后保存此文件。
第二步,执行sysctl命令:
[speng@as4 ~]$ sysctl -p
如果系统没有错误提示,就表明新的本地端口范围设置成功。如果按上述端口范围进行设置,
则理论上单独一个进程最多可以同时建立60000多个TCP客户端连接。

第二种无法建立TCP连接的原因可能是因为Linux网络内核的IP_TABLE防火墙对最大跟踪的TCP
连接数有限制。此时程序会表现为在 connect()调用中阻塞,如同死机,如果用tcpmp工具监视网络,
也会发现根本没有TCP连接时客户端发SYN包的网络流量。由于 IP_TABLE防火墙在内核中会对
每个TCP连接的状态进行跟踪,跟踪信息将会放在位于内核内存中的conntrackdatabase中,
这个数据库的大小有限,当系统中存在过多的TCP连接时,数据库容量不足,IP_TABLE无法为
新的TCP连接建立跟踪信息,于是表现为在connect()调用中阻塞。此时就必须修改内核对最大跟踪
的TCP连接数的限制,方法同修改内核对本地端口号范围的限制是类似的:

第一步,修改/etc/sysctl.conf文件,在文件中添加如下行:
net.ipv4.ip_conntrack_max = 10240
这表明将系统对最大跟踪的TCP连接数限制设置为10240。请注意,此限制值要尽量小,
以节省对内核内存的占用。

第二步,执行sysctl命令:
[speng@as4 ~]$ sysctl -p
如果系统没有错误提示,就表明系统对新的最大跟踪的TCP连接数限制修改成功。
如果按上述参数进行设置,则理论上单独一个进程最多可以同时建立10000多个TCP客户端连接。

3、使用支持高并发网络I/O的编程技术
在Linux上编写高并发TCP连接应用程序时,必须使用合适的网络I/O技术和I/O事件分派机制。
可用的I/O技术有同步I/O,非阻塞式同步I/O(也称反应式I/O),以及异步I/O。在高TCP并发的情形下,
如果使用同步I/O,这会严重阻塞程序的运转,除非为每个TCP连接的I/O创建一个线程。

但是,过多的线程又会因系统对线程的调度造成巨大开销。因此,在高TCP并发的情形下使用
同步 I/O是不可取的,这时可以考虑使用非阻塞式同步I/O或异步I/O。非阻塞式同步I/O的技术包括使用select(),poll(),epoll等机制。异步I/O的技术就是使用AIO。

从I/O事件分派机制来看,使用select()是不合适的,因为它所支持的并发连接数有限(通常在1024个以内)。
如果考虑性能,poll()也是不合适的,尽管它可以支持的较高的TCP并发数,但是由于其采用
“轮询”机制,当并发数较高时,其运行效率相当低,并可能存在I/O事件分派不均,导致部分TCP
连接上的I/O出现“饥饿”现象。而如果使用epoll或AIO,则没有上述问题(早期Linux内核的AIO技术
实现是通过在内核中为每个 I/O请求创建一个线程来实现的,这种实现机制在高并发TCP连接的情形下
使用其实也有严重的性能问题。但在最新的Linux内核中,AIO的实现已经得到改进)。

综上所述,在开发支持高并发TCP连接的Linux应用程序时,应尽量使用epoll或AIO技术来实现并发的
TCP连接上的I/O控制,这将为提升程序对高并发TCP连接的支持提供有效的I/O保证。

内核参数sysctl.conf的优化

/etc/sysctl.conf 是用来控制linux网络的配置文件,对于依赖网络的程序(如web服务器和cache服务器)
非常重要,RHEL默认提供的最好调整。

推荐配置(把原/etc/sysctl.conf内容清掉,把下面内容复制进去):
net.ipv4.ip_local_port_range = 1024 65536
net.core.rmem_max=16777216
net.core.wmem_max=16777216
net.ipv4.tcp_rmem=4096 87380 16777216
net.ipv4.tcp_wmem=4096 65536 16777216
net.ipv4.tcp_fin_timeout = 10
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_timestamps = 0
net.ipv4.tcp_window_scaling = 0
net.ipv4.tcp_sack = 0
net.core.netdev_max_backlog = 30000
net.ipv4.tcp_no_metrics_save=1
net.core.somaxconn = 262144
net.ipv4.tcp_syncookies = 0
net.ipv4.tcp_max_orphans = 262144
net.ipv4.tcp_max_syn_backlog = 262144
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 2

这个配置参考于cache服务器varnish的推荐配置和SunOne 服务器系统优化的推荐配置。

varnish调优推荐配置的地址为:http://varnish.projects.linpro.no/wiki/Performance

不过varnish推荐的配置是有问题的,实际运行表明“net.ipv4.tcp_fin_timeout = 3”的配置
会导致页面经常打不开;并且当网友使用的是IE6浏览器时,访问网站一段时间后,所有网页都会
打不开,重启浏览器后正常。可能是国外的网速快吧,我们国情决定需要
调整“net.ipv4.tcp_fin_timeout = 10”,在10s的情况下,一切正常(实际运行结论)。

修改完毕后,执行:
/sbin/sysctl -p /etc/sysctl.conf
/sbin/sysctl -w net.ipv4.route.flush=1

命令生效。为了保险起见,也可以reboot系统。

调整文件数:
linux系统优化完网络必须调高系统允许打开的文件数才能支持大的并发,默认1024是远远不够的。

执行命令:
Shell代码
echo ulimit -HSn 65536 >> /etc/rc.local
echo ulimit -HSn 65536 >>/root/.bash_profile
ulimit -HSn 65536

㈣ linux socket和sock结构体的区别

//**************************************************************************
/* 1、每一个打开的文件、socket等等都用一个file数据结构代表,这样文件和socket就通过inode->u(union)中的各个成员来区别:
struct inode {
.....................
union {
struct ext2_inode_info ext2_i;
struct ext3_inode_info ext3_i;
struct socket socket_i;
.....................
} u; };
2、每个socket数据结构都有一个sock数据结构成员,sock是对socket的扩充,两者一一对应,socket->sk指向对应的sock,sock->socket
指向对应的socket;
3、socket和sock是同一事物的两个侧面,为什么不把两个数据结构合并成一个呢?这是因为socket是inode结构中的一部分,即把inode结
构内部的一个union用作socket结构。由于插口操作的特殊性,这个数据结构中需要有大量的结构成分,如果把这些成分全部放到socket
结构中,则inode结构中的这个union就会变得很大,从而inode结构也会变得很大,而对于其他文件系统这个union是不需要这么大的,
所以会造成巨大浪费,系统中使用inode结构的数量要远远超过使用socket的数量,故解决的办法就是把插口分成两部分,把与文件系
统关系密切的放在socket结构中,把与通信关系密切的放在另一个单独结构sock中;
*/

struct socket
{
socket_state state; // 该state用来表明该socket的当前状态
typedef enum {
SS_FREE = 0, /* not allocated */
SS_UNCONNECTED, /* unconnected to any socket */
SS_CONNECTING, /* in process of connecting */
SS_CONNECTED, /* connected to socket */
SS_DISCONNECTING /* in process of disconnecting */
} socket_state;
unsigned long flags; //该成员可能的值如下,该标志用来设置socket是否正在忙碌
#define SOCK_ASYNC_NOSPACE 0
#define SOCK_ASYNC_WAITDATA 1
#define SOCK_NOSPACE 2
struct proto_ops *ops; //依据协议邦定到该socket上的特定的协议族的操作函数指针,例如IPv4 TCP就是inet_stream_ops
struct inode *inode; //表明该socket所属的inode
struct fasync_struct *fasync_list; //异步唤醒队列
struct file *file; //file回指指针
struct sock *sk; //sock指针
wait_queue_head_t wait; //sock的等待队列,在TCP需要等待时就sleep在这个队列上
short type; //表示该socket在特定协议族下的类型例如SOCK_STREAM,
unsigned char passcred; //在TCP分析中无须考虑
};

㈤ Linux操作系统下Socket编程地址结构介绍

linux下的网络通信程序,一定要和一个结构打交道,这个结构就是socket
address。比如bind、connect等等函数都要使用socket
address结构。理解socket
address时我们要明白,其实在linux下针对于不同的socket
domain定义了一个通用的地址结构struct
sockaddr,它的具体定义为:
{unsigned
short
int
sa_family;char
sa_data[14];}
struct
sockaddr
其中,sa_family为调用socket()函数时的参数domain参数,sa_data为14个字符长度存储。针对于不同domain下的socket,通用地址结构又对应了不同的定义,例如一般的AF_INET
domain下,socket
address的定义如下:
struct
sockaddr_in{unsigned
short
int
sin_family;uint16_t
sin_port;struct
in_addr
sin_addr;unsigned
char
sin_zero[8];//未使用}struct
in_addr{uint32_t
s_addr;}
当socket的domain不同于AF_INET时,具体的地址定义又是不同的,但是整个地址结构的大小、容量都是和通用地址结构一致的。

㈥ linux socket 连接超时 怎么解决

今天发现自己的系统存在很严重缺陷,当前台关闭的时候后台就无法正常工作,原因很好定位,后台的socket连接超时时间过长,系统默认时间好像是75秒,于是找资料,根据下边文章中的内容解决了,把超时时间设为5秒后,感觉好多了。看来还有好多东西需要慢慢挖掘阿!

如何设置socket的Connect超时(linux)
[From]http://dev.cbw.com/c/c/200510195601_4292587.shtml
1.首先将标志位设为Non-blocking模式,准备在非阻塞模式下调用connect函数
2.调用connect,正常情况下,因为TCP三次握手需要一些时间;而非阻塞调用只要不能立即完成就会返回错误,所以这里会返回EINPROGRESS,表示在建立连接但还没有完成。
3.在读套接口描述符集(fd_set rset)和写套接口描述符集(fd_set wset)中将当前套接口置位(用FD_ZERO()、FD_SET()宏),并设置好超时时间(struct timeval *timeout)
4.调用select( socket, &rset, &wset, NULL, timeout )
返回0表示connect超时
如果你设置的超时时间大于75秒就没有必要这样做了,因为内核中对connect有超时限制就是75秒。

[From]http://www.ycgczj.com.cn/34733.html
网络编程中socket的分量我想大家都很清楚了,socket也就是套接口,在套接口编程中,提到超时的概念,我们一下子就能想到3个:发送超时,接收超时,以及select超时(注: select函数并不是只用于套接口的,但是套接口编程中用的比较多),在connect到目标主机的时候,这个超时是不由我们来设置的。不过正常情况下这个超时都很长,并且connect又是一个阻塞方法,一个主机不能连接,等着connect返回还能忍受,你的程序要是要试图连接多个主机,恐怕遇到多个不能连接的主机的时候,会塞得你受不了的。我也废话少说,先说说我的方法,如果你觉得你已掌握这种方法,你就不用再看下去了,如果你还不了解,我愿意与你分享。本文是已在Linux下的程序为例子,不过拿到Windows中方法也是一样,无非是换几个函数名字罢了。
Linux中要给connect设置超时,应该是有两种方法的。一种是该系统的一些参数,这个方法我不讲,因为我讲不清楚:P,它也不是编程实现的。另外一种方法就是变相的实现connect的超时,我要讲的就是这个方法,原理上是这样的:
1.建立socket
2.将该socket设置为非阻塞模式
3.调用connect()
4.使用select()检查该socket描述符是否可写(注意,是可写)
5.根据select()返回的结果判断connect()结果
6.将socket设置为阻塞模式(如果你的程序不需要用阻塞模式的,这步就省了,不过一般情况下都是用阻塞模式的,这样也容易管理)
如果你对网络编程很熟悉的话,其实我一说出这个过程你就知道怎么写你的程序了,下面给出我写的一段程序,仅供参考。
/******************************
* Time out for connect()
* Write by Kerl W
******************************/
#include <sys/socket.h>
#include <sys/types.h>
#define TIME_OUT_TIME 20 //connect超时时间20秒
int main(int argc , char **argv)
{
………………
int sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0) exit(1);
struct sockaddr_in serv_addr;
………//以服务器地址填充结构serv_addr
int error=-1, len;
len = sizeof(int);
timeval tm;
fd_set set;
unsigned long ul = 1;
ioctl(sockfd, FIONBIO, &ul); //设置为非阻塞模式
bool ret = false;
if( connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)
{
tm.tv_set = TIME_OUT_TIME;
tm.tv_uset = 0;
FD_ZERO(&set);
FD_SET(sockfd, &set);
if( select(sockfd+1, NULL, &set, NULL, &tm) > 0)
{
getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len);
if(error == 0) ret = true;
else ret = false;
} else ret = false;
}
else ret = true;
ul = 0;
ioctl(sockfd, FIONBIO, &ul); //设置为阻塞模式
if(!ret)
{
close( sockfd );
fprintf(stderr , "Cannot Connect the server!n");
return;
}
fprintf( stderr , "Connected!n");
//下面还可以进行发包收包操作
……………
}
以上代码片段,仅供参考,也是为初学者提供一些提示,主要用到的几个函数,select, ioctl, getsockopt都可以找到相关资料,具体用法我这里就不赘述了,你只需要在linux中轻轻的敲一个man <函数名>就能够看到它的用法。
此外我需要说明的几点是,虽然我们用ioctl把套接口设置为非阻塞模式,不过select本身是阻塞的,阻塞的时间就是其超时的时间由调用select 的时候的最后一个参数timeval类型的变量指针指向的timeval结构变量来决定的,timeval结构由一个表示秒数的和一个表示微秒数(long类型)的成员组成,一般我们设置了秒数就行了,把微妙数设为0(注:1秒等于100万微秒)。而select函数另一个值得一提的参数就是上面我们用到的fd_set类型的变量指针。调用之前,这个变量里面存了要用select来检查的描述符,调用之后,针对上面的程序这里面是可写的描述符,我们可以用宏FD_ISSET来检查某个描述符是否在其中。由于我这里只有一个套接口描述符,我就没有使用FD_ISSET宏来检查调用select之后这个sockfd是否在set里面,其实是需要加上这个判断的。不过我用了getsockopt来检查,这样才可以判断出这个套接口是否是真的连接上了,因为我们只是变相的用select来检查它是否连接上了,实际上select检查的是它是否可写,而对于可写,是针对以下三种条件任一条件满足时都表示可写的:
1)套接口发送缓冲区中的可用控件字节数大于等于套接口发送缓冲区低潮限度的当前值,且或者i)套接口已连接,或者ii)套接口不要求连接(UDP方式的)
2)连接的写这一半关闭。
3)有一个套接口错误待处理。
这样,我们就需要用getsockopt函数来获取套接口目前的一些信息来判断是否真的是连接上了,没有连接上的时候还能给出发生了什么错误,当然我程序中并没有标出那么多状态,只是简单的表示可连接/不可连接。
下面我来谈谈对这个程序测试的结果。我针对3种情形做了测试:
1. 目标机器网络正常的情况
可以连接到目标主机,并能成功以阻塞方式进行发包收包作业。
2. 目标机器网络断开的情况
在等待设置的超时时间(上面的程序中为20秒)后,显示目标主机不能连接。
3. 程序运行前断开目标机器网络,超时时间内,恢复目标机器的网络
在恢复目标主机网络连接之前,程序一只等待,恢复目标主机后,程序显示连接目标主机成功,并能成功以阻塞方式进行发包收包作业。
以上各种情况的测试结果表明,这种设置connect超时的方法是完全可行的。我自己是把这种设置了超时的connect封装到了自己的类库,用在一套监控系统中,到目前为止,运行还算正常。这种编程实现的connect超时比起修改系统参数的那种方法的有点就在于它只用于你的程序之中而不影响系统。

㈦ linux socket是什么意思

基于Linux的SOCKET编程。

㈧ linux中socket是如何调用驱动程序

分为发送和接受:
发送:
首先,socketAPI会创建并把数据至一个叫sk_buff的结构体,然后依次把sk_buff交给运输层,网络层,数据链路层协议进行处理,然后在填写完sk_buff后再把他交付给驱动程序由网络设备发送出去。
接受和发送是反过程,驱动层程序一般由中断处理收到数据包后会创建sk_buff结构体,让后把数据和一些控制信息填进去,再把sk_buff向数据链路层协议交付,然后就是网络层,运输层最后交给socketAPI接口了

㈨ LINUX网络编程中socket函数详细解释下吧

函数原型:int socket(int family,int type,int protocol); 参数解释:family指定协议簇,UNIX下(包括linux)有:AF_INET,AF_INET6,AF_LOCAL,AF_ROUTE,AF_KEY,分别是IPv4,IPv6协议,UNIX域协议,路由套接口,密钥套接口; type指定套接口类型:unix下有四种,分别是:SOCK_STREAM,SOCK_DGRAM,SOCK_SEQPACKET,SOCK_RAW。(流套接字,数据报套接字,有序分组套接字,原始套接字),SOCK_SEQPACKET已很少使用; protocol:IPPROTO_TCP,IPPROTO_UDP,IPPROTO_SCTP,分别是TCP传输协议,UDP传输协议,SCTP传输协议; 函数返回一个非负整数值,类似于文件描述符(linux内核知识),称之为套接口描述字,简称套接字。《Linux就该这么学》这本书,希望你能感受到linux系统和这本书带给你的好处及帮助。

㈩ linux socket 怎么处理大量的数据

1、 引言
Linux的兴起可以说是Internet创造的一个奇迹。Linux作为一个完全开放其原代码的免费的自由软件,兼容了各种UNIX标准(如POSIX、UNIX System V 和 BSD UNIX 等)的多用户、多任务的具有复杂内核的操作系统。在中国,随着Internet的普及,一批主要以高等院校的学生和ISP的技术人员组成的Linux爱好者队伍已经蓬勃成长起来。越来越多的编程爱好者也逐渐酷爱上这个优秀的自由软件。本文介绍了Linux下Socket的基本概念和函数调用。

2、 什么是Socket
Socket(套接字)是通过标准的UNIX文件描述符和其它程序通讯的一个方法。每一个套接字都用一个半相关描述:{协议,本地地址、本地端口}来表示;一个完整的套接字则用一个相关描述:{协议,本地地址、本地端口、远程地址、远程端口},每一个套接字都有一个本地的由操作系统分配的唯一的套接字号。

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

4、 利用套接字发送数据
1、 对于流式套接字用系统调用send()来发送数据。
2、 对于数据报套接字,则需要自己先加一个信息头,然后调用sendto()函数把数据发送出去。

5、 Linux中Socket的数据结构
(1) struct sockaddr { //用于存储套接字地址
unsigned short sa_family;//地址类型
char sa_data[14]; //14字节的协议地址
};
(2) struct sockaddr_in{ //in 代表internet
short int sin_family; //internet协议族
unsigned short int sin_port;//端口号,必须是网络字节顺序
struct in_addr sin_addr;//internet地址,必须是网络字节顺序
unsigned char sin_zero;//添0(和struct sockaddr一样大小
};
(3) struct in_addr{
unsigned long s_addr;
};

6、 网络字节顺序及其转换函数
(1) 网络字节顺序
每一台机器内部对变量的字节存储顺序不同,而网络传输的数据是一定要统一顺序的。所以对内部字节表示顺序与网络字节顺序不同的机器,一定要对数据进行转换,从程序的可移植性要求来讲,就算本机的内部字节表示顺序与网络字节顺序相同也应该在传输数据以前先调用数据转换函数,以便程序移植到其它机器上后能正确执行。真正转换还是不转换是由系统函数自己来决定的。
(2) 有关的转换函数
* unsigned short int htons(unsigned short int hostshort):
主机字节顺序转换成网络字节顺序,对无符号短型进行操作4bytes
* unsigned long int htonl(unsigned long int hostlong):
主机字节顺序转换成网络字节顺序,对无符号长型进行操作8bytes
* unsigned short int ntohs(unsigned short int netshort):
网络字节顺序转换成主机字节顺序,对无符号短型进行操作4bytes
* unsigned long int ntohl(unsigned long int netlong):
网络字节顺序转换成主机字节顺序,对无符号长型进行操作8bytes
注:以上函数原型定义在netinet/in.h里

7、 IP地址转换
有三个函数将数字点形式表示的字符串IP地址与32位网络字节顺序的二进制形式的IP地址进行转换
(1) unsigned long int inet_addr(const char * cp):该函数把一个用数字和点表示的IP地址的字符串转换成一个无符号长整型,如:struct sockaddr_in ina
ina.sin_addr.s_addr=inet_addr("202.206.17.101")
该函数成功时:返回转换结果;失败时返回常量INADDR_NONE,该常量=-1,二进制的无符号整数-1相当于255.255.255.255,这是一个广播地址,所以在程序中调用iner_addr()时,一定要人为地对调用失败进行处理。由于该函数不能处理广播地址,所以在程序中应该使用函数inet_aton()。
(2)int inet_aton(const char * cp,struct in_addr * inp):此函数将字符串形式的IP地址转换成二进制形式的IP地址;成功时返回1,否则返回0,转换后的IP地址存储在参数inp中。
(3) char * inet_ntoa(struct in-addr in):将32位二进制形式的IP地址转换为数字点形式的IP地址,结果在函数返回值中返回,返回的是一个指向字符串的指针。

8、 字节处理函数
Socket地址是多字节数据,不是以空字符结尾的,这和C语言中的字符串是不同的。Linux提供了两组函数来处理多字节数据,一组以b(byte)开头,是和BSD系统兼容的函数,另一组以mem(内存)开头,是ANSI C提供的函数。
以b开头的函数有:
(1) void bzero(void * s,int n):将参数s指定的内存的前n个字节设置为0,通常它用来将套接字地址清0。
(2) void b(const void * src,void * dest,int n):从参数src指定的内存区域拷贝指定数目的字节内容到参数dest指定的内存区域。
(3) int bcmp(const void * s1,const void * s2,int n):比较参数s1指定的内存区域和参数s2指定的内存区域的前n个字节内容,如果相同则返回0,否则返回非0。
注:以上函数的原型定义在strings.h中。
以mem开头的函数有:
(1) void * memset(void * s,int c,size_t n):将参数s指定的内存区域的前n个字节设置为参数c的内容。
(2) void * memcpy(void * dest,const void * src,size_t n):功能同b(),区别:函数b()能处理参数src和参数dest所指定的区域有重叠的情况,memcpy()则不能。
(4) int memcmp(const void * s1,const void * s2,size_t n):比较参数s1和参数s2指定区域的前n个字节内容,如果相同则返回0,否则返回非0。
注:以上函数的原型定义在string.h中。

9、 基本套接字函数
(1) socket()
#include< sys/types.h>
#include< sys/socket.h>
int socket(int domain,int type,int protocol)
参数domain指定要创建的套接字的协议族,可以是如下值:
AF_UNIX //UNIX域协议族,本机的进程间通讯时使用
AF_INET //Internet协议族(TCP/IP)
AF_ISO //ISO协议族
参数type指定套接字类型,可以是如下值:
SOCK_STREAM //流套接字,面向连接的和可靠的通信类型
SOCK_DGRAM //数据报套接字,非面向连接的和不可靠的通信类型
SOCK_RAW //原始套接字,只对Internet协议有效,可以用来直接访问IP协议
参数protocol通常设置成0,表示使用默认协议,如Internet协议族的流套接字使用TCP协议,而数据报套接字使用UDP协议。当套接字是原始套接字类型时,需要指定参数protocol,因为原始套接字对多种协议有效,如ICMP和IGMP等。
Linux系统中创建一个套接字的操作主要是:在内核中创建一个套接字数据结构,然后返回一个套接字描述符标识这个套接字数据结构。这个套接字数据结构包含连接的各种信息,如对方地址、TCP状态以及发送和接收缓冲区等等,TCP协议根据这个套接字数据结构的内容来控制这条连接。
(2) 函数connect()
#include< sys/types.h>
#include< sys/socket.h>
int connect(int sockfd,struct sockaddr * servaddr,int addrlen)
参数sockfd是函数socket返回的套接字描述符;参数servaddr指定远程服务器的套接字地址,包括服务器的IP地址和端口号;参数addrlen指定这个套接字地址的长度。成功时返回0,否则返回-1,并设置全局变量为以下任何一种错误类型:ETIMEOUT、ECONNREFUSED、EHOSTUNREACH或ENETUNREACH。
在调用函数connect之前,客户机需要指定服务器进程的套接字地址。客户机一般不需要指定自己的套接字地址(IP地址和端口号),系统会自动从1024至5000的端口号范围内为它选择一个未用的端口号,然后以这个端口号和本机的IP地址填充这个套接字地址。
客户机调用函数connect来主动建立连接。这个函数将启动TCP协议的3次握手过程。在建立连接之后或发生错误时函数返回。连接过程可能出现的错误情况有:
(1) 如果客户机TCP协议没有接收到对它的SYN数据段的确认,函数以错误返回,错误类型为ETIMEOUT。通常TCP协议在发送SYN数据段失败之后,会多次发送SYN数据段,在所有的发送都高中失败之后,函数以错误返回。
注:SYN(synchronize)位:请求连接。TCP用这种数据段向对方TCP协议请求建立连接。在这个数据段中,TCP协议将它选择的初始序列号通知对方,并且与对方协议协商最大数据段大小。SYN数据段的序列号为初始序列号,这个SYN数据段能够被确认。当协议接收到对这个数据段的确认之后,建立TCP连接。
(2) 如果远程TCP协议返回一个RST数据段,函数立即以错误返回,错误类型为ECONNREFUSED。当远程机器在SYN数据段指定的目的端口号处没有服务进程在等待连接时,远程机器的TCP协议将发送一个RST数据段,向客户机报告这个错误。客户机的TCP协议在接收到RST数据段后不再继续发送SYN数据段,函数立即以错误返回。
注:RST(reset)位:表示请求重置连接。当TCP协议接收到一个不能处理的数据段时,向对方TCP协议发送这种数据段,表示这个数据段所标识的连接出现了某种错误,请求TCP协议将这个连接清除。有3种情况可能导致TCP协议发送RST数据段:(1)SYN数据段指定的目的端口处没有接收进程在等待;(2)TCP协议想放弃一个已经存在的连接;(3)TCP接收到一个数据段,但是这个数据段所标识的连接不存在。接收到RST数据段的TCP协议立即将这条连接非正常地断开,并向应用程序报告错误。
(3) 如果客户机的SYN数据段导致某个路由器产生“目的地不可到达”类型的ICMP消息,函数以错误返回,错误类型为EHOSTUNREACH或ENETUNREACH。通常TCP协议在接收到这个ICMP消息之后,记录这个消息,然后继续几次发送SYN数据段,在所有的发送都告失败之后,TCP协议检查这个ICMP消息,函数以错误返回。
注:ICMP:Internet 消息控制协议。Internet的运行主要是由Internet的路由器来控制,路由器完成IP数据包的发送和接收,如果发送数据包时发生错误,路由器使用ICMP协议来报告这些错误。ICMP数据包是封装在IP数据包的数据部分中进行传输的,其格式如下:
类型

校验和
数据
0 8 16 24 31
类型:指出ICMP数据包的类型。
代码:提供ICMP数据包的进一步信息。
校验和:提供了对整个ICMP数据包内容的校验和。
ICMP数据包主要有以下类型:
(1) 目的地不可到达:A、目的主机未运行;B、目的地址不存在;C、路由表中没有目的地址对应的条目,因而路由器无法找到去往目的主机的路由。
(2) 超时:路由器将接收到的IP数据包的生存时间(TTL)域减1,如果这个域的值变为0,路由器丢弃这个IP数据包,并且发送这种ICMP消息。
(3) 参数出错:当IP数据包中有无效域时发送。
(4) 重定向:将一条新的路径通知主机。
(5) ECHO请求、ECHO回答:这两条消息用语测试目的主机是否可以到达。请求者向目的主机发送ECHO请求ICMP数据包,目的主机在接收到这个ICMP数据包之后,返回ECHO回答ICMP数据包。
(6) 时戳请求、时戳回答:ICMP协议使用这两种消息从其他机器处获得其时钟的当前时间。

调用函数connect的过程中,当客户机TCP协议发送了SYN数据段的确认之后,TCP状态由CLOSED状态转为SYN_SENT状态,在接收到对SYN数据段的确认之后,TCP状态转换成ESTABLISHED状态,函数成功返回。如果调用函数connect失败,应该用close关闭这个套接字描述符,不能再次使用这个套接字描述符来调用函数connect。

注:TCP协议状态转换图:

被动OPEN CLOSE 主动OPEN
(建立TCB) (删除TCB) (建立TCB,
发送SYN)
接收SYN SEND
(发送SYN,ACK) (发送SYN)

接收SYN的ACK(无动作)
接收SYN的ACK 接收SYN,ACK
(无动作) (发送ACK)
CLOSE
(发送FIN) CLOSE 接收FIN
(发送FIN) (发送FIN)

接收FIN
接收FIN的ACK(无动作) (发送ACK) CLOSE(发送FIN)

接收FIN 接收FIN的ACK 接收FIN的ACK
(发送ACK) (无动作) (无动作)

2MSL超时(删除TCB)
(3) 函数bind()
函数bind将本地地址与套接字绑定在一起,其定义如下:
#include< sys/types.h>
#include< sys/socket.h>
int bind(int sockfd,struct sockaddr * myaddr,int addrlen);
参数sockfd是函数sockt返回的套接字描述符;参数myaddr是本地地址;参数addrlen是套接字地址结构的长度。执行成功时返回0,否则,返回-1,并设置全局变量errno为错误类型EADDRINUSER。
服务器和客户机都可以调用函数bind来绑定套接字地址,但一般是服务器调用函数bind来绑定自己的公认端口号。绑定操作一般有如下几种组合方式:
表1
程序类型
IP地址
端口号
说明
服务器
INADDR_ANY
非零值
指定服务器的公认端口号
服务器
本地IP地址
非零值
指定服务器的IP地址和公认端口号
客户机
INADDR_ANY
非零值
指定客户机的连接端口号
客户机
本地IP地址
非零值
指定客户机的IP地址连接端口号
客户机
本地IP地址

指定客户机的IP地址
分别说明如下:
(1) 服务器指定套接字地址的公认端口号,不指定IP地址:即服务器调用bind时,设置套接字的IP地址为特殊的INADDE-ANY,表示它愿意接收来自任何网络设备接口的客户机连接。这是服务器最常用的绑定方式。
(2) 服务器指定套接字地址的公认端口号和IP地址:服务器调用bind时,如果设置套接字的IP地址为某个本地IP地址,这表示这台机器只接收来自对应于这个IP地址的特定网络设备接口的客户机连接。当服务器有多块网卡时,可以用这种方式来限制服务器的接收范围。
(3) 客户机指定套接字地址的连接端口号:一般情况下,客户机调用connect函数时不用指定自己的套接字地址的端口号。系统会自动为它选择一个未用的端口号,并且用本地的IP地址来填充套接字地址中的相应项。但有时客户机需要使用一个特定的端口号(比如保留端口号),而系统不会未客户机自动分配一个保留端口号,所以需要调用函数bind来和一个未用的保留端口号绑定。
(4) 指定客户机的IP地址和连接端口号:表示客户机使用指定的网络设备接口和端口号进行通信。
(5) 指定客户机的IP地址:表示客户机使用指定的网络设备接口和端口号进行通信,系统自动为客户机选一个未用的端口号。一般只有在主机有多个网络设备接口时使用。
我们一般不在客户机上使用固定的客户机端口号,除非是必须使用的情况。在客户机上使用固定的端口号有以下不利:
(1) 服务器执行主动关闭操作:服务器最后进入TIME_WAIT状态。当客户机再次与这个服务器进行连接时,仍使用相同的客户机端口号,于是这个连接与前次连接的套接字对完全一样,但是一呢、为前次连接处于TIME_WAIT状态,并未消失,所以这次连接请求被拒绝,函connect以错误返回,错误类型为ECONNREFUSED
(2) 客户机执行主动关闭操作:客户机最后进入TIME_WAIT状态。当马上再次执行这个客户机程序时,客户机将继续与这个固定客户机端口号绑定,但因为前次连接处于TIME_WAIT状态,并未消失,系统会发现这个端口号仍被占用,所以这次绑定操作失败,函数bind以错误返回,错误类型为EADDRINUSE。
(4) 函数listen()
函数listen将一个套接字转换为征听套接字,定义如下;
#include< sys/socket,h>
int listen(int sockfd,int backlog)
参数sockfd指定要转换的套接字描述符;参数backlog设置请求队列的最大长度;执行成功时返回0, 否则返回-1。函数listen功能有两个:
(1) 将一个尚未连接的主动套接字(函数socket创建的可以用来进行主动连接但不能接受连接请求的套接字)转换成一个被动连接套接字。执行listen之后,服务器的TCP状态由CLOSED转为LISTEN状态。
(2) TCP协议将到达的连接请求队列,函数listen的第二个参数指定这个队列的最大长度。
注:参数backlog的作用:
TCP协议为每一个征听套接字维护两个队列:
(1) 未完成连接队列:每个尚未完成3次握手操作的TCP连接在这个队列中占有一项。TCP希望仪在接收到一个客户机SYN数据段之后,在这个队列中创建一个新条目,然后发送对客户机SYN数据段的确认和自己的SYN数据段(ACK+SYN数据段),等待客户机对自己的SYN数据段的确认。此时,套接字处于SYN_RCVD状态。这个条目将保存在这个队列中,直到客户机返回对SYN数据段的确认或者连接超时。
(2) 完成连接队列:每个已经完成3次握手操作,但尚未被应用程序接收(调用函数accept)的TCP连接在这个队列中占有一项。当一个在未完成连接队列中的连接接收到对SYN数据段的确认之后,完成3次握手操作,TCP协议将它从未完成连接队列移到完成连接队列中。此时,套接字处于ESTABLISHED状态。这个条目将保存在这个队列中,直到应用程序调用函数accept来接收它。
参数backlog指定某个征听套接字的完成连接队列的最大长度,表示这个套接字能够接收的最大数目的未接收连接。如果当一个客户机的SYN数据段到达时,征听套接字的完成队列已经满了,那么TCP协议将忽略这个SYN数据段。对于不能接收的SYN数据段,TCP协议不发送RST数据段,
(5) 函数accept()
函数accept从征听套接字的完成队列中接收一个已经建立起来的TCP连接。如果完成连接队列为空,那么这个进程睡眠。
#include< sys/socket.h>
int accept(int sockfd,struct sockaddr * addr,int * addrlen)
参数sockfd指定征听套接字描述符;参数addr为指向一个Internet套接字地址结构的指针;参数addrlen为指向一个整型变量的指针。执行成功时,返回3个结果:函数返回值为一个新的套接字描述符,标识这个接收的连接;参数addr指向的结构变量中存储客户机地址;参数addrlen指向的整型变量中存储客户机地址的长度。失败时返回-1。
征听套接字专为接收客户机连接请求,完成3次握手操作而用的,所以TCP协议不能使用征听套接字描述符来标识这个连接,于是TCP协议创建一个新的套接字来标识这个要接收的连接,并将它的描述符发挥给应用程序。现在有两个套接字,一个是调用函数accept时使用的征听套接字,另一个是函数accept返回的连接套接字(connected socket)。一个服务器通常只需创建一个征听套接字,在服务器进程的整个活动期间,用它来接收所有客户机的连接请求,在服务器进程终止前关闭这个征听套接字;对于没一个接收的(accepted)连接,TCP协议都创建一个新的连接套接字来标识这个连接,服务器使用这个连接套接字与客户机进行通信操作,当服务器处理完这个客户机请求时,关闭这个连接套接字。
当函数accept阻塞等待已经建立的连接时,如果进程捕获到信号,函数将以错误返回,错误类型为EINTR。对于这种错误,一般重新调用函数accept来接收连接。
(6) 函数close()
函数close关闭一个套接字描述符。定义如下:
#include< unistd.h>
int close(int sockfd);
执行成功时返回0,否则返回-1。与操作文件描述符的close一样,函数close将套接字描述符的引用计数器减1,如果描述符的引用计数大于0,则表示还有进程引用这个描述符,函数close正常返回;如果为0,则启动清除套接字描述符的操作,函数close立即正常返回。
调用close之后,进程将不再能够访问这个套接字,但TCP协议将继续使用这个套接字,将尚未发送的数据传递到对方,然后发送FIN数据段,执行关闭操作,一直等到这个TCP连接完全关闭之后,TCP协议才删除该套接字。
(7) 函数read()和write()
用于从套接字读写数据。定义如下:
int read(int fd,char * buf,int len)
int write(int fd,char * buf,int len)
函数执行成功时,返回读或写的数据量的大小,失败时返回-1。
每个TCP套接字都有两个缓冲区:套接字发送缓冲区、套接字接收缓冲区,分别处理发送和接收任务。从网络读、写数据的操作是由TCP协议在内核中完成的:TCP协议将从网络上接收到的数据保存在相应套接字的接收缓冲区中,等待用户调用函数将它们从接收缓冲区拷贝到用户缓冲区;用户将要发送的数据拷贝到相应套接字的发送缓冲区中,然后由TCP协议按照一定的算法处理这些数据。
读写连接套接字的操作与读写文件的操作类似,也可以使用函数read和write。函数read完成将数据从套接字接收缓冲区拷贝到用户缓冲区:当套接字接收缓冲区有数据可读时,1:可读数据量大于函数read指定值,返回函数参数len指定的数据量;2:了度数据量小于函数read指定值,函数read不等待请求的所有数据都到达,而是立即返回实际读到的数据量;当无数据可读时,函数read将阻塞不返回,等待数据到达。
当TCP协议接收到FIN数据段,相当于给读操作一个文件结束符,此时read函数返回0,并且以后所有在这个套接字上的读操作均返回0,这和普通文件中遇到文件结束符是一样的。
当TCP协议接收到RST数据段,表示连接出现了某种错误,函数read将以错误返回,错误类型为ECONNERESET。并且以后所有在这个套接字上的读操作均返回错误。错误返回时返回值小于0。
函数write完成将数据从用户缓冲区拷贝到套接字发送缓冲区的任务:到套接字发送缓冲区有足够拷贝所有用户数据的空间时,函数write将数据拷贝到这个缓冲区中,并返回老辈的数量大小,如果可用空间小于write参数len指定的大小时,函数write将阻塞不返回,等待缓冲区有足够的空间。
当TCP协议接收到RST数据段(当对方已经关闭了这条连接之后,继续向这个套接字发送数据将导致对方TCP协议返回RST数据段),TCP协议接收到RST数据段时,函数write将以错误返回,错误类型为EINTR。以后可以继续在这个套接字上写数据。
(8) 函数getsockname()和getpeername()
函数getsockname返回套接字的本地地址;函数getpeername返回套接字对应的远程地址。

10、 结束语
网络程序设计全靠套接字接收和发送信息。上文主要讲述了Linux 下Socket的基本概念、Sockets API以及Socket所涉及到的TCP常识

热点内容
hp存储扩容 发布:2024-11-17 23:29:16 浏览:569
在ftp中put表示什么 发布:2024-11-17 23:29:12 浏览:383
mvc多文件上传 发布:2024-11-17 23:13:56 浏览:155
玩游戏硬盘缓存32m 发布:2024-11-17 23:03:42 浏览:525
蓝光存储系统 发布:2024-11-17 23:03:41 浏览:436
地平线4提示配置低于最低怎么办 发布:2024-11-17 22:54:38 浏览:611
注册银行卡账户密码填什么 发布:2024-11-17 22:54:35 浏览:537
java压缩上传图片 发布:2024-11-17 22:26:59 浏览:627
plc编程课件 发布:2024-11-17 22:18:23 浏览:469
我的世界服务器信号一直在检测 发布:2024-11-17 22:09:52 浏览:547