Ⅰ 如何提高SOCKET的并发接受连接速度
不知道你用啥语言,网络并发有两种模式:
老的模式,多进程,新程序大多都不会采用这种方式;
多线程,现代程序,大多采用这种方式。
socket,如果只是链接进来,一个链接启动一个线程,有专门的服务进程循环等待。高效的实现,有链接到达,比如TCP通讯完成3路握手过程,操作系统,就会把链接放到待处理队列,通知服务进程处理。
而操作系统的未完成链接队列+在处理链接队列是有上限的,这个上限值不确定,根据操作系统不同,可以用程序动态监测到。
因此,如果操作系统链接队列已满,接受不了新的请求的。一般返回错误是connection refused。
想要快速处理并发链接,编程角度考虑:
第一要求计算机接受请求要快,一般就采取多线程,服务线程,只负责,启动新的业务线程,立马接受下一次请求。
第二为了防止connection refused错误发生,业务处理逻辑代码设计要尽量的快。
第三条,做系统架构设计,再要根据不同的业务量选择适合的硬件,理论上配置越高越好,一般预算有限的,可以根据业务量提前测算个大概。买配置比测算量高一些的设备即可。一般系统设计,要考虑业务的增长。怎么测算,需要经验。
业务量大机器配置好,壮牛拉破车是不行的。设计得再好的架构,写得再好的代码。没有适合的机器。效果达不到期望值的。
Ⅱ 客户端(大概有100个)使用什么socket模型好
1、普通的阻塞和非阻塞编程。
利用线程池技术和内存池,SOCKET池技术,基本可以处理一千五百个左右的SOCKET连接,但我们一般使用的机器大约有两M内存,而在不改变线程堆栈的大小情况下,我们至多可以创建一千七八百个线程,不过也就基本动不了了。我们测试基本到一千个线程左右,机器就很慢了。
还有在WIN-XP-SP2中,对单进程中的线程的并发做了处理,默认是10个,修改的方法网上多的是。
2、SELECT模型(异步和同步)
这个模型在单线程的情况下默认是64个最大SOCKET连接。你可以修改WINSOCK2.h这个文件中的FD_SETSIZE,但不得超过底层WINSOCK的限制(1024),但如果采用多线程的话,可以处理更多,其实际的最大数量,在单线程里建议不超过1000个,至于多线程,应该也要控制线程切换的效率和数据处理的时间。应该几千个没有什么问题。
3、WSAASYNCSELECT模型
这个模型利用的是消息机制,建议不超过1000个。
4、WSAEVENTSELECT模型
这个模型利用的是事件驱动方式,单个线程不超过64个(WSAWaitForMultipleEvents最多等待64个事件),如果多SOCKET并发宜采用线程池技术,应该几千个没什么问题。
5、重叠IO
应该几千个没问题。这个毕竟是下面IOCP的一个技术基础。
6、IOCP完成端口+重叠IO
这个是解决SOCKET通信的终极武器,可惜只用在WIN上和2000以上,LINUX上好象有一个类似的EPOLL,而且好象比这个还好用,没具体用过。这个东东解决几万个SOCKET并发应该是很轻松,当然你得编程水平和技术得跟上。
从网上查看说这个东西如果使用的服务器版的操作系统和机器最大可到一百W,太恐怖了吧,那家伙自己在XP上最大达到了五万,但不断出现内存溢出的BUG。他最高到了9W。
Ⅲ socket高并发网络编程服务端有什么框架
netty;
PayServer.java
package com.miri.pay.scoket;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class PayServer implements Runnable
{
private static final Logger DLOG = LoggerFactory.getLogger(PayServer.class);
private final int port;
public PayServer(int port)
{
this.port = port;
}
/**
* 为ServerBootstrap绑定指定端口
*/
public void run()
{
// 用于接收发来的连接请求
final EventLoopGroup bossGroup = new NioEventLoopGroup();
// 用于处理boss接受并且注册给worker的连接中的信息
final EventLoopGroup workerGroup = new NioEventLoopGroup();
try
{
// 配置服务器
final ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(bossGroup, workerGroup);
bootstrap.channel(NioServerSocketChannel.class);
bootstrap.option(ChannelOption.SO_BACKLOG, 128);
// 通过NoDelay禁用Nagle,使消息立即发出去,不用等待到一定的数据量才发出去
bootstrap.option(ChannelOption.TCP_NODELAY, true);
// 保持长连接状态
bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
// CustomChannelInitializer是一个特殊的handler,用于方便的配置用户自定义的handler实现
bootstrap.childHandler(new CustomChannelInitializer());
// 绑定并开始接受传入的连接
final ChannelFuture future = bootstrap.bind(this.port).sync();
if (future.isSuccess())
{
PayServer.DLOG.info("Start the socket server {} success", this.port);
}
else
{
PayServer.DLOG.info("Start the socket server {} failure,System exit!", this.port);
throw new RuntimeException("Socket服务端启动失败");
}
// 等待服务器套接字关闭
// 关闭服务器
future.channel().closeFuture().sync();
}
catch (final InterruptedException e)
{
PayServer.DLOG.error("Close the socket server exception occurs,System exit!", e);
throw new RuntimeException("关闭Socket服务端失败");
}
finally
{
// 关闭所有事件循环终止线程
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
/**
* 特殊的内部类
* <p>
* 是一个特殊的handler,用于方便的配置用户自定义的handler实现
* @author xulonghui
*/
static class CustomChannelInitializer extends ChannelInitializer<SocketChannel>
{
@Override
protected void initChannel(SocketChannel ch) throws Exception
{
final ChannelPipeline p = ch.pipeline();
p.addLast(new PayMessageEncoder());
p.addLast(new PayMessageDecoder());
p.addLast(new PayServerHandler());
}
}
}
PayMessageEncoder.java
package com.miri.pay.scoket;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import io.netty.util.CharsetUtil;
import com.miri.pay.model.CommonResponse;
import com.miri.pay.utils.jsonUtil;
/**
*消息编码器
* <p>
* 编码从服务端发送出的消息
*/
public class PayMessageEncoder extends MessageToByteEncoder<CommonResponse>
{
@Override
protected void encode(ChannelHandlerContext ctx, CommonResponse rsp, ByteBuf out) throws Exception
{
if (rsp != null)
{
final Object msgContent = rsp.getMsgContent();
// 消息ID,sequenceId和entityId三个加起来是12个长度
int msgLen = 12;
byte[] contentbs = new byte[] {};
if (msgContent != null)
{
final String content = JsonUtil.bean2json(msgContent);
contentbs = content.getBytes(CharsetUtil.UTF_8);
final int cl = contentbs.length;
msgLen += cl;
}
out.writeInt(msgLen);// 写入当前消息的总长度
out.writeInt(rsp.getMsgId());// 写入当前消息的消息ID
out.writeInt(rsp.getSequenceId());// 写入当前消息的SequenceId
out.writeInt(rsp.getEntityId());// 写入当前消息的EntityId
// 写入消息主体内容
if (contentbs.length > 0)
{
out.writeBytes(contentbs);
}
}
}
}
PayMessageDecoder.java
package com.miri.pay.scoket;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.util.CharsetUtil;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.miri.pay.constants.Constants;
import com.miri.pay.model.CommonRequest;
import com.miri.pay.utils.ByteUtil;
/**
* 消息解码器
* <p>
* 解码从客户端请求的消息
*/
public class PayMessageDecoder extends ByteToMessageDecoder
{
private static final Logger DLOG = LoggerFactory.getLogger(PayMessageDecoder.class);
/**
* 表示头长度的字节数
*/
private static final int HEAD_LENGTH = 4;
/**
* 所有ID串所属的字节数
*/
private static final int ID_STR_LENGTH = 12;
/**
* 单个ID所属的字节数
*/
private static final int SINGLE_ID_LENGTH = 4;
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception
{
int readable = in.readableBytes();
if (readable < PayMessageDecoder.HEAD_LENGTH)
{
return;
}
in.markReaderIndex(); // 我们标记一下当前的readIndex的位置
final int dataLength = in.readInt(); // 读取传送过来的消息的长度。ByteBuf 的readInt()方法会让他的readIndex增加4
if (dataLength < 0)
{
// 我们读到的消息体长度为0,这是不应该出现的情况,这里出现这情况,关闭连接。
ctx.close();
}
readable = in.readableBytes();
if (readable < dataLength)
{
// 读到的消息体长度如果小于我们传送过来的消息长度,则resetReaderIndex. 这个配合markReaderIndex使用的。把readIndex重置到mark的地方
in.resetReaderIndex();
return;
}
final byte[] body = new byte[dataLength];
in.readBytes(body);
// 判断是否读取到内容
final int length = body.length;
if (length == 0)
{
return;// 若未读出任何内容,则忽略
}
out.add(this.byte2req(body));
}
/**
* 将读取到的byte数据转换为请求对象
* @param body
* @return
* @throws Exception
*/
private CommonRequest byte2req(byte[] body) throws Exception
{
final CommonRequest req = new CommonRequest(Constants.INVALID_MSGID);
final int length = body.length;
// 若内容数组的长度小于或等于12,则表示消息主体内容为空,直接返回一个无效的消息出去
if (length < PayMessageDecoder.ID_STR_LENGTH)
{
PayMessageDecoder.DLOG
.info("The client sends the message length is: {}, is invalid message, directly returns a msgId = {} request entity",
length, Constants.INVALID_MSGID);
return req;
}
// 获取消息ID
final byte[] mbs = new byte[PayMessageDecoder.SINGLE_ID_LENGTH];
System.array(body, 0, mbs, 0, PayMessageDecoder.SINGLE_ID_LENGTH);
final int msgId = ByteUtil.byte4toint(mbs);
req.setMsgId(msgId);
// 获取sequenceId
final byte[] sbs = new byte[PayMessageDecoder.SINGLE_ID_LENGTH];
System.array(body, 4, sbs, 0, PayMessageDecoder.SINGLE_ID_LENGTH);
final int sequenceId = ByteUtil.byte4toint(sbs);
req.setSequenceId(sequenceId);
// 获取entityId
final byte[] ebs = new byte[PayMessageDecoder.SINGLE_ID_LENGTH];
System.array(body, 8, ebs, 0, PayMessageDecoder.SINGLE_ID_LENGTH);
final int entityId = ByteUtil.byte4toint(ebs);
req.setEntityId(entityId);
// 获取消息主体内容
if (length > PayMessageDecoder.ID_STR_LENGTH)
{
final int contentLen = length - PayMessageDecoder.ID_STR_LENGTH;
final byte[] contentbs = new byte[contentLen];
System.array(body, 12, contentbs, 0, contentLen);
final String content = new String(contentbs, CharsetUtil.UTF_8);
req.setMsgContent(content);
}
return req;
}
}
PayServerHandler.java
package com.miri.pay.scoket;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;
import java.util.HashMap;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.miri.pay.MessageQueue;
import com.miri.pay.model.CommonRequest;
import com.miri.pay.model.PendingBean;
/**
* Socket服务端处理器
*/
public class PayServerHandler extends ChannelInboundHandlerAdapter
{
private static final Logger DLOG = LoggerFactory.getLogger(PayServerHandler.class);
/**
* 外部订单号-频道
*/
public static final Map<String, Channel> CHANNELS = new HashMap<String, Channel>();
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
{
try
{
PayServerHandler.DLOG.info("Client send to msg is: {}", msg);
final CommonRequest request = (CommonRequest) msg;
final PendingBean bean = new PendingBean(ctx.channel(), request);
MessageQueue.offer(bean);
}
finally
{
ReferenceCountUtil.release(msg);
}
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception
{
ctx.flush();
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception
{
super.channelActive(ctx);
final Channel channel = ctx.channel();
PayServerHandler.DLOG.info("Client active form {}", channel.remoteAddress());
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception
{
super.channelInactive(ctx);
final Channel channel = ctx.channel();
PayServerHandler.DLOG.info("Client inactive form {}", channel.remoteAddress());
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception
{
PayServerHandler.DLOG.error("System exception", cause);
ctx.close();
}
}
Ⅳ socket 编程中 accept 函数返回
简单的说就是通信的两方的一种约定,用套接字中的相关函数来完成通信过程
应用层通过传输层进行数据通信时,TCP和UDP会遇到同时为多个应用程序进程提供并发服务的问题。多个TCP连接或多个应用程序进程可能需要通过同一个 TCP协议端口传输数据。为了区别不同的应用程序进程和连接,许多计算机操作系统为应用程序与TCP/IP协议交互提供了称为套接字(Socket)的接口。
区分不同应用程序进程间的网络通信和连接,主要有3个参数:通信的目的IP地址、使用的传输层协议(TCP或UDP)和使用的端口号。Socket原意是 “插座”。通过将这3个参数结合起来,与一个“插座”Socket绑定,应用层就可以和传输层通过套接字接口,区分来自不同应用程序进程或网络连接的通信,实现数据传输的并发服务。
-- win API socket
本文所谈到的Socket函数如果没有特别说明,都是指的Windows Socket API。
一、WSAStartup函数
int WSAStartup(
WORD wVersionRequested,
LPWSADATA lpWSAData
);
使用Socket的程序在使用Socket之前必须调用WSAStartup函数。该函数的第一个参数指明程序请求使用的Socket版本,其中高位字节指明副版本、低位字节指明主版本;操作系统利用第二个参数返回请求的Socket的版本信息。当一个应用程序调用WSAStartup函数时,操作系统根据请求的Socket版本来搜索相应的Socket库,然后绑定找到的Socket库到该应用程序中。以后应用程序就可以调用所请求的 Socket库中的其它Socket函数了。该函数执行成功后返回0。
例:假如一个程序要使用2.1版本的Socket,那么程序代码如下
wVersionRequested = MAKEWORD( 2, 1 );
err = WSAStartup( wVersionRequested, &wsaData );
二、WSACleanup函数
int WSACleanup (void);
应用程序在完成对请求的Socket库的使用后,要调用WSACleanup函数来解除与Socket库的绑定并且释放Socket库所占用的系统资源。
三、socket函数
SOCKET socket(
int af,
int type,
int protocol
);
应用程序调用socket函数来创建一个能够进行网络通信的套接字。第一个参数指定应用程序使用的通信协议的协议族,对于TCP/IP协议族,该参数置PF_INET;第二个参数指定要创建的套接字类型,流套接字类型为SOCK_STREAM、数据报套接字类型为SOCK_DGRAM;第三个参数指定应用程序所使用的通信协议。该函数如果调用成功就返回新创建的套接字的描述符,如果失败就返回INVALID_SOCKET。套接字描述符是一个整数类型的值。每个进程的进程空间里都有一个套接字描述符表,该表中存放着套接字描述符和套接字数据结构的对应关系。该表中有一个字段存放新创建的套接字的描述符,另一个字段存放套接字数据结构的地址,因此根据套接字描述符就可以找到其对应的套接字数据结构。每个进程在自己的进程空间里都有一个套接字描述符表但是套接字数据结构都是在操作系统的内核缓冲里。下面是一个创建流套接字的例子:
struct protoent *ppe;
ppe=getprotobyname("tcp");
SOCKET ListenSocket=socket(PF_INET,SOCK_STREAM,ppe->p_proto);
四、closesocket函数
int closesocket(
SOCKET s
);
closesocket函数用来关闭一个描述符为s套接字。由于每个进程中都有一个套接字描述符表,表中的每个套接字描述符都对应了一个位于操作系统缓冲区中的套接字数据结构,因此有可能有几个套接字描述符指向同一个套接字数据结构。套接字数据结构中专门有一个字段存放该结构的被引用次数,即有多少个套接字描述符指向该结构。当调用closesocket函数时,操作系统先检查套接字数据结构中的该字段的值,如果为1,就表明只有一个套接字描述符指向它,因此操作系统就先把s在套接字描述符表中对应的那条表项清除,并且释放s对应的套接字数据结构;如果该字段大于1,那么操作系统仅仅清除s在套接字描述符表中的对应表项,并且把s对应的套接字数据结构的引用次数减1。
closesocket函数如果执行成功就返回0,否则返回SOCKET_ERROR。
五、send函数
int send(
SOCKET s,
const char FAR *buf,
int len,
int flags
);
不论是客户还是服务器应用程序都用send函数来向TCP连接的另一端发送数据。客户程序一般用send函数向服务器发送请求,而服务器则通常用 send函数来向客户程序发送应答。该函数的第一个参数指定发送端套接字描述符;第二个参数指明一个存放应用程序要发送数据的缓冲区;第三个参数指明实际要发送的数据的字节数;第四个参数一般置0。这里只描述同步Socket的send函数的执行流程。当调用该函数时,send先比较待发送数据的长度 len和套接字s的发送缓冲区的长度,如果len大于s的发送缓冲区的长度,该函数返回SOCKET_ERROR;如果len小于或者等于s的发送缓冲区的长度,那么send先检查协议是否正在发送s的发送缓冲中的数据,如果是就等待协议把数据发送完,如果协议还没有开始发送s的发送缓冲中的数据或者s的发送缓冲中没有数据,那么send就比较s的发送缓冲区的剩余空间和len,如果len大于剩余空间大小send就一直等待协议把s的发送缓冲中的数据发送完,如果len小于剩余空间大小send就仅仅把buf中的数据到剩余空间里(注意并不是send把s的发送缓冲中的数据传到连接的另一端的,而是协议传的,send仅仅是把buf中的数据到s的发送缓冲区的剩余空间里)。如果send函数数据成功,就返回实际的字节数,如果send在数据时出现错误,那么send就返回SOCKET_ERROR;如果send在等待协议传送数据时网络断开的话,那么send 函数也返回SOCKET_ERROR。要注意send函数把buf中的数据成功到s的发送缓冲的剩余空间里后它就返回了,但是此时这些数据并不一定马上被传到连接的另一端。如果协议在后续的传送过程中出现网络错误的话,那么下一个Socket函数就会返回SOCKET_ERROR。(每一个除 send外的Socket函数在执行的最开始总要先等待套接字的发送缓冲中的数据被协议传送完毕才能继续,如果在等待时出现网络错误,那么该Socket 函数就返回SOCKET_ERROR)
注意:在Unix系统下,如果send在等待协议传送数据时网络断开的话,调用send的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。
六、recv函数
int recv(
SOCKET s,
char FAR *buf,
int len,
int flags
);
不论是客户还是服务器应用程序都用recv函数从TCP连接的另一端接收数据。该函数的第一个参数指定接收端套接字描述符;第二个参数指明一个缓冲区,该缓冲区用来存放recv函数接收到的数据;第三个参数指明buf的长度;第四个参数一般置0。这里只描述同步Socket的recv函数的执行流程。当应用程序调用recv函数时,recv先等待s的发送缓冲中的数据被协议传送完毕,如果协议在传送s的发送缓冲中的数据时出现网络错误,那么 recv函数返回SOCKET_ERROR,如果s的发送缓冲中没有数据或者数据被协议成功发送完毕后,recv先检查套接字s的接收缓冲区,如果s接收缓冲区中没有数据或者协议正在接收数据,那么recv就一直等待,只到协议把数据接收完毕。当协议把数据接收完毕,recv函数就把s的接收缓冲中的数据 到buf中(注意协议接收到的数据可能大于buf的长度,所以在这种情况下要调用几次recv函数才能把s的接收缓冲中的数据完。 recv函数仅仅是数据,真正的接收数据是协议来完成的),recv函数返回其实际的字节数。如果recv在时出错,那么它返回 SOCKET_ERROR;如果recv函数在等待协议接收数据时网络中断了,那么它返回0。
注意:在Unix系统下,如果recv函数在等待协议接收数据时网络断开了,那么调用recv的进程会接收到一个SIGPIPE信号,进程对该信号的默认处理是进程终止。
七、bind函数
int bind(
SOCKET s,
const struct sockaddr FAR *name,
int namelen
);
当创建了一个Socket以后,套接字数据结构中有一个默认的IP地址和默认的端口号。一个服务程序必须调用bind函数来给其绑定一个IP地址和一个特定的端口号。客户程序一般不必调用bind函数来为其Socket绑定IP地址和断口号。该函数的第一个参数指定待绑定的Socket描述符;第二个参数指定一个sockaddr结构,该结构是这样定义的:
struct sockaddr {
u_short sa_family;
char sa_data[14];
};
sa_family指定地址族,对于TCP/IP协议族的套接字,给其置AF_INET。当对TCP/IP协议族的套接字进行绑定时,我们通常使用另一个地址结构:
struct sockaddr_in {
short sin_family;
u_short sin_port;
struct in_addr sin_addr;
char sin_zero[8];
};
其中sin_family置AF_INET;sin_port指明端口号;sin_addr结构体中只有一个唯一的字段s_addr,表示IP地址,该字段是一个整数,一般用函数inet_addr()把字符串形式的IP地址转换成unsigned long型的整数值后再置给s_addr。有的服务器是多宿主机,至少有两个网卡,那么运行在这样的服务器上的服务程序在为其Socket绑定IP地址时可以把htonl(INADDR_ANY)置给s_addr,这样做的好处是不论哪个网段上的客户程序都能与该服务程序通信;如果只给运行在多宿主机上的服务程序的Socket绑定一个固定的IP地址,那么就只有与该IP地址处于同一个网段上的客户程序才能与该服务程序通信。我们用0来填充 sin_zero数组,目的是让sockaddr_in结构的大小与sockaddr结构的大小一致。下面是一个bind函数调用的例子:
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_port = htons(8888);
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
bind(ListenSocket,(struct sockaddr *)&saddr,sizeof(saddr));
八、listen函数
int listen( SOCKET s, int backlog );
服务程序可以调用listen函数使其流套接字s处于监听状态。处于监听状态的流套接字s将维护一个客户连接请求队列,该队列最多容纳backlog个客户连接请求。假如该函数执行成功,则返回0;如果执行失败,则返回SOCKET_ERROR。
九、accept函数
SOCKET accept(
SOCKET s,
struct sockaddr FAR *addr,
int FAR *addrlen
);
服务程序调用accept函数从处于监听状态的流套接字s的客户连接请求队列中取出排在最前的一个客户请求,并且创建一个新的套接字来与客户套接字创建连接通道,如果连接成功,就返回新创建的套接字的描述符,以后与客户套接字交换数据的是新创建的套接字;如果失败就返回 INVALID_SOCKET。该函数的第一个参数指定处于监听状态的流套接字;操作系统利用第二个参数来返回新创建的套接字的地址结构;操作系统利用第三个参数来返回新创建的套接字的地址结构的长度。下面是一个调用accept的例子:
struct sockaddr_in ServerSocketAddr;
int addrlen;
addrlen=sizeof(ServerSocketAddr);
ServerSocket=accept(ListenSocket,(struct sockaddr *)&ServerSocketAddr,&addrlen);
十、connect函数
int connect(
SOCKET s,
const struct sockaddr FAR *name,
int namelen
);
客户程序调用connect函数来使客户Socket s与监听于name所指定的计算机的特定端口上的服务Socket进行连接。如果连接成功,connect返回0;如果失败则返回SOCKET_ERROR。下面是一个例子:
struct sockaddr_in daddr;
memset((void *)&daddr,0,sizeof(daddr));
daddr.sin_family=AF_INET;
daddr.sin_port=htons(8888);
daddr.sin_addr.s_addr=inet_addr("133.197.22.4");
connect(ClientSocket,(struct sockaddr *)&daddr,sizeof(daddr));
Ⅳ socket是什么呀
套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。
一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议根进行交互的接口。
套接字是通信的基石,是支持TCP/IP协议的路通信的基本操作单元。
可以将套接字看作不同主机间的进程进行双间通信的端点,它构成了单个主机内及整个网络间的编程界面。套接字存在于通信域中,通信域是为了处理一般的线程通过套接字通信而引进的一种抽象概念。
套接字通常和同一个域中的套接字交换数据(数据交换也可能穿越域的界限,但这时一定要执行某种解释程序),各种进程使用这个相同的域互相之间用Internet协议簇来进行通信。
Socket(套接字)可以看成是两个网络应用程序进行通信时,各自通信连接中的端点,这是一个逻辑上的概念。它是网络环境中进程间通信的API(应用程序编程接口),也是可以被命名和寻址的通信端点,使用中的每一个套接字都有其类型和一个与之相连进程。
通信时其中一个网络应用程序将要传输的一段信息写入它所在主机的 Socket中,该 Socket通过与网络接口卡(NIC)相连的传输介质将这段信息送到另外一台主机的 Socket中,使对方能够接收到这段信息。
Socket是由IP地址和端口结合的,提供向应用层进程传送数据包的机制。
类型
1、数据报套接字
无连接套接字,使用用户数据报协议(UDP)。在数据报套接字上发送或接收的每个数据包都单独寻址和路由。数据报套接字不能保证顺序和可靠性,因此从一台机器或进程发送到另一台机器或进程的多个数据包可能以任何顺序到达或可能根本不到达。在数据报套接字上发送广播可能需要特殊配置。
为了接收广播数据包,数据报套接字不应该绑定到特定地址,尽管在某些实现中,当数据报套接字绑定到特定地址时也可能接收广播数据包。
2、流套接字
面向连接的套接字,使用传输控制协议(TCP)、流控制传输协议(SCTP) 或数据报拥塞控制协议(DCCP)。流套接字提供了无记录边界的有序且独特的无错误数据流,并具有用于创建和销毁连接以及报告错误的明确定义的机制。
流套接字以带外功能可靠地、有序地传输数据。在 Internet 上,流套接字通常使用 TCP 实现,以便应用程序可以使用 TCP/IP 协议在任何网络上运行。
3、原始套接字
允许直接发送和接收 IP 数据包,无需任何特定于协议的传输层格式。对于其他类型的套接字,根据选择的传输层协议(例如 TCP、UDP)自动封装有效载荷,并且套接字用户不知道与有效载荷一起广播的协议头的存在。从原始套接字读取时,通常包含标头。
从原始套接字传输数据包时,自动添加标头是可选的。
大多数套接字应用程序编程接口(API),例如基于Berkeley 套接字的那些,支持原始套接字。Windows XP于 2001 年发布,在Winsock接口中实现了原始套接字支持,但三年后,微软出于安全考虑限制了 Winsock 的原始套接字支持。
原始套接字用于与安全相关的应用程序,如Nmap。原始套接字的一个用例是在用户空间中实现新的传输层协议。
原始套接字通常在网络设备中可用,用于路由协议,例如Internet 组管理协议(IGMP) 和开放最短路径优先(OSPF),以及用于Internet 控制消息协议(ICMP) 等事情,由ping 实用程序。
以上内容参考网络-套接字