1.socket编程:socket编程,网络字节序,函数介绍,IP地址转换函数,sockaddr数据结构,网络套接字函数,socket相关函数,TCP server和client
1? Socket編程
socket這個詞可以表示很多概念:
在TCP/IP協(xié)議中,“IP地址+TCP或UDP端口號”唯一標識網(wǎng)絡(luò)通訊中的一個進程,“IP
地址+端口號”就稱為socket。
在TCP協(xié)議中,建立連接的兩個進程各自有一個socket來標識,那么這兩個socket組成的socket pair就唯一標識一個連接。socket本身有“插座”的意思,因此用來描述網(wǎng)絡(luò)連
接的一對一關(guān)系。
TCP/IP協(xié)議最早在BSD UNIX上實現(xiàn),為TCP/IP協(xié)議設(shè)計的應(yīng)用層編程接口稱為socket
API。
本章的主要內(nèi)容是socketAPI,主要介紹TCP協(xié)議的函數(shù)接口,最后介紹UDP協(xié)議和UNIX Domain Socket的函數(shù)接口。
圖11.1:socketAPI
2 網(wǎng)絡(luò)字節(jié)序
我們已經(jīng)知道,內(nèi)存中的多字節(jié)數(shù)據(jù)相對于內(nèi)存地址有大端和小端之分,磁盤文件中的
多字節(jié)數(shù)據(jù)相對于文件中的偏移地址也有大端小端之分。網(wǎng)絡(luò)數(shù)據(jù)流同樣有大端小端之分,
那么如何定義網(wǎng)絡(luò)數(shù)據(jù)流的地址呢?發(fā)送主機通常將發(fā)送緩沖區(qū)中的數(shù)據(jù)按內(nèi)存地址從低到高的順序發(fā)出,接收主機把從網(wǎng)絡(luò)上接到的字節(jié)依次保存在接收緩沖區(qū)中,也是按內(nèi)存地址從低到高的順序保存,因此,網(wǎng)絡(luò)數(shù)據(jù)流的地址應(yīng)這樣規(guī)定:先發(fā)出的數(shù)據(jù)是低地址,后發(fā)出的數(shù)據(jù)是高地址。
??? TCP/IP協(xié)議規(guī)定,網(wǎng)絡(luò)數(shù)據(jù)流應(yīng)采用大端字節(jié)序,即低地址高字節(jié)。例如上一節(jié)的UDP
段格式,地址0-1是16位的源端口號,如果這個端口號是1000(0x3e8),則地址0是0x03,
地址1是0xe8,也就是先發(fā)0x03,再發(fā)0xe8,這16位在發(fā)送主機的緩沖區(qū)中也應(yīng)該是低地址存0x03,高地址存0xe8。但是,如果發(fā)送主機是小端字節(jié)序的,這16位被解釋成0xe803,而不是1000。因此,發(fā)送主機把1000填到發(fā)送緩沖區(qū)之前需要做字節(jié)序的轉(zhuǎn)換。同樣地,接收主機如果是小端字節(jié)序的,接到16位的源端口號也要做字節(jié)序的轉(zhuǎn)換。如果主機是大端字節(jié)序的,發(fā)送和接收都不需要做轉(zhuǎn)換。同理,32位的IP地址也要考慮網(wǎng)絡(luò)字節(jié)序和主機字節(jié)序的問題。
?
為使網(wǎng)絡(luò)程序具有可移植性,使同樣的C代碼在大端和小端計算機上編譯后都能正常運行,可以調(diào)用以下庫函數(shù)做網(wǎng)絡(luò)字節(jié)序和主機字節(jié)序的轉(zhuǎn)換。
3 函數(shù)介紹
A 依賴的頭文件
#include <arpa/inet.h>
B 函數(shù)聲明
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
h表示host,n表示network,l表示32位長整數(shù),s表示16位短整數(shù)。
如果主機是小端字節(jié)序,這些函數(shù)將參數(shù)做相應(yīng)的大小端轉(zhuǎn)換然后返回,如果主機是大端字節(jié)序,這些函數(shù)不做轉(zhuǎn)換,將參數(shù)原封不動地返回。
?
uint32_t htonl(uint32_t hostlong);
| 名稱: | htonl |
| 功能: | The htonl() function converts the unsigned integer hostlong? from? host byte order to network byte order |
| 頭文件: | #include <arpa/inet.h> |
| 函數(shù)原形: | uint32_t htonl(uint32_t hostlong); |
| 參數(shù): | ? |
| 返回值: | ? |
?
uint16_t htons(uint16_t hostshort);
| 名稱: | htons |
| 功能: | The htons() function converts the unsigned short integer hostshort from host byte order to network byte order. |
| 頭文件: | #include <arpa/inet.h> |
| 函數(shù)原形: | uint16_t htons(uint16_t hostshort); |
| 參數(shù): | ? |
| 返回值: | ? |
?
uint32_t ntohl(uint32_t netlong);
| 名稱: | ntohl |
| 功能: | The ntohl() function converts the unsigned integer netlong from network byte order to host byte order. |
| 頭文件: | #include <arpa/inet.h> |
| 函數(shù)原形: | uint32_t ntohl(uint32_t netlong); |
| 參數(shù): | ? |
| 返回值: | ? |
?
uint16_t ntohs(uint16_t netshort);
| 名稱: | ntohs |
| 功能: | The ntohs() function converts the unsigned short integer netshort from network byte order to host byte order. |
| 頭文件: | #include <arpa/inet.h> |
| 函數(shù)原形: | uint16_t ntohs(uint16_t netshort); |
| 參數(shù): | ? |
| 返回值: | ? |
?
4 IP地址轉(zhuǎn)換函數(shù)
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int inet_aton(const char *cp, structin_addr *inp);
in_addr_t inet_addr(const char *cp);
char *inet_ntoa(struct in_addr in);
只能處理IPv4的ip地址
不可重入函數(shù)
注意參數(shù)是struct in_addr
?
現(xiàn)在
#include <arpa/inet.h>
int inet_pton(int af, const char *src, void*dst);
const char *inet_ntop(int af, const void*src, char *dst, socklen_t size);
支持IPv4和IPv6
可重入函數(shù)
?
其中inet_pton和inet_ntop不僅可以轉(zhuǎn)換IPv4的in_addr,還可以轉(zhuǎn)換IPv6的in6_addr,因此函數(shù)接口是void*addrptr
?
5 sockaddr數(shù)據(jù)結(jié)構(gòu)
??? strcutsockaddr 很多網(wǎng)絡(luò)編程函數(shù)誕生早于IPv4協(xié)議,那時候都使用的是sockaddr結(jié)
構(gòu)體,為了向前兼容,現(xiàn)在sockaddr退化成了(void *)的作用,傳遞一個地址給函數(shù),至
于這個函數(shù)是sockaddr_in還是sockaddr_in6,由地址族確定,然后函數(shù)內(nèi)部再強制類型轉(zhuǎn)
化為所需的地址類型
圖 11.2:sockaddr數(shù)據(jù)結(jié)構(gòu)
struct sockaddr {
sa_family_t sa_family; /* address family, AF_xxx */
char sa_data[14]; /* 14 bytes of protocol address */
};
?
struct sockaddr_in {
__kernel_sa_family_t sin_family; /* Address family */
__be16 sin_port; /* Port number */
struct in_addr sin_addr; /* Internet address */
/* Pad to size of `struct sockaddr'. */
unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
sizeof(unsigned short int) - sizeof(struct in_addr)];
};
?
/* Internet address. */
struct in_addr {
__be32 s_addr;
};
?
struct sockaddr_in6 {
unsigned short int sin6_family; /* AF_INET6*/
__be16 sin6_port; /* Transport layer port # */
__be32 sin6_flowinfo; /* IPv6 flow information */
struct in6_addr sin6_addr; /* IPv6 address */
__u32 sin6_scope_id; /* scope id (new in RFC2553) */
};
?
struct in6_addr {
union {
__u8 u6_addr8[16];
__be16 u6_addr16[8];
__be32 u6_addr32[4];
} in6_u;
#define s6_addr in6_u.u6_addr8
#define s6_addr16 in6_u.u6_addr16
#define s6_addr32 in6_u.u6_addr32
};
?
#define UNIX_PATH_MAX 108
struct sockaddr_un {
__kernel_sa_family_t sun_family; /* AF_UNIX */
char sun_path[UNIX_PATH_MAX]; /* pathname */
};
?
Pv4和IPv6的地址格式定義在netinet/in.h中,IPv4地址用sockaddr_in結(jié)構(gòu)體表示,包
括16位端口號和32位IP地址,IPv6地址用sockaddr_in6結(jié)構(gòu)體表示,包括16位端口號、128位IP地址和一些控制字段。UNIX Domain Socket的地址格式定義在sys/un.h中,用sockaddr_un結(jié)構(gòu)體表示。各種socket地址結(jié)構(gòu)體的開頭都是相同的,前16位表示整個結(jié)構(gòu)體的長度(并不是所有UNIX的實現(xiàn)都有長度字段,如Linux就沒有),后16位表示地址類型。IPv4、IPv6和Unix Domain Socket的地址類型分別定義為常數(shù)AF_INET、AF_INET6、AF_UNIX。這樣,只要取得某種sockaddr結(jié)構(gòu)體的首地址,不需要知道具體是哪種類型的sockaddr結(jié)構(gòu)體,就可以根據(jù)地址類型字段確定結(jié)構(gòu)體中的內(nèi)容。因此,socket API可以接受各種類型的sockaddr結(jié)構(gòu)體指針做參數(shù),例如bind、accept、connect等函數(shù),這些函數(shù)的參數(shù)應(yīng)該設(shè)計成void *類型以便接受各種類型的指針,但是sock API的實現(xiàn)早于ANSI C標準化,那時還沒有void *類型,因此這些函數(shù)的參數(shù)都用struct sockaddr *類型表示,在傳遞參數(shù)之前要強制類型轉(zhuǎn)換一下,例如:
struct sockaddr_in servaddr;
/* initialize servaddr */
bind(listen_fd,(struct sockaddr*)&servaddr,sizeof(servaddr));
?
6 網(wǎng)絡(luò)套接字函數(shù)
A socket
#include <sys/types.h>
#include <sys/socket.h>
?
int socket(int domain,int types,intprotocol);
?
domain:
AF_INET 這是大多數(shù)用來產(chǎn)生socket的協(xié)議,使用TCP或UDP來傳輸,用IPv4的地址
AF_INET6 與上面類似,不過是來用IPv6的地址
AF_UNIX 本地協(xié)議,使用在Unix和Linux系統(tǒng)上,一般都是當(dāng)客戶端和服務(wù)器在同一臺及其上的時候使用
?
type:
SOCK_STREAM 這個協(xié)議是按照順序的、可靠的、數(shù)據(jù)完整的基于字節(jié)流的連接。這是一個使用最多的socket類型,這個socket是使用TCP來進行傳輸。
SOCK_DGRAM 這個協(xié)議是無連接的、固定長度的傳輸調(diào)用。該協(xié)議是不可靠的,使用UDP來進行它的連接。
SOCK_SEQPACKET 這個協(xié)議是雙線路的、可靠的連接,發(fā)送固定長度的數(shù)據(jù)包進行傳輸。必須把這個包完整的接受才能進行讀取。
SOCK_RAW 這個socket類型提供單一的網(wǎng)絡(luò)訪問,這個socket類型使用ICMP公共協(xié)議。(ping、traceroute使用該協(xié)議)
SOCK_RDM 這個類型是很少使用的,在大部分的操作系統(tǒng)上沒有實現(xiàn),它是提供給數(shù)據(jù)鏈路層使用,不保證數(shù)據(jù)包的順序
?
protocol:
0 默認協(xié)議
返回值:
成功返回一個新的文件描述符,失敗返回-1,設(shè)置errno
?
socket()打開一個網(wǎng)絡(luò)通訊端口,如果成功的話,就像open()一樣返回一個文件描述符,應(yīng)用程序可以像讀寫文件一樣用read/write在網(wǎng)絡(luò)上收發(fā)數(shù)據(jù),如果socket()調(diào)用出錯則返回-1。對于IPv4,domain參數(shù)指定為AF_INET。對于TCP協(xié)議,type參數(shù)指定為SOCK_STREAM,表示面向流的傳輸協(xié)議。如果是UDP協(xié)議,則type參數(shù)指定為SOCK_DGRAM,表示面向數(shù)據(jù)報的傳輸協(xié)議。protocol參數(shù)的介紹從略,指定為0即可。
?
7 bind
A 依賴的頭文件
#include <sys/types.h> /* See NOTES*/
#include <sys/socket.h>
B 函數(shù)聲明
int bind(int sockfd, const struct sockaddr*addr, socklen_t addrlen);
sockfd:
???socket文件描述符
addr:
構(gòu)造出IP地址加端口號
addrlen:
???sizeof(addr)長度
返回值:
??? 成功返回0,失敗返回-1,設(shè)置errno
?
服務(wù)器程序所監(jiān)聽的網(wǎng)絡(luò)地址和端口號通常是固定不變的,客戶端程序得知服務(wù)器程序
的地址和端口號后就可以向服務(wù)器發(fā)起連接,因此服務(wù)器需要調(diào)用bind綁定一個固定的網(wǎng)絡(luò)地址和端口號。
??? bind()的作用是將參數(shù)sockfd和addr綁定在一起,使sockfd這個用于網(wǎng)絡(luò)通訊的文件
描述符監(jiān)聽addr所描述的地址和端口號。前面講過,struct sockaddr *是一個通用指針類
型,addr參數(shù)實際上可以接受多種協(xié)議的sockaddr結(jié)構(gòu)體,而它們的長度各不相同,所以需要第三個參數(shù)addrlen指定結(jié)構(gòu)體的長度。如:
?
struct sockaddr_in servaddr;
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr =htonl(INADDR_ANY);
servaddr.sin_port = htons(8000);
首先將整個結(jié)構(gòu)體清零,然后設(shè)置地址類型為AF_INET,網(wǎng)絡(luò)地址為INADDR_ANY,這個宏表示本地的任意IP地址,因為服務(wù)器可能有多個網(wǎng)卡,每個網(wǎng)卡也可能綁定多個IP地址,這樣設(shè)置可以在所有的IP地址上監(jiān)聽,直到與某個客戶端建立了連接時才確定下來到底用哪個IP地址,端口號為8000。
?
8 listen
#include <sys/types.h> /* See NOTES*/
#include <sys/socket.h>
?
int listen(int sockfd, int backlog);
sockfd:
socket文件描述符
backlog:
排隊建立3次握手隊列和剛剛建立3次握手隊列的鏈接數(shù)和
?
查看系統(tǒng)默認backlog
cat /proc/sys/net/ipv4/tcp_max_syn_backlog
典型的服務(wù)器程序可以同時服務(wù)于多個客戶端,當(dāng)有客戶端發(fā)起連接時,服務(wù)器調(diào)用的
accept()返回并接受這個連接,如果有大量的客戶端發(fā)起連接而服務(wù)器來不及處理,尚未
accept的客戶端就處于連接等待狀態(tài),listen()聲明sockfd處于監(jiān)聽狀態(tài),并且最多允許有
backlog個客戶端處于連接待狀態(tài),如果接收到更多的連接請求就忽略。listen()成功返回
0,失敗返回-1。
?
9 ?accept
#include <sys/types.h> /* See NOTES*/
#include <sys/socket.h>
int accept(int sockfd, struct sockaddr*addr, socklen_t *addrlen);
sockdf:
socket文件描述符
addr:
傳出參數(shù),返回鏈接客戶端地址信息,含IP地址和端口號
addrlen:
傳入傳出參數(shù)(值-結(jié)果),傳入sizeof(addr)大小,函數(shù)返回時返回真正接收到地址結(jié)構(gòu)體的大小
返回值:
成功返回一個新的socket文件描述符,用于和客戶端通信,失敗返回-1,設(shè)置errno
?
三方握手完成后,服務(wù)器調(diào)用accept()接受連接,如果服務(wù)器調(diào)用accept()時還沒有
客戶端的連接請求,就阻塞等待直到有客戶端連接上來。addr是一個傳出參數(shù),accept()
返回時傳出客戶端的地址和端口號。addrlen參數(shù)是一個傳入傳出參數(shù)(value-result argument),傳入的是調(diào)用者提供的緩沖區(qū)addr的長度以避免緩沖區(qū)溢出問題,傳出的是客
戶端地址結(jié)構(gòu)體的實際長度(有可能沒有占滿調(diào)用者提供的緩沖區(qū))。如果給addr參數(shù)傳
NULL,表示不關(guān)心客戶端的地址。
?
我們的服務(wù)器程序結(jié)構(gòu)是這樣的:
while (1) {
cliaddr_len = sizeof(cliaddr);
connfd = accept(listenfd, (struct sockaddr *)&cliaddr,&cliaddr_len);
n = read(connfd, buf, MAXLINE);
......
close(connfd);
}
?
整個是一個while死循環(huán),每次循環(huán)處理一個客戶端連接。由于cliaddr_len是傳入傳出
參數(shù),每次調(diào)用accept()之前應(yīng)該重新賦初值。accept()的參數(shù)listenfd是先前的監(jiān)聽文件
描述符,而accept()的返回值是另外一個文件描述符connfd,之后與客戶端之間就通過這個
connfd通訊,最后關(guān)閉connfd斷開連接,而不關(guān)閉listenfd,再次回到循環(huán)開頭listenfd仍
然用作accept的參數(shù)。accept()成功返回一個文件描述符,出錯返回-1。
?
10 connect
#include <sys/types.h> /* See NOTES*/
#include <sys/socket.h>
int connect(int sockfd, const structsockaddr *addr, socklen_t addrlen);
sockdf:
socket文件描述符
addr:
傳入?yún)?shù),指定服務(wù)器端地址信息,含IP地址和端口號
addrlen:
傳入?yún)?shù),傳入sizeof(addr)大小
返回值:
成功返回0,失敗返回-1,設(shè)置errno
?
客戶端需要調(diào)用connect()連接服務(wù)器,connect和bind的參數(shù)形式一致,區(qū)別在于bind的參數(shù)是自己的地址,而connect的參數(shù)是對方的地址。connect()成功返回0,出錯返回-1。
?
11 C/S模型-TCP
??? 下圖是基于TCP協(xié)議的客戶端/服務(wù)器程序的一般流程:
圖11.3: TCP協(xié)議通訊流程
服務(wù)器調(diào)用socket()、bind()、listen()完成初始化后,調(diào)用accept()阻塞等待,處于
監(jiān)聽端口的狀態(tài),客戶端調(diào)用socket()初始化后,調(diào)用connect()發(fā)出SYN段并阻塞等待服
務(wù)器應(yīng)答,服務(wù)器應(yīng)答一個SYN-ACK段,客戶端收到后從connect()返回,同時應(yīng)答一個ACK
段,服務(wù)器收到后從accept()返回。
數(shù)據(jù)傳輸?shù)倪^程:
建立連接后,TCP協(xié)議提供全雙工的通信服務(wù),但是一般的客戶端/服務(wù)器程序的流程是由客戶端主動發(fā)起請求,服務(wù)器被動處理請求,一問一答的方式。因此,服務(wù)器從accept()
返回后立刻調(diào)用read(),讀socket就像讀管道一樣,如果沒有數(shù)據(jù)到達就阻塞等待,這時客
戶端調(diào)用write()發(fā)送請求給服務(wù)器,服務(wù)器收到后從read()返回,對客戶端的請求進行處
理,在此期間客戶端調(diào)用read()阻塞等待服務(wù)器的應(yīng)答,服務(wù)器調(diào)用write()將處理結(jié)果發(fā)
回給客戶端,再次調(diào)用read()阻塞等待下一條請求,客戶端收到后從read()返回,發(fā)送下一
條請求,如此循環(huán)下去。
如果客戶端沒有更多的請求了,就調(diào)用close()關(guān)閉連接,就像寫端關(guān)閉的管道一樣,
服務(wù)器的read()返回0,這樣服務(wù)器就知道客戶端關(guān)閉了連接,也調(diào)用close()關(guān)閉連接。注
意,任何一方調(diào)用close()后,連接的兩個傳輸方向都關(guān)閉,不能再發(fā)送數(shù)據(jù)了。如果一方
調(diào)用shutdown()則連接處于半關(guān)閉狀態(tài),仍可接收對方發(fā)來的數(shù)據(jù)。
在學(xué)習(xí)socket API時要注意應(yīng)用程序和TCP協(xié)議層是如何交互的: *應(yīng)用程序調(diào)用某個
socket函數(shù)時TCP協(xié)議層完成什么動作,比如調(diào)用connect()會發(fā)出SYN段*應(yīng)用程序如何知
道TCP協(xié)議層的狀態(tài)變化,比如從某個阻塞的socket函數(shù)返回就表明TCP協(xié)議收到了某些段,再比如read()返回0就表明收到了FIN段
12 TCP服務(wù)器客戶端案例說明
server.c
| #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <stdio.h> #include <string.h> #include <netinet/in.h> #include <ctype.h> #include <unistd.h> ? #define SERVER_PORT 8000 #define MAXLINE 4096 ? int main(void) { ??? struct sockaddr_in serveraddr,clientaddr; ??? int sockfd,addrlen,confd,len,i; ??? //存儲ip地址 ??? char ipstr[128]; ??? char buf[MAXLINE]; ??? ??? //1.socket ??? //AF_INET:表示使用的是Ipv4協(xié)議,如果想使用ipv6,使用AF_INET6 ??? //SOCK_STREAM:表示使用的是TCP協(xié)議 ??? sockfd = socket(AF_INET,SOCK_STREAM,0); ??? //2.bind,bzero將內(nèi)容清零 ??? bzero(&serveraddr,sizeof(serveraddr)); ??? /* 地址族協(xié)議IPv4 */ ??? serveraddr.sin_family = AF_INET; ??? /* IP地址 */ ??? serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); ??? /*端口號*/ ??? serveraddr.sin_port = htons(SERVER_PORT); ??? bind(sockfd,(struct sockaddr *)&serveraddr,sizeof(serveraddr)); ??? //3.listen,表示最大等待排隊的數(shù)量為128個 ??? listen(sockfd,128); ??? while(1) { ??????? //4.accept 阻塞監(jiān)聽客戶端鏈接請求 ??????? addrlen = sizeof(clientaddr); ??????? confd = accept(sockfd,(struct sockaddr *)&clientaddr,&addrlen); ??????? //輸出客戶端IP地址和端口號 ??????? inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,ipstr,sizeof(ipstr)); ??????? //打印除ip地址和端口號 ??????? printf("client ip %s\tport %d\n", ??????????? inet_ntop(AF_INET,&clientaddr.sin_addr.s_addr,ipstr,sizeof(ipstr)), ??????????? ntohs(clientaddr.sin_port)); ? ??????? //和客戶端交互數(shù)據(jù)操作confd ??????? //5.處理客戶端請求,read,write默認是阻塞的。 ??????? len = read(confd,buf,sizeof(buf)); ??????? i = 0; ??????? while(i < len) { ??????????? buf[i] = toupper(buf[i]); ?????? ?????i++; ??????? } ??????? write(confd,buf,len); ??????? //發(fā)生里4次鏈接 ??????? close(confd); ??? } ??? close(sockfd); ? ??? return 0; } |
client.c
| #include <netinet/in.h> #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <arpa/inet.h> #include <string.h> #include <stdlib.h> #include <sys/stat.h> #include <unistd.h> #include <fcntl.h> ? #define SERVER_PORT 8000 #define MAXLINE 4096 ? int main(int argc,char *argv[]) { ??? struct sockaddr_in serveraddr; ??? int confd,len; ??? char ipstr[] = "192.168.6.14"; ??? char buf[MAXLINE]; ??? if(argc < 2) { ??????? printf("./client str\n"); ??????? exit(1); ??? } ??? //1、創(chuàng)建一個socket ??? confd = socket(AF_INET,SOCK_STREAM,0); ??? //2、初始化服務(wù)器地址 ??? bzero(&serveraddr,sizeof(serveraddr)); ??? serveraddr.sin_family = AF_INET; ??? //"192.168.6.14" ??? inet_pton(AF_INET,ipstr,&serveraddr.sin_addr.s_addr); ??? serveraddr.sin_port = htons(SERVER_PORT); ??? //3.鏈接服務(wù)器處理數(shù)據(jù) ??? connect(confd,(struct sockaddr *)&serveraddr,sizeof(serveraddr)); ??? //4.請求服務(wù)器處理數(shù)據(jù) ??? write(confd,argv[1],strlen(argv[1])); ??? len = read(confd,buf,sizeof(buf)); ??? write(STDOUT_FILENO,buf,len); ? ??? //關(guān)閉socket ??? close(confd); ??? return 0; } |
Makefile
| all:server client ? server:server.c ???????? gcc $< -o $@ client:client.c ???????? gcc $< -o $@ ? .PHONY:clean clean: ???????? rm -f server ???????? rm -f client |
運行:
在終端上輸入make,先啟動server端,在啟動client,在啟動客戶端的時候同時輸入字符串。
客戶端運行效果:
總結(jié)
以上是生活随笔為你收集整理的1.socket编程:socket编程,网络字节序,函数介绍,IP地址转换函数,sockaddr数据结构,网络套接字函数,socket相关函数,TCP server和client的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怎么给pe设置开机密码 设置Pe开机密码
- 下一篇: u盘上的密码怎么取消密码保护 取消U盘密