Linux下的socket演示程序

一个Web服务之所以不会执行一段时间就结束,本质上是因为它在代码里启动了一个while死循环。另外一个重要特征是,它能够跟客户端打交道,需要进行网络通信,因此需要用到socket这个东西。

socket是一个4元组,标识TCP连接的两个终端:本地IP地址、本地端口、远程IP地址、远程端口。一个socket对唯一地标识着网络上的TCP连接。每个终端的IP地址和端口号,称为socket。服务器创建socket并开始接受客户端连接的流程如下: 1. 创建一个TCP/IP socket,socket.socket() 2. 设置一些socket选项,setsockopt()函数 3. 绑定指定地址,bind()函数 4. 将创建的socket变为监听socket,listen()函数

做完这些以后,服务器开始循环地一次接受一个客户端连接。当有连接到达时,accept调用返回已连接的客户端socket。然后服务器从这个socket读取请求数据,处理后发送一个响应给客户端。然后服务器关闭客户端连接,准备好接受新的客户端连接。

服务端:

TCP服务端一般需要下面几个操作:建立,绑定IP地址和端口,监听端口,等待连接,接收数据,传输数据 ,关闭连接

  • 建立:server=socket.socket(socket.AF_INET, socket.SOCK_STREAM) 【参数默认就是socket.AF_INET, socket.SOCK_STREAM】
  • 绑定端口:server.bind((‘IP地址’,端口)),【地址和端口号是一个 tuple 】
  • 监听:server.listen()
  • 接受连接: conn,addr=server.accept(),返回值是一个连接实例和一个地址,地址是连接过来的客户端地址,而数据操作要利用这个连接实例
  • 传输数据:conn.send(data),【传输的数据必须是字节流,所以对字符串数据需要使用encode() 】
  • 接收数据read:conn.recv(size),【传输的数据必须是字节流,size是接收的字节数,如果需要转成Unicode,需要使用decode() 】
  • 关闭连接close:close()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import socket
server=socket.socket()#建立socket

server.bind(('localhost',1234))#绑定
server.listen()#监听
print("开始等待。。。")
conn,addr=server.accept()#接收连接
print("连接成功")
data=conn.recv(1024)#接收数据
print(data.decode())
conn.send(data)#发送数据

server.close()#关闭连接


print("--------------------")
server 只接受一次 client 请求,当 server 向 client 传回数据后,程序就运行结束了。如果想再次接收到服务器的数据,必须再次运行 server,所以这是一个非常简陋的 socket 程序,不能够一直接受客户端的请求。

上述代码存在一个问题:只能接受一次连接,连接结束后,服务端socket将关闭,更改成不立即关闭能等待下一个连接的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#服务器端

import socket
server = socket.socket()
server.bind(('localhost',1234)) #绑定ip和端口
server.listen(5) #监听


while True:
print("开始等待")
conn, addr = server.accept()
print(conn, addr)
print("客户端连接")
while True:
data = conn.recv(1024)
print("recv:",data)
if not data: #当data=0时为真
print("连接断开...")
break
conn.send(data)

server.close()

注:上述代码中在linux中正常运行,在windows中会报错!

如果要在windows中运行,需要捕获异常:

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#服务器端

import socket
server = socket.socket()
server.bind(('localhost',1234)) #绑定ip和端口
server.listen(5) #监听

while True:
print("开始等待")
conn, addr = server.accept()
print(conn, addr)
print("客户端连接")
while True:
try:
data = conn.recv(1024)
print("recv:",data)
if not data: #当data=0时为真
print("连接断开...")
break
conn.send(data)
except ConnectionResetError as e:
print(e)
break


server.close()

客户端:

TCP客户端一般需要下面几个操作:建立socket,连接远程socket,传输数据 ,接收数据,关闭连接

  • 建立:client=socket.socket()
  • 连接:client.connect((‘IP地址’,端口)),其中地址和端口号是一个 tuple
  • 传输数据:client.send(data),传输的数据必须是字节流,所以对字符串数据需要使用encode()
  • 接收数据recv:client.recv(size),传输的数据是字节流,如果需要转成Unicode,需要使用decode()
  • 关闭连接close:close()

复制代码

1
2
3
4
5
6
7
8
import socket
client=socket.socket()#建立socket
client.connect(('localhost',1234))#连接
client.send("你好".encode())#发送数据
data=client.recv(1024)#接收数据
print(data.decode())

client.close()#关闭连接

复制代码

  • 上述代码存在一个问题:只能发送一次数据,发生完数据就会断开连接,改成可以多次发送数据,不自动断开的【前提是服务端能接收多次】:

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import socket

client=socket.socket()

client.connect(('localhost',1234))
while True:
cmd=input(">>")
if len(cmd)==0:
continue
client.send(cmd.encode())
cmd_res=client.recv(1024)
print(cmd_res.decode())

client.close()

正常情况下,程序运行到accept()函数就会被阻塞,等待客户端发起请求。

接下来再启动一个终端,运行client.py

client接收从server发送过来的字符串。然后继续运行。

client运行后,通过connect()函数向server发起请求,处于监听状态的server被激活,执行accept()函数,接受客户端的请求,然后执行send()函数向client传回数据。client接收到传回的数据后,通过recv()将数据读取出来。

通过socket()函数创建一个套接字,参数AF_INET表示使用IPv4地址,SOCK_STREAM 表示使用面向连接的套接字,在 Linux 中,socket 也是一种文件,有文件描述符,可以使用 send() / recv() 函数进行 I/O 操作。

通过 bind() 函数将套接字 serv_sock 与特定的 IP 地址和端口绑定。

socket() 函数确定了套接字的各种属性,bind() 函数让套接字与特定的IP地址和端口对应起来,这样客户端才能连接到该套接字。

让套接字处于被动监听状态。所谓被动监听,是指套接字一直处于“睡眠”中,直到客户端发起请求才会被“唤醒”。

accept() 函数用来接收客户端的请求。程序一旦执行到 accept() 就会被阻塞(暂停运行),直到客户端发起请求。

send() 函数用来向套接字文件中写入数据,也就是向客户端发送数据。

和普通文件一样,socket 在使用完毕后也要用 close() 关闭。

client与server区别 :

通过 connect() 向服务器发起请求,服务器的IP地址和端口号保存在client 结构体中。直到服务器传回数据后,直到退出循环,connect() 才运行结束。

通过 recv() 从套接字文件中读取数据。

UDP:

服务端:

UDP服务端通常有以下几个操作:创建socket,绑定端口,传输数据,接收数据

  • 创建socket:server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
  • 绑定端口:server.bind(addr),【addr是一个元组,内容为(地址,端口)】
  • 接收数据:data,client_addr=server.recvfrom(1024)
  • 传输数据:server.sendto(data,client_addr)

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import socket
import time
server=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
server.bind(("localhost",1234))

start_time=time.time()
while True:
data,addr=server.recvfrom(1024)
print(data,addr)
server.sendto("hello".encode(),addr)
time.sleep(1)
if time.time()-start_time>30:
break


server.close()

复制代码

客户端:

UDP客户端通常有以下几个操作:创建socket,传输数据,接收数据

  • 创建socket:client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
  • 传输数据:server.sendto(data,addr),【addr是一个元组,内容为(地址,端口)】
  • 接收数据:data,server_addr=client.recvfrom(1024)

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import socket,time

client=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
addr=("localhost",1234)
start_time=time.time()
while True:

client.sendto(time.ctime().encode(),addr)

data,addr= client.recvfrom(1024)
print(data)
time.sleep(1)
if time.time()-start_time>30:
break

client.close()