Linux Socket - 基本socket链接

0x0000 Linux Socket 函数

bind
listen
connect
accept
send
recv
read
write

0x0001

Server绑不上ip

报错位置在bind函数

[root@localhost 01]# ./server 191.168.80.151 1588
191.168.80.151 : 1588
Bind: Cannot assign requested address
port已被占用

报错位置在bind函数

[root@localhost 01]# ./server 192.168.80.151 1588
192.168.80.151 : 1588
Bind: Address already in use
没有这个网卡/端口号出不去

报错

Connect: No route to host

考虑自己防火墙是否挡住了这个端口

telnet 自己ip 端口

操作:

1. 关防火墙(不推荐)
2. 向防火墙添加这个端口放过的规则

recv后ctrl+c结束client

931955-20180303235427906-912537394.png

recv后在新窗口结束client

931955-20180303235440621-1884057398.png

0x0002 Server接不上(端口号错、对方ip输错)

报错:

Connect: Network is unreachable

考虑Server防火墙是不是过不去

telnet 对方ip 端口
第二次接同一个Server端口

报错在Connect

[root@localhost 01]# ./client 192.168.80.151 1588
192.168.80.151 : 1588
Connect: Connection refused
接通后再杀死client
Server以非异常方式退出(不报error)

Client:

[root@localhost 01]# ./client 192.168.80.151 1588
192.168.80.151 : 1588
Connect: Success
Enter message to send:^Z
[1]+  已停止                  ./client 192.168.80.151 1588
[root@localhost 01]# ps
   PID TTY          TIME CMD
  8114 pts/0    00:00:00 bash
 11288 pts/0    00:00:00 client
 11290 pts/0    00:00:00 ps
[root@localhost 01]# kill -9 11288
[root@localhost 01]# 

Server:

[root@localhost 01]# ./server 192.168.80.151 1588
192.168.80.151 : 1588
Listening
Accept client 192.168.80.153
[root@localhost 01]# 

931955-20180303235504291-1234039231.png

接通后杀死Server
Client以正常方式退出(不报error)

Server:

[root@localhost 01]# ./server 192.168.80.151 1588
192.168.80.151 : 1588
Listening
Accept client 192.168.80.153
^C
[root@localhost 01]# 

Client:

[root@localhost 01]# ./client 192.168.80.151 1588
192.168.80.151 : 1588
Connect: Success
Enter message to send:HelloServer
received:
Enter message to send:HelloServer
[root@localhost 01]#

0x0003 链接成功后从新会话启动Client

931955-20180303235512158-965137174.png

使用指定端口的方式在client和server上都用bind方法

931955-20180303235518074-1388114609.png

Client3连接不存在的ip
[root@localhost 03]# ./tcp_client3 192.168.122.1
server_port: 192
server_ip: INADDR_ANY
client_port: 0
Connect: Network is unreachable
255.255.255.255:192

同一张网卡设置多个ip掩码都不在一个网段上能通才怪呢

0x0004 read write recv send

0x0004-1-1Read Write Test

read一旦读完一次缓冲就立刻返回,不能读满后返回

write读到的内容和client相同

931955-20180303235526887-559556558.png

0x0004-1-2延时write

931955-20180303235533499-1665758679.png

0x0004-2-1Recv Send Test

recv 和send的主要区别是带有第四个参数能够对阻塞和非阻塞等信息进行控制

MSG_DONTROUTE | 不查找表 |
MSG_OOB | 接受或者发送带外数据 |
MSG_PEEK | 查看数据,并不从系统缓冲区移走数据 |
MSG_WAITALL | 等待所有数据 |

recv和send的结果与read write相同

931955-20180303235543818-1717589987.png

0x0004-2-2延时send

931955-20180303235549677-446123968.png

0x0005

内核缓冲区测试

测试最大写入write后阻塞

931955-20180303235556317-1076996659.png

netstat 命令及其参数

-a (all)显示所有选项,默认不显示LISTEN相关
-t (tcp)仅显示tcp相关选项
-u (udp)仅显示udp相关选项
-n 拒绝显示别名,能显示数字的全部转化成数字。
-l 仅列出有在 Listen (监听) 的服務状态

-p 显示建立相关链接的程序名
-r 显示路由信息,路由表
-e 显示扩展信息,例如uid等
-s 按各个协议进行统计
-c 每隔一个固定时间,执行该netstat命令。

提示:LISTEN和LISTENING的状态只有用-a或者-l才能看到

setsockopt 函数

设置套接口的选项。
#include <sys/types.h>
#include <sys/socket.h>
int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
sockfd:标识一个套接口的描述字。
level:选项定义的层次;支持SOL_SOCKET、IPPROTO_TCP、IPPROTO_IP和IPPROTO_IPV6。
optname:需设置的选项。
optval:指针,指向存放选项待设置的新值的缓冲区。
optlen:optval缓冲区长度。

1.closesocket(一般不会立即关闭而经历TIME_WAIT的过程)后想继续重用该socket:
BOOL bReuseaddr=TRUE;
setsockopt(s,SOL_SOCKET ,SO_REUSEADDR,(const char*)&bReuseaddr,sizeof(BOOL));


2. 如果要已经处于连接状态的soket在调用closesocket后强制关闭,不经历
TIME_WAIT的过程:
BOOL bDontLinger = FALSE;
setsockopt(s,SOL_SOCKET,SO_DONTLINGER,(const char*)&bDontLinger,sizeof(BOOL));


3.在send(),recv()过程中有时由于网络状况等原因,发收不能预期进行,而设置收发时限:
int nNetTimeout=1000;//1秒
//发送时限
setsockopt(socket,SOL_S0CKET,SO_SNDTIMEO,(char *)&nNetTimeout,sizeof(int));
//接收时限
setsockopt(socket,SOL_S0CKET,SO_RCVTIMEO,(char *)&nNetTimeout,sizeof(int));


4.在send()的时候,返回的是实际发送出去的字节(同步)或发送到socket缓冲区的字节
(异步);系统默认的状态发送和接收一次为8688字节(约为8.5K);在实际的过程中发送数据
和接收数据量比较大,可以设置socket缓冲区,而避免了send(),recv()不断的循环收发:
// 接收缓冲区
int nRecvBuf=32*1024;//设置为32K
setsockopt(s,SOL_SOCKET,SO_RCVBUF,(const char*)&nRecvBuf,sizeof(int));
//发送缓冲区
int nSendBuf=32*1024;//设置为32K
setsockopt(s,SOL_SOCKET,SO_SNDBUF,(const char*)&nSendBuf,sizeof(int));


5. 如果在发送数据的时,希望不经历由系统缓冲区到socket缓冲区的拷贝而影响
程序的性能:
int nZero=0;
setsockopt(socket,SOL_S0CKET,SO_SNDBUF,(char *)&nZero,sizeof(nZero));


6.同上在recv()完成上述功能(默认情况是将socket缓冲区的内容拷贝到系统缓冲区):
int nZero=0;
setsockopt(socket,SOL_S0CKET,SO_RCVBUF,(char *)&nZero,sizeof(int));


7.一般在发送UDP数据报的时候,希望该socket发送的数据具有广播特性:
BOOL bBroadcast=TRUE;
setsockopt(s,SOL_SOCKET,SO_BROADCAST,(const char*)&bBroadcast,sizeof(BOOL));


8.在client连接服务器过程中,如果处于非阻塞模式下的socket在connect()的过程中可
以设置connect()延时,直到accpet()被呼叫(本函数设置只有在非阻塞的过程中有显著的
作用,在阻塞的函数调用中作用不大)
BOOL bConditionalAccept=TRUE;
setsockopt(s,SOL_SOCKET,SO_CONDITIONAL_ACCEPT,(const char*)&bConditionalAccept,sizeof(BOOL));


9.如果在发送数据的过程中(send()没有完成,还有数据没发送)而调用了closesocket(),以前我们
一般采取的措施是"从容关闭"shutdown(s,SD_BOTH),但是数据是肯定丢失了,如何设置让程序满足具体
应用的要求(即让没发完的数据发送出去后在关闭socket)?
struct linger {
u_short l_onoff;
u_short l_linger;
};
linger m_sLinger;
m_sLinger.l_onoff=1;//(在closesocket()调用,但是还有数据没发送完毕的时候容许逗留)
// 如果m_sLinger.l_onoff=0;则功能和2.)作用相同;
m_sLinger.l_linger=5;//(容许逗留的时间为5秒)
setsockopt(s,SOL_SOCKET,SO_LINGER,(const char*)&m_sLinger,sizeof(linger));

使用setsockopt调整内核缓冲区的大小后

(不慎调的更小了)

931955-20180303235609952-959883149.png

0x0006

0x0006.1

两边都进入read阻塞状态,无法传送数据

931955-20180303235617712-1119053854.png

0x0006.2

读写大小一致,可以正常传输

read_w

读写大小不一致,冗余的数据最后会堵住一边的write
(图相同,省略)

931955-20180303235623212-809343970.png

0x0006.3

前两种都是正常收发
(省略其他图片)

931955-20180303235629812-1536371712.png

第三种 server 1000 1000 client 700 700
client完成了一轮write read server read都没有做完就阻塞了

931955-20180303235640921-1520483789.png

0x0006.4

前两种都是正常收发
(同3)

第三种 server 1000 1000 client 700 700
client写完之后阻塞在read server read没有做完,阻塞

931955-20180303235647550-1579664216.png

转载于:https://www.cnblogs.com/liutianchen/p/8503550.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值