1. 程式人生 > >Linux Socket - 基本socket鏈接

Linux Socket - 基本socket鏈接

窗口 服務器 1-1 bind peek 結果 lin recv request

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

技術分享圖片

recv後在新窗口結束client

技術分享圖片

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]# 

技術分享圖片

接通後殺死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

技術分享圖片

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

技術分享圖片

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相同

技術分享圖片

0x0004-1-2延時write

技術分享圖片

0x0004-2-1Recv Send Test

recv 和send的主要區別是帶有第四個參數能夠對阻塞和非阻塞等信息進行控制

MSG_DONTROUTE | 不查找表 |
MSG_OOB | 接受或者發送帶外數據 |
MSG_PEEK | 查看數據,並不從系統緩沖區移走數據 |
MSG_WAITALL | 等待所有數據 |

recv和send的結果與read write相同

技術分享圖片

0x0004-2-2延時send

技術分享圖片

0x0005

內核緩沖區測試

測試最大寫入write後阻塞

技術分享圖片

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調整內核緩沖區的大小後

(不慎調的更小了)

技術分享圖片

0x0006

0x0006.1

兩邊都進入read阻塞狀態,無法傳送數據

技術分享圖片

0x0006.2

讀寫大小一致,可以正常傳輸

技術分享圖片

讀寫大小不一致,冗余的數據最後會堵住一邊的write
(圖相同,省略)

技術分享圖片

0x0006.3

前兩種都是正常收發
(省略其他圖片)

技術分享圖片

第三種 server 1000 1000 client 700 700
client完成了一輪write read server read都沒有做完就阻塞了

技術分享圖片

0x0006.4

前兩種都是正常收發
(同3)

第三種 server 1000 1000 client 700 700
client寫完之後阻塞在read server read沒有做完,阻塞

技術分享圖片

Linux Socket - 基本socket鏈接