Socket網絡編程核心API深入分析(一):bind函數

2020-12-10 酷扯兒

本文轉載自【微信公眾號:小碼逆襲,ID:gh_7c5a039380a0】經微信公眾號授權轉載,如需轉載與原文作者聯繫

本篇文章你能學到:

1、實現簡單的c++版本的伺服器和客戶端

2、深入理解bind()函數,bind的系統調用過程,bind是如何處理埠衝突的,面試的時候你就這樣講。

注意:本片文章涉及到的內核源碼來自linux內核版本3.6

簡單的伺服器與客戶端實現

本篇文章的重點在於從底層深入分析bind()函數,相信已經能夠自己實現一個簡單的伺服器和客戶端並進行交互,下面是一個簡單的demo,幫助大家複習一下socket編程api的調用過程。

伺服器server.cpp

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#include <unistd.h>

#include <arpa/inet.h>

#include <sys/socket.h>

#include <netinet/in.h>

int main(){

//創建套接字

int serv_sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);

//將套接字和IP、埠綁定

struct sockaddr_in serv_addr;

memset(&serv_addr, 0, sizeof(serv_addr)); //每個字節都用0填充

serv_addr.sin_family = AF_INET; //使用IPv4地址

serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //具體的IP位址

serv_addr.sin_port = htons(1234); //埠

bind(serv_sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));

//進入監聽狀態,等待用戶發起請求

listen(serv_sock, 20);

//接收客戶端請求

struct sockaddr_in clnt_addr;

socklen_t clnt_addr_size = sizeof(clnt_addr);

int clnt_sock = accept(serv_sock, (struct sockaddr*)&clnt_addr, &clnt_addr_size);

//向客戶端發送數據

char str[] = "http://c.biancheng.net/socket/";

write(clnt_sock, str, sizeof(str));

//關閉套接字

close(clnt_sock);

close(serv_sock);

return 0;

}

客戶端client.cpp

#include <stdio.h>

#include <string.h>

#include <stdlib.h>

#include <unistd.h>

#include <arpa/inet.h>

#include <sys/socket.h>

int main(){

//創建套接字

int sock = socket(AF_INET, SOCK_STREAM, 0);

//向伺服器(特定的IP和埠)發起請求

struct sockaddr_in serv_addr;

memset(&serv_addr, 0, sizeof(serv_addr)); //每個字節都用0填充

serv_addr.sin_family = AF_INET; //使用IPv4地址

serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //具體的IP位址

serv_addr.sin_port = htons(1234); //埠

connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));

//讀取伺服器傳回的數據

char buffer[40];

read(sock, buffer, sizeof(buffer)-1);

printf("Message form server: %s\n", buffer);

//關閉套接字

close(sock);

return 0;

}

socket編程TCP協議的調用流程如下:

深入分析bind函數

我從應用層出發,沿著網絡協議棧,從bind()的系統調用、到Socket層實現,最終到它的TCP層實現。

應用層

int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen);

給socket描述符綁定IP和埠,一般伺服器才需要。

也可交給系統來選擇:

my_addr.sin_port = 0; 系統隨機選擇一個未被使用的埠

my_addr.sin_addr.s_addr = INADDR_ANY; 自動填入本機的IP位址

#define INADDR_ANY ((unsigned long int) 0x00000000)

埠號的範圍為0 ~ 65535。調用bind()時,一般不要把埠號置為小於1024的值,因為1到1023是保留埠號。

系統調用

bind()是由glibc提供的,聲明位於include/sys/socket.h中,實現位於sysdeps/mach/hurd/bind.c中,主要是用來從用戶空間進入名為sys_socketcall的系統調用,並傳遞參數。sys_scoketcall()實際上是所有socket函數進入內核空間的共同入口。

在sys_socketcall()中會調用sys_bind()。

SYSCALL_DEFINE2(socketcall, int, call, unsigned long __user *, args)

{

...

switch(call) {

...

case SYS_BIND:

err = sys_bind(a0, (struct sockaddr __user *)a1, a[2]);

break;

...

}

return err;

}

經過了socket層的總入口sys_socketcall(),現在進入sys_bind()。

/*

* Bind a name to a socket. Nothing much to do here since it's the protocol's responsibility

* to handle the local address.

* We move the socket address to kernel space before we call the protocol layer (having also

* checked the address is ok).

*/

SYSCALL_DEFINE3(bind, int, fd, struct sockaddr __user *, umyaddr, int, addrlen)

{

struct socket *sock;

struct sockaddr_storage address;

int err, fput_needed;

/* 通過文件描述符fd,找到對應的socket。

* 以fd為索引從當前進程的文件描述符表files_struct中找到對應的file實例,

* 然後從file實例的private_data成員中獲取socket實例。

*/

sock = sockfd_lookup_light(fd, &err, &fput_needed);

if (sock) {

/* 把用戶空間的地址複製到內核空間,成功返回0 */

err = move_addr_to_kernel(umyaddr, addrlen, &address);

if (err >= 0) {

/* SELInux相關 */

err = security_socket_bind(sock, (struct sockaddr *)&address, addrlen);

if (!err)

/* socket層的操作函數集。如果是SOCK_STREAM的話,proto_ops是inet_stream_ops,

* 接下來調用的是inet_bind()。

*/

err = sock->ops->bind(sock, (struct sockaddr *)&address, addrlen);

}

fput_light(sock->file, fput_needed);

}

return err;

}

通過文件描述符,找到對應的file結構。

static struct socket *sockfd_lookup_light(int fd, int *err, int *fput_needed)

{

struct file *file;

struct socket *sock;

*err = -EBADF; /* Bad file number */

/* 從當前進程的files_struct中找到網絡文件系統中的file指針,並增加它的引用計數 */

file = fget_light(fd, fput_needed);

if (file) {

sock = sock_from_file(file, err); /* 通過file找到對應的socket */

if (sock)

return sock;

fput_light(file, *fput_needed); /* 失敗的話減少file的引用計數 */

}

return NULL;

}

通過file結構,找到對應的socket結構。

struct socket *sock_from_file(struct file *file, int *err)

{

if (file->f_op == &socket_file_ops) /* 說明此file對應一個socket */

return file->private_data; /* set in sock_map_fd */

*err = -ENOTSOCK;

return NULL;

}

把用戶空間的socket地址複製到內核空間,同時檢查是否合法,成功返回0。

int move_addr_to_kernel(void __user *uaddr, int ulen, struct sockaddr_storage *kaddr)

{

if (ulen < 0 || ulen > sizeof(struct sockaddr_storage)) /* socket地址長度是否合法 */

return -EINVAL;

if (ulen == 0)

return 0;

if (copy_from_user(kaddr, uaddr, ulen))

return -EFAULT; /* socket地址是否合法 */

return audit_sockaddr(ulen, kaddr);

}

Socket層

SOCK_STREAM套接口的socket層操作函數集實例為inet_stream_ops,其中綁定函數為inet_bind()。

const struct proto_ops inet_stream_ops = {

.family = PF_INET,

.owner = THIS_MODULE,

...

.bind = inet_bind, /* socket層的bind實現 */

...

}

socket層做的主要事情為合法性檢查、綁定IP位址,而真正的埠綁定是在TCP層進行的。

int inet_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len)

{

struct sockaddr_in *addr = (struct sockaddr_in *)uaddr;

struct sock *sk = sock->sk; /* 傳輸層實例 */

struct inet_sock *inet = inet_sk(sk); /* INET實例 */

unsigned short snum; /* 要綁定的埠 */

int chk_addr_ret; /* IP位址類型 */

int err;

/* If the socket has its own bind function then use it. (RAW)

* 用於原始套接字,TCP協議實例tcp_prot不含此函數指針。

*/

if (sk->sk_prot->bind) {

err = sk->sk_prot->bind(sk, uaddr, addr_len);

goto out;

}

err = -EINVAL;

if (addr_len < sizeof(struct sockaddr_in)) /* socket地址長度錯誤 */

goto out;

if (addr->sin_family != AF_INET) { /* 非INET協議族 */

/* Compatibility games: accept AF_UNSPEC (mapped to AF_INET)

* only if s_addr is INADDR_ANY.

*/

err = -EAFNOSUPPORT;

if (addr->sin_family != AF_UNSPEC || addr->sin_addr.s_addr != htonl(INADDR_ANY))

goto out;

}

/* 在路由中檢查IP位址類型,單播、多播還是廣播 */

chk_addr_ret = inet_addr_type(sock_net(sk), addr->sin_addr.s_addr);

/* Not specified by any standard per-se, however it breaks too many applications

* when removed. It is unfortunate since allowing applications to make a non-local

* bind solves several problems with systems using dynamic addressing.

* (ie. your servers still start up even if your ISDN link is temporarily down)

*/

/* sysctl_ip_nonlocal_bind表示是否允許綁定非本地的IP位址。

* inet->freebind表示是否允許綁定非主機地址。

* 這裡需要允許綁定非本地地址,除非是發送給自己、多播或廣播。

*/

err = -EADDRNOTAVAIL; /* Cannot assign requested address */

if (! sysctl_ip_nonlocal_bind && ! (inet->freebind || inet->transparent) &&

addr->sin_addr.s_addr != htonl(INADDR_ANY) &&

chk_addr_ret != RTN_LOCAL && chk_addr_ret != RTN_MULTICAST &&

chk_addr_ret != RTN_BROADCAST)

goto out;

snum = ntohs(addr->sin_port); /* 要綁定的埠 */

err = -EACCES; /* Permission denied */

/* snum為0表示讓系統隨機選擇一個未使用的埠,因此是合法的。

* 如要需要綁定的埠為1 ~ 1023,則需要對應的特權。

*/

if (snum && snum < PORT_SOCK && ! capable(CAP_NET_BIND_SERVICE))

goto out;

lock_sock(sk);

/* Check these errors (active socket, double bind).

* 如果套接字不在初始狀態TCP_CLOSE,或者已經綁定埠了,則出錯。

* 一個socket最多可以綁定一個埠,而一個埠則可能被多個socket共用。

*/

err = -EINVAL;

if (sk->sk_state != TCP_CLOSE || inet->inet_num)

goto out_release_sock;

/* We keep a pair of addresses. rcv_saddr is the one used by hash lookups,

* and saddr is used for transmit.

* In the BSD API these are the same except where it would be illegal to use them

* (multicast/broadcast) in which case the sending device address is used.

*/

inet->inet_rcv_saddr = inet->inet_saddr = addr->sin_addr.s_addr; /* 綁定地址 */

if (chk_addr_ret == RTN_MULTICAST || chk_addr_ret == RTN_BROADCAST)

inet->inet_saddr = 0; /* Use device */

/* Make sure we are allowed to bind here.

* 如果使用的是TCP,則sk_prot為tcp_prot,get_port為inet_csk_get_port()

* 埠可用的話返回0。

*/

if (sk->sk_prot->get_port(sk, snum)) {

inet->inet_saddr = inet->inet_rcv_saddr = 0;

err = -EADDRINUSE;

goto out_release_sock;

}

/* inet_rcv_saddr表示綁定的地址,接收數據時用於查找socket */

if (inet->inet_rcv_saddr)

sk->sk_userlocks |= SOCK_BINDADDR_LOCK; /* 表示綁定了本地地址 */

if (snum)

sk->sk_userlocks |= SOCK_BINDPORT_LOCK; /* 表示綁定了本地埠 */

inet->inet_sport = htons(inet->inet_num); /* 綁定埠 */

inet->inet_daddr = 0;

inet->inet_dport = 0;

sk_dst_reset(sk);

err = 0;

out_release_sock:

release_sock(sk);

out:

return err;

}

/* Sockets 0 - 1023 can't be bound to unless you are superuser */

#define PORT_SOCK 1024

/* Allows binding to TCP/UDP sockets below 1024 */

#define CAP_NET_BIND_SERVICE 10

TCP層

SOCK_STREAM套接口的TCP層操作函數集實例為tcp_prot,其中埠綁定函數為inet_csk_get_port()。

struct proto tcp_prot = {

.name = "TCP",

.owner = THIS_MODULE,

...

.get_port = inet_csk_get_port, /* TCP層bind()相關操作 */

...

};

和較早的內核版本不同,現在系統自動選擇埠時,也可以復用埠了。

/* Obtain a reference to a local port for the given sock,

* if snum is zero it means select any available local port.

*/

int inet_csk_get_port(struct sock *sk, unsigned short snum)

{

struct inet_hashinfo *hashinfo = sk->sk_prot->h.hashinfo; /* 指向tcp_hashinfo */

struct inet_bind_hashbucket *head;

struct hlist_node *node;

struct inet_bind_bucket *tb;

int ret, attempts = 5;

struct net *net = sock_net(sk);

int smallest_size = -1, smallest_rover;

local_bh_disable(); /* 禁止下半部,防止和進程衝突 */

/* 如果snum為0,系統自動為sock選擇一個埠號 */

if (! snum) {

int remaining, rover, low, high;

again:

inet_get_local_port_range(&low, &high); /* 獲取埠號的取值範圍 */

remaining = (high - low) + 1; /* 取值範圍內埠號的個數 */

smallest_rover = rover = net_random() % remaining + low; /* 隨機選取範圍內的一個埠 */

smallest_size = -1;

do {

if (inet_is_reserved_local_port(rover)) /* 查看埠是否屬於保留的 */

goto next_nolock; /* rover加1,繼續 */

/* 根據埠號,確定所在的哈希桶 */

head = &hashinfo->bhash[inet_bhashfn(net, rover, hashinfo->bhash_size)];

spin_lock( &head->lock); /* 鎖住哈希桶 */

inet_bind_bucket_for_each(tb, node, &head->chain) /* 從頭遍歷哈希桶 */

/* 如果埠被使用了 */

if (eq(ib_net(tb), net) && tb->port == rover) {

if (tb->fastreuse > 0 && sk->sk_reuse && sk->sk_state != TCP_LISTEN &&

tb->num_owners < smallest_size || smallest_size == -1)) {

smallest_size = tb->num_owners; /* 記下這個埠使用者的個數 */

smallest_rover = rover; /* 記下這個埠 */

/* 如果系統綁定的埠已經很多了,那麼就判斷埠是否有綁定衝突*/

if (atomic_read(&hashinfo->bsockets) > (high - low) + 1 &&

! inet_csk(sk)->icsk_af_ops->bind_conflict(sk, tb, false)) {

snum = smallest_rover; /* 沒有衝突,使用此埠 */

goto tb_found;

}

}

/* 檢查是否有埠綁定衝突,該埠是否能重用 */

if (! inet_csk(sk)->icsk_af_ops->bind_conflict(sk, tb, false)) {

snum = rover;

goto tb_found;

}

goto next; /* 此埠不可重用,看下一個 */

}

break; /* 找到了沒被用的埠,退出 */

next:

spin_unlock(&head->lock);

next_nolock:

if (++rover > high)

rover = low;

} while(--remaining > 0);

/* Exhausted local port range during search? It is not possible for us to be holding

* one of the bind hash locks if this test triggers, because if 'remaining' drops to zero,

* we broke out of the do/while loop at the top level, not from the 'break' statement.

*/

ret = 1;

if (remaining <= 0) { /* 完全遍歷 */

if (smallest_size != -1) {

snum = smallest_rover;

goto have_snum;

}

goto fail;

}

/* OK, here is the one we will use.

* HEAD is non-NULL and we hold it's mutex.

*/

snum = rover; /* 自動選擇的可用埠 */

} else { /* 如果有指定要綁定的埠 */

have_snum:

head = &hashinfo->bhash[inet_bhashfn(net, snum, hashinfo->bhash_size)];

spin_lock(&head->lock);

inet_bind_bucket_for_each(tb, node, &head->chain)

if (net_eq(ib_net(tb), net) && tb->port == snum)

goto tb_found; /* 發現埠在用 */

}

tb = NULL;

goto tb_not_found;

tb_found:

/* 埠上有綁定sock時 */

if (! hlist_empty(&tb->owners)) {

/* 這是強制的綁定啊,不管埠是否會綁定衝突!*/

if (sk->sk_reuse == SK_FORCE_REUSE)

goto success;

if (tb->fastreuse > 0 && sk->sk_reuse && sk->sk_state != TCP_LISTEN &&

smallest_size == -1) { /* 指定埠的情況 */

goto success;

} else {

ret = 1;

if (inet_csk(sk)->icsk_af_ops->bind_conflict(sk, tb, true)) { /* 埠綁定衝突 */

/* 自動分配的埠綁定衝突了,再次嘗試,最多重試5次。

* 我覺得以下if不必要,因為自動選擇時goto tb_found之前都有檢測過了。

*/

if (sk->sk_reuse && sk->sk_state != TCP_LISTEN && smallest_size != -1

&& --attempts >= 0) {

spin_unlock(&head->lock);

goto again;

}

goto fail_unlock; /* 失敗 */

}

}

}

tb_not_found:

ret = 1;

/* 申請和初始化一個inet_bind_bucket結構 */

if (!tb && (tb = inet_bind_bucket_create(hashinfo->bind_bucket_cachep, net, head, snum)) == NULL)

goto fail_unlock;

if (hlist_empty(&tb->owners)) {

if (sk->sk_reuse && sk->sk_state != TCP_LISTEN)

tb->fastreuse = 1;

else

tb->fastreuse = 0;

} else if (tb->fastreuse && (! sk->sk_reuse || sk->sk_state == TCP_LISTEN))

tb->fastreuse = 0;

success:

/* 賦值icsk中的inet_bind_bucket */

if (! inet_csk(sk)->icsk_bind_hash)

inet_bind_hash(sk, tb, snum);

WARN_ON(inet_csk(sk)->icsk_bind_hash != tb);

ret = 0;

fail_unlock:

spin_unlock(&head->lock);

fail:

local_bh_enable();

return ret;

}

埠區間

我們可以指定系統自動分配埠號時,埠的區間:

/proc/sys/net/ipv4/ip_local_port_range,默認為:32768 61000

也可以指定要保留的埠區間:

/proc/sys/net/ipv4/ip_local_reserved_ports,默認為空。

/* This struct holds the first and last local port number.

* 用於系統自動分配的埠區間。

*/

struct local_ports sysctl_local_ports __read_mostly = {

.lock = __SEQLOCK_UNLOCKED(sysctl_local_ports.lock),

.range = {32768, 61000},

};

埠綁定衝突

看下面這段代碼的實現:

int inet_csk_bind_conflict(const struct sock *sk, const struct inet_bind_bucket *tb, bool relax)

{

struct sock *sk2;

struct hlist_node *node;

int reuse = sk->sk_reuse; /* SO_REUSEADDR,表示處於TIME_WAIT狀態的埠允許重用 */

/* Unlike other sk lookup places we do not check for sk_net here, since all the socks

* listed in tb->owners list belong to the same net - the one this bucket belongs to.

* 遍歷此埠上的sock。

*/

sk_for_each_bound(sk2, node, &tb->owners) {

/* 衝突的條件1:不是同一socket、綁定在相同的設備上 */

if (sk != sk2 && ! inet_v6_ipv6only(sk2) && (! sk->sk_bound_dev_if || ! sk2->sk_bound_dev_if

|| sk->sk_bound_dev_if == sk2->sk_bound_dev_if)) {

/* 衝突的條件2:綁定在相同的IP上

* 衝突的條件3(符合一個即滿足):

* 3.1 本socket不允許重用

* 3.2 鍊表中的socket不允許重用

* 3.3 鍊表中的socket處於監聽狀態

*/

if (! reuse || ! sk2->sk_reuse || sk2->sk_state == TCP_LISTEN) {

const __u32 sk2_rcv_saddr = sk_rcv_saddr(sk2); /* sk2的綁定IP */

if (! sk2_rcv_saddr || ! sk_rcv_saddr(sk) || sk2_rcv_saddr == sk_rcv_saddr(sk))

break; /* 衝突了 */

}

/* 覺得這段代碼有好多冗餘,可以精簡下:)

* 3.4 relax為false

*/

if (! relax && reuse && sk2->sk_reuse && sk2->sk_state != TCP_LISTEN) {

const __be32 sk2_rcv_saddr = sk_rcv_saddr(sk2);

if (! sk2_rcv_saddr || ! sk_rcv_saddr(sk) || sk2_rcv_saddr == sk_rcv_saddr(sk))

break;

}

}

}

return node != NULL;

}

什麼條件下會出現衝突呢?

同時符合以下條件才會衝突:

綁定的設備相同(不允許自動選擇設備)綁定的IP位址相同(不允許自動選擇IP)以下條件任意一個:要綁定的socket不允許重用已綁定的socket不允許重用已綁定的socket處於監聽狀態 relax參數為false

能夠重用埠號的情況

綁定的設備不同綁定的IP位址不同要綁定的socket允許重用,且已綁定的socket允許重用,且已綁定的socket不處於監聽狀態,relex參數為true。自動選擇埠的思路

相關焦點

  • Linux C語言高級編程之Socket網絡編程!
    Socket網絡編程網絡上的兩個程序通過一個雙向的通信連接實現數據的交換,這個連接的一端稱為一個
  • Linux C Socket Api詳解
    文章主要梳理於《UNIX 環境高級編程第二版》 第十六章 網絡IPC: 套接字以前都只是在網上搜的能用的例子,對一些參數不是很清楚,這次匯總。而且網絡通信還是很常用的通信手段。UNIX 環境高級編程對Socket通信的描述是套接字網絡IPC(進程間通信),可以用於計算機間通信也可用於計算機內通信,管道、消息隊列、信號量以及共享內存等都是屬於計算機內通信的情況。一、 套接字Api詳細介紹1. 套接字描述符首先會先到的是文件描述符,對Linux一切皆文件的哲學又多懂了一點兒點兒。套接字是通信端點的抽象。
  • PHP SOCKET編程
    此系列函數包括fsockopen,pfsockopen這兩個函數的具體信息可以查詢php.net的用戶手冊他們均會返回一個資源編號對於這個資源可以使用幾乎所有對文件操作的函數對其進行操作如fgets(),fwrite(), fclose()等單注意的是所有函數遵循這些函數面對網絡信息流時的規律,例如:fread() 從文件指針 handle
  • Windows編程技術:Socket通信(上)
    Socket翻譯成中文就是「套接字」的意思,所謂的Socket編程就是指用計算機語言通過編程來實現計算機之間的通信問題。Socket通信技術即就是兩臺聯網或者多臺聯網的計算機之間的數據交換技術,這就涉及著通信端的協議等等問題。
  • 網絡編程基礎學習筆記2:socket接口及網絡編程三要素
    對於網絡編程而言,由於其底層語言是通過C語言來實現的,所以建議在學習的時候使用C語言來進行學習!網絡編程中常用的socket接口如下:socket()接口,用於創建socket;bind():用於綁定socket到本地地址和埠,通常由服務端調用(客戶端一般直接由connect直接主動去連結服務端);listen():用於開啟監聽模式,TCP專用;accept():用於伺服器等待客戶端連接,該情況一般在阻塞狀態下產生
  • Python Socket 編程學習筆記
    套接字(socket):套接字是對網絡通訊過程中端點的抽象表示,包含進行網絡通信必須的五種信息:連接使用的協議,本地主機的IP位址,本地進程的協議埠,遠地主機的IP位址,遠地進程的協議埠。通過socket,就能建立server和client之間的連接了。2.
  • 簡單說說Python Socket編程步驟?
    小猿會從最基礎的面試題開始,每天一題。如果參考答案不夠好,或者有錯誤的話,麻煩大家可以在留言區給出自己的意見和討論,大家是要一起學習的 。廢話不多說,開始今天的題目:問:簡單說說Python socket編程步驟?
  • 程式設計師的術與道:術——編程基本功之網絡編程
    一、常用網絡服務服務軟體——DHCP伺服器——dhcpd郵件發送伺服器——sendmail郵件列表伺服器——mailman接收郵件的伺服器——pop3web站點——apache/nginx防火牆服務——iptables工具配置 ——netfilter二、 網路診斷逐步檢查網絡的各個層次:物理連結、鏈路層、網絡層直到應用層,熟悉使用各種如下的工具
  • 從socket到TCP協議,透徹理解網絡編程
    網絡協議也是一樣的,通過對數據格式的規範化,從而使計算機之間能夠彼此明確對方的意圖。下面本文介紹一下網絡編程,網絡編程也稱為socket編程,socket通常譯作「套接字」,但原意其實意譯應該為」接口「。也就是作業系統提供給開發人員進行網絡開發的API接口。這套接口通常可以參數的調整支持多種協議,包括TCP、UDP和IP等等。下面本文從套接字編程和協議兩方面分別詳細的進行介紹。
  • Python網絡編程socket模塊實例解析
    網絡也是IO的一種,在Python中有專門的socket模塊用來處理網絡操作的。
  • Python入門基礎之socket多線程編程,TCP伺服器和客戶端通信
    在上一篇文章中,我介紹了一下python3 socket編程的基礎,包括TCP、UDP客戶端和伺服器的建立過程(連結在最下方)。不過那個只是單線程的,伺服器一次只能和一個客戶端會話,多個客戶端的話只能等待。我們平時的應用中,伺服器肯定是要並發的,所以,今天將介紹socket編程的多線程編程。
  • PHP編寫基本的Socket程序
    我的目的因為在以後的學習中,我可能會用到網絡方面的內容,但同時很多寫 PHP 的 coder 都沒寫過 socket 程序,但是肯定聽說過它,也肯定聽說過網絡編程這個詞;所以為了今後的學習,我打算在這裡先簡單的講解下相關知識,本篇博文自帶實例程序,代碼託管在碼云:php-socket-base-code
  • Java socket編程
    一,網絡編程中兩個主要的問題一個是如何準確的定位網絡上一臺或多臺主機,另一個就是找到主機後如何可靠高效的進行數據傳輸。在TCP/IP協議中IP層主要負責網絡主機的定位,數據傳輸的路由,由IP位址可以唯一地確定Internet上的一臺主機。
  • python socket編程的理解
    編寫server的步驟第一步創建socket對象。調用socket構造函數。如:socket = socket.socket( family, type )family參數代表地址家族,可為AF_INET或AF_UNIX。
  • python socket實戰(二)
    每周一總結,準時為你充電上周講了1和2部分,這周接著講3和4,希望給以後用到socket編程的同學一些參考。這個在c語言編程中尤為常見。例如,有一個如下到結構體,python如何用socket進行通信傳遞該消息呢。
  • 超詳細的Socket通信原理和實例講解
    socket的基本操作3.1、socket()函數3.2、bind()函數3.3、listen()、connect()函數3.4、accept()函數3.5、read()、write()函數等3.6、close()函數 socket中TCP的三次握手建立連接詳解 socket中TCP的四次握手釋放連接詳解
  • 網絡協議-TCP和UDP最完整的區別介紹
    時確定  5.UDP:在sendto/recvfrom函數中每次均 需指定地址信息  6.UDP:shutdown函數無效 編程區別   通常我們在說到網絡編程時默認是指TCP編程,即用前面提到的socket函數創建一個socket用於TCP通訊,函數參數我們通常填為SOCK_STREAM。
  • Linux系統下socket編程socket接口介紹(二)
    (2)recv和read:函數recv和之前介紹的read函數的用法差不多,我們還是用man手冊來查看一它的形式和用法:   #include <sys/types.h>   #include <sys/socket.h>   ssize_t recv(int sockfd, void *buf,
  • Socket基本原理和套接字
    每一層的功能都必須相同,也就是擁有完全相同的網絡模型。如果網絡模型都不同,那不就亂套了,誰都不認識誰。數據只能逐層傳輸,不能躍層。每一層可以使用下層提供的服務,並向上層提供服務。到目前為止,大致的了解了應用程式和tcpip協議的大致關係,我們只是知道socket編程是在tcp/IP上的網絡編程,但是socket在上述的模型的什麼位置呢。這個位置被一個天才的理論家或者是抽象的計算機大神提出並且安排出來.
  • Python視頻教程網課編程零基礎入門數據分析網絡爬蟲全套Python...
    Python視頻教程網課編程零基礎入門數據分析網絡爬蟲全套Python視頻教程 2020年07月08日 11:00作者:黃頁編輯:黃頁