【计算机网络】Socket
socket进行通信的方式如下:
使用socket()系统调用能够创建一个socket,它返回一个用来在后续系统调用中引用该socket的文件描述符。
socket存在于一个通信domain中,它确定:
现在操作系统支持下列domain:
每个socket实现都至少提供了两种socket:流和数据报。这两种类型在UNIX和Internet domain中都得到了支持。
流socket提供了一个可靠的双向的字节流通信信道:
数据报socket允许数据以数据报的形式进行交换。在使用时无需与另一个socket简历连接。
传入bind()的addr比较复杂,每种socket domain都使用了不同的地址格式,如UNIX domain socket使用路径名,而Internet domain socket 使用IP地址和端口号。struct sockaddr适用于所有domain,将各种domain特定的地址结构转换成单个类型以供socket系统调用中的各个参数使用。
socket I/O 可以使用传统的read()和write()系统调用或使用一组socket特有的系统调用send() recv() sendto() recvfrom()。默认情况下,这些系统调用在I/O操作无法被立即完成时阻塞,使用fcntl() F_SETFL 操作用启用 O_NONBLOCK 打开文件状态标记可以执行非阻塞I/O
listen()系统调用将文件描述符sockfd引用的流socket标记为被动,这个socket后面会被用来接受来自其他(主动的)socket的链接。
无法再一个已连接的socket(已成功执行connect()的socket或由accept()调用返回的socket)上执行 listen()
如果服务器正忙于处理其他客户端,那么客户端的connect()可能并不能马上被accept(),这将产生一个未决的连接。
内核必须要记录所有未决的连接请求的相关信息,backlog参数允许限制这种未决连接的数量。在这个限制之内的连接请求会立即成功,之外的连接请求就会阻塞直到一个未决的连接被接受,并从未决连接队列中删除。
accept()系统调用会文件描述符sockfd引用的监听流socket上接受一个连入连接。如果在调用accept时不存在未决的连接,那么调用会阻塞直到有连接请求到达为止。
返回的结果是已连接的socket的文件描述符。addr参数指向一个用来返回socket地址的结构。
一对连接的流 socket 在两个端点之间提供了一个双向通信信道。
关闭一个连接之后,对等应用程序读取数据时将会收到文件结束(所有缓冲数据都读取之后),如果要写入数据,会收到一个SIGPIPE信号,并且系统调用返回EPIPE错误。
无法保证顺序,也无法保证能够到达。由于底层协议有时会重新传包,也可能多次到达。
尽管数据报socket是无连接的,但在数据报socket上应用connect()系统调用仍然起作用,会导致内核记录这个socket的对等socket地址。
当一个数据报socket已连接后:
在UNIX domain中,socket地址以路径名来表示,domain特定的socket地址结构的定义如下:
为将一个UNIX domain socket绑定到一个地址上,需要初始化一个sockaddr_un结构,然后将指向这个结构的一个指针作为addr参数传入bind()并将addrlen指定为这个结构的大小。
当用来绑定UNIX domain socket时,bind()会在文件系统中创建一个条目,作为socket路径名的一部分的目录需要可访问和可写。这个文件会被标记为一个socket,当再这个路径名上应用stat()时,它会在stat结构的st_mode字段中的文件类型部分返回值S_IFSOCK。
尽管UNIX domain socket是通过路径名来标识的,但这些socket上发生的I/O无须对底层设备进行操作。
有关绑定一个UNIX domain socket的注意点:
服务器流程:
客户端流程:
对于UNIX domain socket来说,数据报的传输是在内核中发生的,也是可靠的,所有消息都会按序被递送并且不会发生重复的状况。
服务器创建socket后并绑定后,进入一个无线循环,在循环中使用recvfrom()接收来自客户端的数据报,将接收到的文本转换成大小格式并使用通过recvfrom()获取的地址将转换过的文本返回给客户端。
socket文件的所有权和权限决定了哪些进程能够与这个socket进行通信
有时候让单个进程创建一对socket并将它们连接起来是比较有用的。
允许将一个UNIX domain socket绑定到一个名字上但不会在文件系统中创建的名字
要传输数据,数据链路层需要将网络层传递过来的数据报封装进被称为帧的一个一个单元。最大传输单元MTC是改层所能传输的帧大小的上限。
网络层任务:
网络层的协议是IP,IPv4使用32位地址来标识子网和主机,IPv6则使用了128位的地址。
一个裸socket(SOCK_RAW),允许程序直接与IP层进行通信,但大多数都会基于一种传输层协议之上的socket。
IP以数据报(包)的形式来传输数据。在两个主机之间发送的每一个数据报都是在网络上独立传输的,它们经过的路径可能会不同。一个IP数据报包含一个头,其大小范围为20字节到60字节。包含目标主机的地址,源地址。
一个IP实现可能会给它所支持的数据报的大小设定一个上限。所有IP实现都必须做到数据报的大小上限至少与规定的IP最小重组缓冲区大小一样大。IPv4限制值是576字节,IPv6是1500字节。
IP是一种无连接协议,并没有在相互连接的两个主机之间提供一个虚拟电路。
IP是一种不可靠的协议:尽最大可能将数据报从发送者传输给接收者,但并不保证包到达的顺序与它们被传输的顺序一致,也不保证是否重复,甚至到达。IP也美誉错误恢复。可靠性是通过使用TCP来保证的。
IPv4为IP头提供了一个校验和,这样能够检测出头中的错误,但并没有为包中所传输的数据提供任何错误检测机制。IPv6并没有为IP头提供校验和,它依赖高层协议来完成错误检测和可靠性。
IP数据报的重复使可能发生的,数据链路层采用一些技术确保可靠性以及IP数据报可能会以隧道形式穿越采用了重传机制。
IP会将数据报分段成一个个大小合适的传输单元,这些分段在到达最终目的之后会被重组成原始的数据报(每个IP分段本身就包含一个偏移量)
使用ServletSocket创建TCP服务器端
从图 中看上去TCP通信的两个通信实体之间并没有服务器端 客户端之分 但那是两个通信实体已经建立虚拟链路之后的示意图 在两个通信实体没有建立虚拟链路之前 必须有一个通信实体先做出 主动姿态 主动接收来自其他通信实体的连接请求
Java中能接受其他通信实体连接请求的类是ServerSocket ServerSocket对象用于监听来自客户端的Socket连接 如果没有连接 它将一直处于等待状态 ServerSocket包含一个监听来自客户端连接请求的方法
Socket accept() 如果接收到一个客户端Socket的连接请求 该方法将返回一个与客户端Socket对应的Socket(如图 所示每个TCP连接有两个Socket) 否则该方法将一直处于等待状态 线程也被阻塞
为了创建ServerSocket对象 ServerSocket类提供了如下几个构造器
ServerSocket(int port) 用指定的端口port来创建一个ServerSocket 该端口应该是有一个有效的端口整数值 ~
ServerSocket(int port int backlog) 增加一个用来改变连接队列长度的参数backlog
ServerSocket(int port int backlog InetAddress localAddr) 在机器存在多个 IP地址的情况下 允许通过localAddr这个参数来指定将ServerSocket绑定到指定的IP地址
当ServerSocket使用完毕 应使用ServerSocket的close()方法来关闭该ServerSocket 通常情况下 服务器不应该只接受一个客户端请求 而应该不断地接受来自客户端的所有请求 所以Java程序通常会通过循环 不断地调用ServerSocket的accept()方法 如下代码片段所示
//创建一个ServerSocket 用于监听客户端Socket的连接请求
ServerSocket ss = new ServerSocket( )
//采用循环不断接受来自客户端的请求
while (true)
{
//每当接受到客户端Socket的请求 服务器端也对应产生一个Socket
Socket s = ss accept()
//下面就可以使用Socket进行通信了
…
}
上面程序中创建ServerSocket没有指定IP地址 则该ServerSocket将会绑定到本机默认的IP地址 程序中使用 作为该ServerSocket的端口号 通常推荐使用 以上的端口 主要是为了避免与其他应用程序的通用端口冲突
返回目录 疯狂Java讲义
编辑推荐
Java程序性能优化 让你的Java程序更快 更稳定
新手学Java 编程
lishixinzhi/Article/program/Java/hx/201311/27266
# -- coding: utf-8 --
# -- version: beta-00 --
####################################################################################################
import socket
####################################################################################################
class Main():
def __init__(self):
selfhost = '127001'
selfport = 9999
#--------------------------------------------------------------------------------------------------#
def Start(self):
clientSock = socketsocket(socketAF_INET, socketSOCK_STREAM)
clientSockconnect((selfhost, selfport))
while True:
clientSocksend(b'hello')
dataRecv = clientSockrecv(1024)
if not dataRecv:
break
clientSockclose()
####################################################################################################
def test():
m = Main()
mStart()
if __name__ == '__main__':
test()
上面的做法是CONNECT后使用 SOCKET循环接收,如果需要循环CONNECT,WHILE TURE改到CONNECT之前就行了,另外注意CLOSE SOCKET。
进入mysql,创建一个新用户xuys:
格式:grant 权限 on 数据库名表名 用户@登录主机 identified by "用户密码";
grant select,update,insert,delete on to xuys@19216888234 identified by "xuys1234";
查看结果,执行:
use mysql;
select host,user,password from user;
可以看到在user表中已有刚才创建的xuys用户。host字段表示登录的主机,其值可以用IP,也可用主机名,
将host字段的值改为%就表示在任何客户端机器上能以xuys用户登录到mysql服务器,建议在开发时设为%。
update user set host = '%' where user = 'xuys';
2、 /mysqladmin -uroot -p21century reload
/mysqladmin -uroot -p21century shutdown
3、/mysqld_safe --user-root &
记住:对授权表的任何修改都需要重新reload,即执行第3步。
如果经过以上3个步骤还是无法从客户端连接,请执行以下操作,在mysql数据库的db表中插入一条记录:
use mysql;
insert into db values('19216888234','%','xuys','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y');
update db set host = '%' where user = 'xuys';
重复执行上面的第2、3步。
Socket是网络上运行的两个程序间双向通讯的一端,它既可以接受请求,也可以发送请求,利用它可以较为方便的编写网络上的数据的传递。在java中,有专门的socket类来处理用户的请求和响应。利用SOCKET类的方法,就可以实现两台计算机之间的通讯。这里就介绍一下在JAVA中如何利用socket进行网络编程。 在Java中Socket可以理解为客户端或者服务器端的一个特殊的对象,这个对象有两个关键的方法,一个是getInputStream方法,另一个是getOutputStream方法。getInputStream方法可以得到一个输入流,客户端的Socket对象上的getInputStream方法得到的输入流其实就是从服务器端发回的数据流。GetOutputStream方法得到一个输出流,客户端Socket对象上的getOutputStream方法返回的输出流就是将要发送到服务器端的数据流,(其实是一个缓冲区,暂时存储将要发送过去的数据)。 程序可以对这些数据流根据需要进行进一步的封装。本文的例子就对这些数据流进行了一定的封装(关于封装可以参考Java中流的实现部分)。 一、建立服务器类 Java中有一个专门用来建立Socket服务器的类,名叫ServerSocket,可以用服务器需要使用的端口号作为参数来创建服务器对象。ServerSocket server = new ServerS
感觉这样的提问没有什么意义
建议看看书,查查资料
一般socket链接有以下两种方式:长(常)链接和短链接。
长链接:当数据发送完成后socket链接不断开。一直保留到异常或者是程序退出为止 ,这种方式的好处是不用每次去发起连接断开,在速度上可以比短连接要快一些,但是相 对来说对服务器的资源压力也要大些。长链接用的范围很广,比如游戏系统,qq等等,长 (常)链接一般还需要定时向服务器ping数据,以保证socket链接畅通。当ping不通服务 器时,需要重新开启链接。
短链接:当一次数据发送完毕后,主动断开链接,每次发送数据都要一次链接、断开 操作,这种方式的好处是:对服务器的资源占用相对来说比较小,但是由于每次都要重新 链接,速度开销上也比较大,这种方式对于那种不需要经常与服务器交互的情况下比较适 用。
上面两种方法在用户量非常大的情况下都存在着很大的不足,因此,考虑可以用 一种折衷的办法,那就是使用socket的连接池。
程序一开始初始化创建若干数量的长链接。给他们设置一个标识位,这个标识位表示 该链接是否空闲的状态。当需要发送数据的时候,系统给它分配一个当前空闲的链接。同 时,将得到的链接设置为“忙”,当数据发送完毕后,把链接标识位设置为 “闲”,让系统可以分配给下个用户,这样使得两种方式的优点都充分的发挥 出来了。用户数量足够多的时候,只需要动态增加链接池的数量即可。
下面我们用具体的程序来讲解下:
首先声明一个socket类:
public class XieGouSocket
{
public Socket m_socket; //Socket对象
public bool m_isFree; //判断是否空闲
public int m_index; //在链接缓存池中的索引值
}
下面的函数是创建socket链接池,这里为了使代码更加清晰,特地把异常处理部分 全部取掉了。
public XieGouSocket[] m_socket; //先定义个缓冲池
public void CreateSocketPool()
{
string ip= “127001”;
string port= 2003;
IPAddress serverIp=IPAddressParse(ip);
int serverPort=ConvertToInt32(port);
IPEndPoint iep=new IPEndPoint(serverIp,serverPort);
m_socket = new XieGouSocket[200];
for(int i =0; i < 200 ; i ++)
{
m_socket[i] = new XieGouSocket();
m_socket[i]m_index = i ;
m_socket[i]m_isFree = true;
m_socket[i]m_socket =new Socket (AddressFamilyInterNetwork,SocketTypeStream,ProtocolTypeTcp);
m_socket[i]m_socketSetSocketOption (SocketOptionLevelSocket,SocketOptionNameSendTimeout,1000);
m_socket[i]m_socketConnect(iep);
}
}
下面的函数是获取当前空闲的socket链接:
因为是多线程,所以需要加一个原子操作,定义一个原子变量,以防止多个线程 之间抢占资源问题的发生。
private static Mutex m_mutex=new Mutex();
public static XieGouSocket GetFreeConnection()
{
m_mutexWaitOne(); //先阻塞
for(int i =0; i < m_socketLength ; i ++)
{
if(m_socket[i]m_isFree) //如果找到一个空闲的
{
m_socket[i]m_isFree = false;
m_mutexReleaseMutex();//释放资源
return m_socket[i];
}
}
//如果没有空闲的链接,要么等待,要么程序再动态创建一个链接。
m_mutexReleaseMutex();//释放资源
return null;
}
当数据发送完毕后,程序必须将m_isFree 设置为 False。否则只使用不释放,程序很 快就溢出了。
0条评论