java linux 調用32位so_從linux源碼看socket(tcp)的timeout

從linux源碼看socket(tcp)的timeout

前言

網絡編程中超時時間是一個重要但又容易被忽略的問題,對其的設置需要仔細斟酌。在經歷了數次物理機宕機之后,筆者詳細的考察了在網絡編程(tcp)中的各種超時設置,于是就有了本篇博文。本文大部分討論的是socket設置為block的情況,即setNonblock(false),僅在最后提及了nonblock socket(本文基于linux 2.6.32-431內核)。

connectTimeout

在討論connectTimeout之前,讓我們先看下java和C語言對于socket connect調用的函數簽名:

java:
// 函數調用中攜帶有超時時間
public void connect(SocketAddress endpoint, int timeout) ;
C語言:
// 函數調用中并不攜帶超時時間
int connect(int sockfd, const struct sockaddr * sockaddr, socklen_t socklent)

操作系統提供的connect系統調用并沒有提供timeout的參數設置而java卻有,我們先考察一下原生系統調用的超時策略。

connect系統調用

我們觀察一下此系統調用的kernel源碼,調用棧如下所示:

connect[用戶態]
|->SYSCALL_DEFINE3(connect)[內核態]
|->sock->ops->connect

由于我們考察的是tcp的connect,其socket的內部結構如下圖所示:1e365d54fb7dadf3d19ea14aa4260c6c.png
最終調用的是tcp_connect,代碼如下所示:

int tcp_connect(struct sock *sk) {
......
// 發送SYN
err = tcp_transmit_skb(sk, buff, 1, sk->sk_allocation);
...
/* Timer for repeating the SYN until an answer. */
// 由于是剛建立連接,所以其rto是TCP_TIMEOUT_INIT
inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
inet_csk(sk)->icsk_rto, TCP_RTO_MAX);
return 0;
}

又上面代碼可知,在tcp_connect設置了重傳定時器之后return回了tcp_v4_connect再return到inet_stream_connect。我們繼續考察:

int inet_stream_connect(struct socket *sock, struct sockaddr *uaddr,
int addr_len, int flags)
{
......
// tcp_v4_connect=>tcp_connect
err = sk->sk_prot->connect(sk, uaddr, addr_len);
// 這邊用的是sk->sk_sndtimeo
timeo = sock_sndtimeo(sk, flags & O_NONBLOCK);
......
inet_wait_for_connect(sk, timeo));
......
out:
release_sock(sk);
return err;

sock_error:
err = sock_error(sk) ? : -ECONNABORTED;
sock->state = SS_UNCONNECTED;
if (sk->sk_prot->disconnect(sk, flags))
sock->state = SS_DISCONNECTING;
goto out
}

由上面代碼可見,可以采用設置SO_SNDTIMEO來控制connect系統調用的超時,如下所示:

setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, len);

不設置SO_SNDTIMEO

如果不設置SO_SNDTIMEO,那么會由tcp重傳定時器在重傳超過設置的時候后超時,如下圖所示:

e8398c64ac20f2e70bb0a096f0de2dcd.png
這個syn重傳的次數由:

cat /proc/sys/net/ipv4/tcp_syn_retries 筆者機器上是5

來決定。那么我們就來看一下這個重傳到底是多長時間:

tcp_connect中:
// 設置的初始超時時間為icsk_rto=TCP_TIMEOUT_INIT為1s
inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS,
inet_csk(sk)->icsk_rto, TCP_RTO_MAX);

其重傳定時器的回掉函數為tcp_retransmit_timer:

void tcp_retransmit_timer(struct sock *sk)
{
......
// 檢測是否超時
if (tcp_write_timeout(sk))
goto out;
......
// icsk_rto = icsk_rto * 2,由于syn階段,所以isck_rto不會由于網絡傳輸而改變
// 重傳的時候會以1,2,4,8指數遞增
icsk->icsk_rto = min(icsk->icsk_rto << 1, TCP_RTO_MAX);
// 重設timer
inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, icsk->icsk_rto, TCP_RTO_MAX);
out:;
}

而計算tcp_write_timeout的邏輯則是在這篇blog中已經詳細描述過,

https://my.oschina.net/alchemystar/blog/1936433

只不過在connect時刻,重傳的計算以TCP_TIMEOUT_INIT為單位進行計算。而ESTABLISHED(read/write)時刻,重傳以TCP_RTO_MIN進行計算。那么根據這段重傳邏輯,我們就可以計算出不同tcp_syn_retries最終表現的超時時間。如下圖所示:

4055478bed6e83f001da695b1d1d3379.png
那么整理下表格,對于系統調用,connect的超時時間為:

tcp_syn_retriestimeout
1min(so_sndtimeo,3s)
2min(so_sndtimeo,7s)
3min(so_sndtimeo,15s)
4min(so_sndtimeo,31s)
5min(so_sndtimeo,63s)

上述超時時間和筆者的實測一致。

推薦一本書

這本書教你怎么用wireshark來分析網絡問題,非常有意思,而且很薄~

kernel代碼版本細微變化

值得注意的是,linux本身官方發布的2.6.32源碼對于tcp_syn_retries2的解釋和RFC并不一致(至少筆者閱讀的代碼如此,這個細微的變化困擾了筆者好久,筆者下載了和機器對應的內核版本后才發現代碼改了)。而redhat發布的2.6.32-431已經修復了這個問題(不清楚具體哪個小版本修改的),并將初始RTO設置為1s(官方2.6.32為3s)。這也是,不同內核小版本上的實驗會有不同的connect timeout表現的原因(有的抓包到的重傳SYN時間間隔為3,6,12……)。以下為代碼對比:

========================>linux 內核版本2.6.32-431<========================
#define TCP_TIMEOUT_INIT ((unsigned)(1*HZ)) /* RFC2988bis initial RTO value */

static inline bool retransmits_timed_out(struct sock *sk,
unsigned int boundary,
unsigned int timeout,
bool syn_set)
{
......
unsigned int rto_base = syn_set ? TCP_TIMEOUT_INIT : TCP_RTO_MIN;
......
timeout = ((2 << boundary) - 1) * rto_base;
......

}
========================>linux 內核版本2.6.32.63<========================
#define TCP_TIMEOUT_INIT ((unsigned)(3*HZ)) /* RFC 1122 initial RTO value */

static inline bool retransmits_timed_out(struct sock *sk,
unsigned int boundary
{
......
timeout = ((2 << boundary) - 1) * TCP_RTO_MIN;
......
}

另外,tcp_syn_retries重傳次數可以在單個socket中通過setsockopt設置。

JAVA connect API

現在我們考察下java的connect api,其connect最終調用下面的代碼:

Java_java_net_PlainSocketImpl_socketConnect(...){

if (timeout <= 0) {
......
connect_rv = NET_Connect(fd, (struct sockaddr *)&him, len);
.....
}else{
// 如果timeout > 0 ,則設置為nonblock模式
SET_NONBLOCKING(fd);
/* no need to use NET_Connect as non-blocking */
connect_rv = connect(fd, (struct sockaddr *)&him, len);
/*
* 這邊用系統調用select來模擬阻塞調用超時
*/
while (1) {
......
struct timeval t;
t.tv_sec = timeout / 1000;
t.tv_usec = (timeout % 1000) * 1000;
connect_rv = NET_Select(fd+1, 0, &wr, &ex, &t);
......
}
......
// 重新設置為阻塞模式
SET_BLOCKING(fd);
......
}
}

其和connect系統調用的不同點是,在timeout為0的時候,走默認的系統調用不設置超時時間的邏輯。在timeout>0時,將socket設置為非阻塞,然后用select系統調用去模擬超時,而沒有走linux本身的超時邏輯,如下圖所示:3ca34bf15bd7a1af6fc8e6275c372ca7.png
由于沒有java并沒有設置so_sndtimeo的選項,所以在timeout為0的時候,直接就通過重傳次數來控制超時時間。而在調用connect時設置了timeout(不為0)的時候,超時時間如下表格所示:

tcp_syn_retriestimeout
1min(timeout,3s)
2min(timeout,7s)
3min(timeout,15s)
4min(timeout,31s)
5min(timeout,63s)

socketTimeout

write系統調用的超時時間

socket的write系統調用最后調用的是tcp_sendmsg,源碼如下所示:

int tcp_sendmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg,
size_t size){
......
timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT);
......
while (--iovlen >= 0) {
......
// 此種情況是buffer不夠了
if (copy <= 0) {
new_segment:
......
if (!sk_stream_memory_free(sk))
goto wait_for_sndbuf;

skb = sk_stream_alloc_skb(sk, select_size(sk),sk->sk_allocation);
if (!skb)
goto wait_for_memory;
}
......
}
......
// 這邊等待write buffer有空間
wait_for_sndbuf:
set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
wait_for_memory:
if (copied)
tcp_push(sk, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH);
// 這邊等待timeo長的時間
if ((err = sk_stream_wait_memory(sk, &timeo)) != 0)
goto do_error;
......
out:
// 如果拷貝了數據,則返回
if (copied)
tcp_push(sk, flags, mss_now, tp->nonagle);
TCP_CHECK_TIMER(sk);
release_sock(sk);
return copied;
out_err:
// error的處理
err = sk_stream_error(sk, flags, err);
TCP_CHECK_TIMER(sk);
release_sock(sk);
return err;
}

從上面的內核代碼看出,如果socket的write buffer依舊有空間的時候,會立馬返回,并不會有timeout。但是write buffer不夠的時候,會等待SO_SNDTIMEO的時間(nonblock時候為0)。但是如果SO_SNDTIMEO沒有設置的時候,默認初始化為MAX_SCHEDULE_TIMEOUT,可以認為其超時時間為無限。那么其超時時間會有另一個條件來決定,我們看下sk_stream_wait_memory的源碼:

int sk_stream_wait_memory(struct sock *sk, long *timeo_p){
// 等待socket shutdown或者socket出現err
sk_wait_event(sk, &current_timeo, sk->sk_err ||
(sk->sk_shutdown & SEND_SHUTDOWN) ||
(sk_stream_memory_free(sk) &&
!vm_wait));
}

在write等待的時候,如果出現socket被shutdown或者socket出現錯誤的時候,則會跳出wait進而返回錯誤。在不考慮對端shutdown的情況下,出現sk_err的時間其實就是其write的timeout時間,那么我們看下什么時候出現sk->sk_err。

SO_SNDTIMEO不設置,write buffer滿之后ack一直不返回的情況(例如,物理機宕機)

物理機宕機后,tcp發送msg的時候,ack不會返回,則會在重傳定時器tcp_retransmit_timer到期后timeout,其重傳到期時間通過tcp_retries2以及TCP_RTO_MIN計算出來。其源碼可見筆者的blog:

https://my.oschina.net/alchemystar/blog/1936433

tcp_retries2的設置位置為:

cat /proc/sys/net/ipv4/tcp_retries2 筆者機器上是5,默認是15

SO_SNDTIMEO不設置,write buffer滿之后對端不消費,導致buffer一直滿的情況

和上面ack超時有些許不一樣的是,一個邏輯是用TCP_RTO_MIN通過tcp_retries2計算出來的時間。另一個是真的通過重傳超過tcp_retries2次數來time_out,兩者的區別和rto的動態計算有關。但是可以大致認為是一致的。

上述邏輯如下圖所示:

f1e492f875be9185361c341caa4f4cd0.png

write_timeout表格

tcp_retries2buffer未滿buffer滿
5立即返回min(SO_SNDTIMEO,(25.6s-51.2s)根據動態rto定
15立即返回min(SO_SNDTIMEO,(924.6s-1044.6s)根據動態rto定

java的SocketOutputStream的sockWrite0超時時間

java的sockWrite0沒有設置超時時間的地方,同時也沒有設置過SO_SNDTIMEOUT,其直接調用了系統調用,所以其超時時間和write系統調用保持一致。

readTimeout

ReadTimeout可能是最容易導致問題的地方。我們先看下系統調用的源碼:

read系統調用

socket的read系統調用最終調用的是tcp_recvmsg, 其源碼如下:

int tcp_recvmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
size_t len, int nonblock, int flags, int *addr_len)
{
......
// 這邊timeo=SO_RCVTIMEO
timeo = sock_rcvtimeo(sk, nonblock);
......
do{
......
// 下面這一堆判斷表明,如果出現錯誤,或者已經被CLOSE/SHUTDOWN則跳出循環
if(copied) {
if (sk->sk_err ||
sk->sk_state == TCP_CLOSE ||
(sk->sk_shutdown & RCV_SHUTDOWN) ||
!timeo ||
signal_pending(current))
break;
} else {
if (sock_flag(sk, SOCK_DONE))
break;

if (sk->sk_err) {
copied = sock_error(sk);
break;
}
// 如果socket shudown跳出
if (sk->sk_shutdown & RCV_SHUTDOWN)
break;
// 如果socket close跳出
if (sk->sk_state == TCP_CLOSE) {
if (!sock_flag(sk, SOCK_DONE)) {
/* This occurs when user tries to read
* from never connected socket.
*/
copied = -ENOTCONN;
break;
}
break;
}
.......
}
.......

if (copied >= target) {
/* Do not sleep, just process backlog. */
release_sock(sk);
lock_sock(sk);
} else /* 如果沒有讀到target自己數(和水位有關,可以暫認為是1),則等待SO_RCVTIMEO的時間 */
sk_wait_data(sk, &timeo);
} while (len > 0);
......
}

上面的邏輯如下圖所示:5c2df5e64cd4f5934babec82817a5788.png
重傳以及探測定時器timeout事件的觸發時機如下圖所示:181b467eb8d5a05f07e02ff0876249bb.png
如果內核層面ack正常返回而且對端窗口不為0,僅僅應用層不返回任何數據,那么就會無限等待,直到對端有數據或者socket close/shutdown為止,如下圖所示:aba4be23186fefa6cd6b2d63ac8d1f96.png
很多應用就是基于這個無限超時來設計的,例如activemq的消費者邏輯。

java的SocketInputStream的sockRead0超時時間

java的超時時間由SO_TIMOUT決定,而linux的socket并沒有這個選項。其sockRead0和上面的java connect一樣,在SO_TIMEOUT>0的時候依舊是由nonblock socket模擬,在此就不再贅述了。

ReadTimeout超時表格

C系統調用:

tcp_retries2對端無響應對端內核響應正常
5min(SO_RCVTIMEO,(25.6s-51.2s)根據動態rto定SO_RCVTIMEO==0?無限,SO_RCVTIMEO)
15min(SO_RCVTIMEO,(924.6s-1044.6s)根據動態rto定SO_RCVTIMEO==0?無限,SO_RCVTIMEO)

Java系統調用

tcp_retries2對端無響應對端內核響應正常
5min(SO_TIMEOUT,(25.6s-51.2s)根據動態rto定SO_TIMEOUT==0?無限,SO_RCVTIMEO
15min(SO_TIMEOUT,(924.6s-1044.6s)根據動態rto定SO_TIMEOUT==0?無限,SO_RCVTIMEO

對端物理機宕機之后的timeout

對端物理機宕機后還依舊有數據發送

對端物理機宕機時對端內核也gg了(不會發出任何包通知宕機),那么本端發送任何數據給對端都不會有響應。其超時時間就由上面討論的
min(設置的socket超時[例如SO_TIMEOUT],內核內部的定時器超時來決定)。

對端物理機宕機后沒有數據發送,但在read等待

這時候如果設置了超時時間timeout,則在timeout后返回。但是,如果僅僅是在read等待,由于底層沒有數據交互,那么其無法知道對端是否宕機,所以會一直等待。但是,內核會在一個socket兩個小時都沒有數據交互情況下(可設置)啟動keepalive定時器來探測對端的socket。如下圖所示:6fa730360265d10eb61af0127114f25b.png
大概是2小時11分鐘之后會超時返回。keepalive的設置由內核參數指定:

cat /proc/sys/net/ipv4/tcp_keepalive_time 7200 即兩個小時后開始探測
cat /proc/sys/net/ipv4/tcp_keepalive_intvl 75 即每次探測間隔為75s
cat /proc/sys/net/ipv4/tcp_keepalve_probes 9 即一共探測9次

可以在setsockops中對單獨的socket指定是否啟用keepalive定時器(java也可以)。

對端物理機宕機后沒有數據發送,也沒有read等待

和上面同理,也是在keepalive定時器超時之后,將連接close。所以我們可以看到一個不活躍的socket在對端物理機突然宕機之后,依舊是ESTABLISHED狀態,過很長一段時間之后才會關閉。

進程宕后的超時

如果僅僅是對端進程宕機的話(進程所在內核會close其所擁有的所有socket),由于fin包的發送,本端內核可以立刻知道當前socket的狀態。如果socket是阻塞的,那么將會在當前或者下一次write/read系統調用的時候返回給應用層相應的錯誤。如果是nonblock,那么會在select/epoll中觸發出對應的事件通知應用層去處理。
如果fin包沒發送到對端,那么在下一次write/read的時候內核會發送reset包作為回應。

nonblock

設置為nonblock=true后,由于read/write都是立刻返回,且通過select/epoll等處理重傳超時/probe超時/keep alive超時/socket close等事件,所以根據應用層代碼決定其超時特性。定時器超時事件發生的時間如上面幾小節所述,和是否nonblock無關。nonblock的編程模式可以讓應用層對這些事件做出響應。

總結

網絡編程中超時時間是個重要但又容易被忽略的問題,這個問題只有在遇到物理機宕機等平時遇不到的現象時候才會凸顯。筆者在經歷數次物理機宕機之后才好好的研究了一番,希望本篇文章可以對讀者在以后遇到類似超時問題時有所幫助。

推薦一本書,這本書教你怎么用wireshark來分析網絡問題,非常有意思,而且很薄~

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/539067.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/539067.shtml
英文地址,請注明出處:http://en.pswp.cn/news/539067.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

java 數據庫連接 釋放_java - 數據庫連接池耗盡 - Java - 堆棧內存溢出

timeout變量似乎不對應于連接空閑的時間&#xff0c;而是對應于池等待返回新連接或拋出異常的時間(我看了一下這個源代碼 &#xff0c;不知道是不是已是最新)。 我認為跟蹤“空閑”連接是相當困難的&#xff0c;因為在這種情況下“空閑”真正意味著什么&#xff1f; 您可能希望…

spark DAGScheduler、TaskSchedule、Executor執行task源碼分析

摘要 spark的調度一直是我想搞清楚的東西&#xff0c;以及有向無環圖的生成過程、task的調度、rdd的延遲執行是怎么發生的和如何完成的&#xff0c;還要就是RDD的compute都是在executor的哪個階段調用和執行我們定義的函數的。這些都非常的基礎和困難。花一段時間終于弄白了其中…

一個“老”程序員的思考

本文是一位40歲老程序員對職業生涯的思考&#xff0c;建議多學習接觸新事物&#xff0c;將精力投入到衰竭期比較長的知識領域&#xff0c;這些都是很有建設性的。下面是大意譯文&#xff1a; 我是一個程序員&#xff0c;幾個月前剛過完害羞的四十歲生日。這是一個星期六的早晨…

python socketio_python3--socketIO_client 摸索怕坑指南

前言:websocket和socketIO是全然不同的兩個東西,websocket的話 使用自帶ws的庫就可以完成一些功能,但是socketIO屬于sw的另外一塊內容工作中遇到了一個監控socketIO傳輸的聊天信息監控的需求.研究了一陣 話不多說 上代碼!from socketIO_client import SocketIO, BaseNamespacei…

java正則表達式非貪婪_正則表達式中的貪婪與非貪婪匹配模式

1.首先看看下面的例子&#xff1a;try{str"abcdefgabcdefghijkl";re1str.match(/[\W\w]?/ig);alert("非貪婪模式:\r\n\r\n&#xff11;&#xff1a;"re1[0]"\r\n&#xff12;&#xff1a;"re1[1]);re1str.match(/[\W\w]/ig);alert("貪婪模…

python編程運算符號-Python中的邏輯運算符

參考資料&#xff1a;http://blog.csdn.net/sasoritattoo/article/details/12451359 一、邏輯判斷詞not 1.在python中not是邏輯判斷詞&#xff0c;用于布爾型True和False&#xff0c;not True為False&#xff0c;not False為True&#xff0c;以下是幾個常用的not的用法&#xf…

代碼實現tan graph model for classification_自定義 Estimator 實現(以BERT為例)

本文將主要介紹tensorflow 的Estimator 這個高級API&#xff0c;它的主要作用就是提出一個高級范式&#xff08;paradigm&#xff09;&#xff0c;將模型的訓練&#xff0c;驗證&#xff0c;預測&#xff0c;以及保存規范起來&#xff0c;免去了tensorflow的Session.run 的操作…

Linux查看文件內容

cat 一次性將文件內容全部輸出到控制臺 more 可以翻頁查看 空格&#xff1a;下翻一頁 b&#xff1a;上翻一頁 q&#xff1a;退出 less 可以翻頁查看 空格&#xff1a;下翻一頁 b&#xff1a;上翻一頁 q&#xff1a;退出 向上鍵&#xff1a;上翻一行 向下鍵&#xff1…

刷新頁面微信二維碼圖片隨機換,點擊按鈕自動復制對應微信號

<div style"text-align: center;"> <p style"font-size: 18px;color: rgb(255, 79, 121);">添加微信號</p> <span style"font-size: 18px;margin-left: 10%;" id"cod">jyl88jimei</span><br /> &…

SecureCRT向多個tab窗口發命令

可以利用SecureCRT的 Chat Windows選項Send chat to all tabs來同時在服務器上執行相同的命令&#xff0c;具體步驟如下&#xff1a; 一、首先在SecureCRT里同時打開多個服務器session 二、選擇菜單欄View -->Chat Windows 對號&#xff0c;此時所有服務器連接下方應該有個…

英雄聯盟怎么解除小窗口_英雄聯盟手游怎么加好友_英雄聯盟手游怎么加好友一起玩_資訊...

英雄聯盟手游是騰訊聯合英雄聯盟開發商拳頭開發的英雄聯盟手游。不僅能夠高度還原端游的經典操作和競技體驗&#xff0c;也具有非常多創新的元素&#xff0c;對于英雄聯盟的全球生態布局具有重要意義。英雄聯盟手游游戲中有非常多的英雄可以供玩家選擇&#xff0c;并且擁有排位…

jfinal mysql 配置文件_JFinal 如何將操作日志存入到數據庫中

展開全部操作日志, 也分粗細顆粒.比如常見的 配置JFinal的Handler, 配置LogHandler的處理器&#xff0c;Handler可以接管所有web請求, 這里可以做粗顆粒的處理, 對每一個請62616964757a686964616fe59b9ee7ad9431333365653839求做入庫處理, 如果訪問量大時, 入庫操作做列隊處理就…

快速入門python_一天快速入門 Python

Python 是由Guido Van Rossum在 90 年代早期設計&#xff0c;現在是最常用的編程語言之一。特別是人工智能的火熱&#xff0c;再加之它的語法簡潔且優美&#xff0c;實乃初學者入門AI必備的編程語言。作者 | yuquanle責編 | 屠敏Python基本語法標識符第一個字符必須是英文字母或…

Sonar與jenkins集成

2019獨角獸企業重金招聘Python工程師標準>>> 參考文檔&#xff1a;http://blog.csdn.net/kefengwang/article/details/54377055 一.下載&#xff1a;wget https://fossies.org/linux/misc/sonarqube-7.0.zip 二.配置sonar.properties ## sudo vim /opt/sonarqube-6.…

python變量和常量_5、python的變量和常量

今天看看python的變量和常量&#xff0c;這是python中最基本的兩個概念。首先先說一下解釋器執行Python的過程&#xff1a; python3 C:\test.py1. 啟動python解釋器(內存中)2. 將C:\test.py內容從硬盤讀入內存(這一步與文本編輯器是一樣的)3. 執行讀入內存的代碼如果想要永久…

eplan連接定義點不顯示_EPLAN電氣圖實例--控制柜(控制面板)

EPLAN電氣圖實例--控制柜(控制面板)上期回顧(上期主要畫了硬件的布局圖)&#xff1a;這期主要畫一個控制面板控制柜布局1.0 上期主要做了一個長方形的結構板&#xff0c;里面插入了一個結構盒&#xff0c;然后放置一個HMI的宏(這里是KTP1000&#xff0c;在官網隨便找下就行了)&…

virtualbox安裝android6.0并設置分辨率為1920x1080x32

下載安裝&#xff1a;https://www.cnblogs.com/wynn0123/p/6288344.html 這里我做的是下載android6.0-64bit&#xff0c;然后文件系統只支持ext4 安裝完成之后我的虛擬機名稱是Android6.0 設置分辨率為1920x1080x32&#xff1a;https://my.oschina.net/xldc/blog/290155 首先&a…

python中裝飾器修復技術_python3之裝飾器修復技術@wraps

普通函數def f():"""這是一個用來測試裝飾器修復技術的函數"""print("哈哈哈")if __name__ __main__:print("執行的函數名:", f.__name__)print("函數的注釋:", f.__doc__)# 打印結果執行的函數名: f函數的注釋:…

markdown 語法_markdown特殊語法之上下標

markdown特殊語法之上下標?markdown的基本語法很簡單&#xff0c;百度一下就可以了&#xff0c;有空的話我再轉載一些過來。我想的是平常其實需要用到的一些輸入技巧&#xff0c;特殊用法或者擴展語法&#xff0c;還有一些難點倒是要記錄學習一下。在寫作的時候&#xff0c;大…

oracle安裝向導卡住了_JDK 8 的安裝與配置

一、安裝環節1. 打開網頁https://www.oracle.com/java/technologies/javase/javase-jdk8-downloads.html2.找到對象的版本 盡量從官網下載(官網可能會比較慢&#xff0c;也可以通過瀏覽器輸入jdk版本號進行下載)官網下載需要注冊一個賬號3.雙擊下載的 exe,如 jdk-8u131-windows…