前2篇文章,我寫(xiě)了一些網(wǎng)絡(luò)編程的基礎(chǔ)以及一些網(wǎng)絡(luò)編程需要掌握的基礎(chǔ)。
socket編程
TCP協(xié)議的流程圖
收到確認(rèn)消息后才會(huì)繼續(xù)發(fā)送消息,否則繼續(xù)等待。這樣的好處是傳輸?shù)臄?shù)據(jù)是可靠的,此外它是有連接的傳輸,大多數(shù)網(wǎng)絡(luò)傳輸都是用的TCP。
TCP協(xié)議
(1)面向連接的可靠的傳輸控制協(xié)議,連接的建立需要三次握手,連接的 ? ? 釋放需要進(jìn)行四次握手才能保證連接的建立,數(shù)據(jù)的同步傳輸。
(2)面向字節(jié)流,會(huì)把從上層傳輸下來(lái)的數(shù)據(jù)當(dāng)作是無(wú)結(jié)構(gòu)的字節(jié)流。
(3)一對(duì)一的通信。
(4)TCP在IP協(xié)議的基礎(chǔ)之上添加了序號(hào)機(jī)制,確認(rèn)機(jī)制,超時(shí)重傳機(jī)制,數(shù)據(jù)校驗(yàn),從而保證傳輸?shù)目煽啃?,同時(shí)保證不出現(xiàn)丟失或者是亂序。
TCP通信的基本步驟如下:
服務(wù)端:socket---bind---listen---while(1){---accept---recv---send---close---}---close
客戶(hù)端:socket---------connect---send---recv-----------------close
服務(wù)器端:
1. 創(chuàng)建socket
1sock_fd?=?socket(AF_INET,?SOCK_STREAM,0);//AF_INET:IPV4;SOCK_STREAM:TCP
2if(-1==?sock_fd)
3{
4fprintf(stderr,"socket?error:%s\n\a",?strerror(errno));
5exit(1);
6}
所需要頭文件:
#include <sys/types.h>
#include <sys/socket.h>
函數(shù)格式:
int socket(int domain, int type, int protocol);
函數(shù)功能:
創(chuàng)建一個(gè)套接字;
domain:協(xié)議域(族),決定了套接字的地址類(lèi)型,例如AF_INET決定了要用IPv4地址(32位)與端口號(hào)(16位)的組合。常見(jiàn)的協(xié)議族有:AF_INET、AF_INET6、AF_LOCAL(或稱(chēng)AF_UNIX)、AF_ROUTE等;
type:指定套接字類(lèi)型,SOCK_STREAM(TCP)、SOCK_DGRAM(UDP)、SOCK_RAW
protocol:指定socket所使用的傳輸協(xié)議編號(hào),通常為0
返回值:
若成功,返回一個(gè)套接字描述符,否則返回-1;
Socket就是一種文件描述符,和普通的打開(kāi)文件一樣,需要檢測(cè)其返回結(jié)果。
2. 設(shè)置socket
1memset(&server_addr,0,sizeof(struct?sockaddr_in));//clear
2server_addr.sin_family?=?AF_INET;
3server_addr.sin_addr.s_addr?=?htonl(INADDR_ANY);//INADDR_ANY:This?machine?all?IP
4server_addr.sin_port?=?htons(PORT_NUMBER);
設(shè)置何種協(xié)議族,設(shè)置本機(jī)IP和端口,也就有了唯一性。
3. 綁定socket
1ret?=?bind(sock_fd,?(struct?sockaddr?*)(&server_addr),sizeof(struct?sockaddr));
2if(-1==?ret)
3{
4fprintf(stderr,"bind?error:%s\n\a",?strerror(errno));
5close(sock_fd);
6exit(1);
7}
所需要頭文件:
#include <sys/types.h>
#include <sys/socket.h>
函數(shù)格式:
int bind(int sockfd, struct sockaddr *addr, int addrlen);
函數(shù)功能:
把套接字綁定到本地計(jì)算機(jī)的某一個(gè)端口上;
sockfd:待綁定的套接字描述符
addr:一個(gè)struct sockaddr *指針,指定要綁定給sockfd的協(xié)議地址。內(nèi)容結(jié)構(gòu)由前面的協(xié)議族決定。
addrlen:地址的長(zhǎng)度
返回值:
若成功,返回0,否則返回-1,錯(cuò)誤信息存在errno中;
4. 開(kāi)始監(jiān)聽(tīng)
1ret?=listen(sock_fd,?BACKLOG);
2if(-1==?ret)
3{
4fprintf(stderr,"listen?error:%s\n\a",?strerror(errno));
5close(sock_fd);
6exit(1);
7}
所需要頭文件:
#include <sys/types.h>
#include <sys/socket.h>
函數(shù)格式:
int listen(int sockfd, int backlog);
函數(shù)功能:
使服務(wù)器的這個(gè)端口和IP處于監(jiān)聽(tīng)狀態(tài),等待網(wǎng)絡(luò)中某一客戶(hù)機(jī)的連接請(qǐng)求,最大連接數(shù)量為backlog≤128;
sockfd:待監(jiān)聽(tīng)的套接字描述符
backlog:最大可監(jiān)聽(tīng)和連接的客戶(hù)端數(shù)量
返回值:
若成功,返回0,否則返回-1;
5. 阻塞,等待連接
1addr_len?=sizeof(struct?sockaddr);
2new_fd?=?accept(sock_fd,?(struct?sockaddr?*)&client_addr,?&addr_len);
3if(-1==?new_fd)
4{
5fprintf(stderr,"accept?error:%s\n\a",?strerror(errno));
6close(sock_fd);
7exit(1);
8}
1
所需要頭文件:
#include <sys/types.h>
#include <sys/socket.h>
函數(shù)格式:
int accept(int sockfd, struct sockaddr *addr, int *addrlen);
函數(shù)功能:
接受連接請(qǐng)求,建立起與客戶(hù)機(jī)之間的通信連接。服務(wù)器處于監(jiān)聽(tīng)狀態(tài)時(shí),如果某時(shí)刻獲得客戶(hù)機(jī)的連接請(qǐng)求,此時(shí)并不是立即處理這個(gè)請(qǐng)求,而是將這個(gè)請(qǐng)求放在等待隊(duì)列中,當(dāng)系統(tǒng)空閑時(shí)再處理客戶(hù)機(jī)的連接請(qǐng)求;
當(dāng)accept函數(shù)接受一個(gè)連接時(shí),會(huì)返回一個(gè)新的socket標(biāo)識(shí)符,以后的數(shù)據(jù)傳輸和讀取就要通過(guò)這個(gè)新的socket編號(hào)來(lái)處理,原來(lái)參數(shù)中的socket也可以繼續(xù)使用,繼續(xù)監(jiān)聽(tīng)其它客戶(hù)機(jī)的連接請(qǐng)求;
accept連接成功時(shí),參數(shù)addr所指的結(jié)構(gòu)體會(huì)填入所連接機(jī)器的地址數(shù)據(jù);
sockfd:待監(jiān)聽(tīng)的套接字描述符
addr:指向struct sockaddr的指針,用于返回客戶(hù)端的協(xié)議地址
addrlen:協(xié)議地址的長(zhǎng)度
返回值:
若成功,返回一個(gè)由內(nèi)核自動(dòng)生成的一個(gè)全新描述字,代表與返回客戶(hù)的TCP連接,否則返回-1,錯(cuò)誤信息存在errno中;
6. 接收數(shù)據(jù)
1recv_len?=recv(new_fd,?recv_buf,999,0);
2if(recv_len?<=0)
3{
4fprintf(stderr,"recv?error:%s\n\a", strerror(errno));
5close(new_fd);
6exit(1);
7}
8else
9{
10recv_buf[recv_len]?='\0';
11printf("Get?msg?from?client%d:?%s\n",?client_num,?recv_buf);
12}
所需要頭文件:
#include <sys/types.h>
#include <sys/socket.h>
函數(shù)格式:
int recv(int sockfd, void *buf, size_t len, int flags);
函數(shù)功能:
用新的套接字來(lái)接收遠(yuǎn)端主機(jī)傳來(lái)的數(shù)據(jù),并把數(shù)據(jù)存到由參數(shù)buf指向的內(nèi)存空間;
sockfd:sockfd為前面accept的返回值,即new_fd,也就是新的套接字
buf:指明一個(gè)緩沖區(qū)
len:指明緩沖區(qū)的長(zhǎng)度
flags:通常為0
返回值:
若成功,返回接收到的字節(jié)數(shù),另一端已關(guān)閉則返回0,否則返回-1,錯(cuò)誤信息存在errno中;
7. 關(guān)閉socket
1close(sock_fd);
2exit(0);
為了應(yīng)對(duì)多個(gè)連接,并保證它們之間相互獨(dú)立,實(shí)際編程中往往還要加入多進(jìn)程fork()。
讓子進(jìn)程接收數(shù)據(jù),父進(jìn)程繼續(xù)監(jiān)聽(tīng)新的連接。
客戶(hù)機(jī)端:
1. 創(chuàng)建socket
1sock_fd?=?socket(AF_INET,?SOCK_STREAM,0);//AF_INET:IPV4;SOCK_STREAM:TCP
2if(-1==?sock_fd)
3{
4fprintf(stderr,"socket?error:%s\n\a",?strerror(errno));
5exit(1);
6}
2. 設(shè)置socket
1memset(&server_addr,0,sizeof(struct?sockaddr_in));//clear
2server_addr.sin_family?=?AF_INET;
3server_addr.sin_port?=?htons(PORT_NUMBER);
其中注意的是,這里設(shè)置的socket內(nèi)容是指 希望連接的服務(wù)器IP和端口號(hào)信息,IP地址來(lái)自用戶(hù)的輸入,并轉(zhuǎn)換格式得到。因此,這里的設(shè)置和服務(wù)器的設(shè)置,要保持內(nèi)容上的一致。
1ret?=?inet_aton(argv[1],?&server_addr.sin_addr);
2if(0==?ret)
3{
4fprintf(stderr,"server_ip?error.\n");
5close(sock_fd);
6exit(1);
7}
3. 連接
1ret?=?connect(sock_fd,?(conststruct?sockaddr?*)&server_addr,sizeof(struct?sockaddr));
2if(-1==?ret)
3{
4fprintf(stderr,"connect?error:%s\n\a",?strerror(errno));
5close(sock_fd);
6exit(1);
7}
所需要頭文件:
#include <sys/types.h>
#include <sys/socket.h>
函數(shù)格式:
int connect (int sockfd, struct sockaddr *serv_addr, int addrlen);
函數(shù)功能:
用來(lái)請(qǐng)求連接遠(yuǎn)程服務(wù)器,將參數(shù)sockfd的socket連至參數(shù)serv_addr所指定的服務(wù)器IP和端口號(hào)上去;
sockfd:客戶(hù)端的socket套接字
serv_addr:一個(gè)struct sockaddr類(lèi)型的結(jié)構(gòu)體指針變量,存儲(chǔ)著遠(yuǎn)程服務(wù)器的IP與端口號(hào)信息
addrlen:結(jié)構(gòu)體變量的長(zhǎng)度
返回值:
若成功,返回0,否則返回-1,錯(cuò)誤信息存在errno中;
4. 發(fā)送
1send_buf?=?send(sock_fd,?send_buf,strlen(send_buf),0);
2if(send_buf?<=0)
3{
4fprintf(stderr,"send?error:%s\n\a",?strerror(errno));
5close(sock_fd);
6exit(1);
7}
所需要頭文件:
#include <sys/types.h>
#include <sys/socket.h>
函數(shù)格式:
int send(int sockfd, const void *buf, int len, int flags);
函數(shù)功能:
用來(lái)發(fā)送數(shù)據(jù)給指定的遠(yuǎn)端主機(jī);
sockfd:客戶(hù)端的socket套接字
buf:指明一個(gè)緩沖區(qū)
len:指明緩沖區(qū)的長(zhǎng)度
flags:通常為0
返回值:
若成功,返回發(fā)送的字節(jié)數(shù),否則返回-1,錯(cuò)誤信息存在errno中
5. 關(guān)閉socket
1close(sock_fd);
2exit(0);
TCP完整例子:
service:
1#include<stdio.h>
2#include<unistd.h>
3#include<sys/types.h>
4#include<sys/socket.h>
5#include<strings.h>
6#include<string.h>
7#include<ctype.h>
8#include<arpa/inet.h>
9
10#defineSERV_PORT?9527
11
12intmain(void)
13
{
14intsfd,?cfd;
15intlen,?i;
16charbuf[BUFSIZ],?clie_IP[BUFSIZ];
17
18structsockaddr_inserv_addr,clie_addr;
19socklen_tclie_addr_len;
20
21/*創(chuàng)建一個(gè)socket?指定IPv4協(xié)議族?TCP協(xié)議*/
22sfd?=?socket(AF_INET,?SOCK_STREAM,0);
23
24/*初始化一個(gè)地址結(jié)構(gòu)?man?7?ip?查看對(duì)應(yīng)信息*/
25bzero(&serv_addr,sizeof(serv_addr));//將整個(gè)結(jié)構(gòu)體清零
26serv_addr.sin_family?=?AF_INET;//選擇協(xié)議族為IPv4
27serv_addr.sin_addr.s_addr?=?htonl(INADDR_ANY);//監(jiān)聽(tīng)本地所有IP地址
28serv_addr.sin_port?=?htons(SERV_PORT);//綁定端口號(hào)????
29
30/*綁定服務(wù)器地址結(jié)構(gòu)*/
31bind(sfd,?(struct?sockaddr?*)&serv_addr,sizeof(serv_addr));
32
33/*設(shè)定鏈接上限,注意此處不阻塞*/
34listen(sfd,64);//同一時(shí)刻允許向服務(wù)器發(fā)起鏈接請(qǐng)求的數(shù)量
35
36printf("wait?for?client?connect?...\n");
37
38/*獲取客戶(hù)端地址結(jié)構(gòu)大小*/
39clie_addr_len?=sizeof(clie_addr_len);
40/*參數(shù)1是sfd;?參2傳出參數(shù),?參3傳入傳入?yún)?shù),?全部是client端的參數(shù)*/
41cfd?=?accept(sfd,?(struct?sockaddr?*)&clie_addr,?&clie_addr_len);/*監(jiān)聽(tīng)客戶(hù)端鏈接,?會(huì)阻塞*/
42
43printf("client?IP:%s\tport:%d\n",
44inet_ntop(AF_INET,?&clie_addr.sin_addr.s_addr,?clie_IP,sizeof(clie_IP)),
45ntohs(clie_addr.sin_port));
46
47while(1)?{
48/*讀取客戶(hù)端發(fā)送數(shù)據(jù)*/
49len?=?read(cfd,?buf,sizeof(buf));
50write(STDOUT_FILENO,?buf,?len);
51
52/*處理客戶(hù)端數(shù)據(jù)*/
53for(i?=0;?i?<?len;?i++)
54buf[i]?=toupper(buf[i]);
55
56/*處理完數(shù)據(jù)回寫(xiě)給客戶(hù)端*/
57write(cfd,?buf,?len);
58}
59
60/*關(guān)閉鏈接*/
61close(sfd);
62close(cfd);
63
64return0;
65}
client:
1#include<stdio.h>
2#include<unistd.h>
3#include<string.h>
4#include<sys/socket.h>
5#include<arpa/inet.h>
6
7#defineSERV_IP"127.0.0.1"
8#defineSERV_PORT?9527
9
10intmain(void)
11
{
12intsfd,?len;
13structsockaddr_inserv_addr;
14charbuf[BUFSIZ];
15
16/*創(chuàng)建一個(gè)socket?指定IPv4?TCP*/
17sfd?=?socket(AF_INET,?SOCK_STREAM,0);
18
19/*初始化一個(gè)地址結(jié)構(gòu):*/
20bzero(&serv_addr,sizeof(serv_addr));//清零
21serv_addr.sin_family?=?AF_INET;//IPv4協(xié)議族
22inet_pton(AF_INET,?SERV_IP,?&serv_addr.sin_addr.s_addr);//指定IP?字符串類(lèi)型轉(zhuǎn)換為網(wǎng)絡(luò)字節(jié)序?參3:傳出參數(shù)
23serv_addr.sin_port?=?htons(SERV_PORT);//指定端口?本地轉(zhuǎn)網(wǎng)絡(luò)字節(jié)序
24
25/*根據(jù)地址結(jié)構(gòu)鏈接指定服務(wù)器進(jìn)程*/
26connect(sfd,?(struct?sockaddr?*)&serv_addr,sizeof(serv_addr));
27
28while(1)?{
29/*從標(biāo)準(zhǔn)輸入獲取數(shù)據(jù)*/
30fgets(buf,sizeof(buf),stdin);
31/*將數(shù)據(jù)寫(xiě)給服務(wù)器*/
32write(sfd,?buf,strlen(buf));//寫(xiě)個(gè)服務(wù)器
33/*從服務(wù)器讀回轉(zhuǎn)換后數(shù)據(jù)*/
34len?=?read(sfd,?buf,sizeof(buf));
35/*寫(xiě)至標(biāo)準(zhǔn)輸出*/
36write(STDOUT_FILENO,?buf,?len);
37}
38
39/*關(guān)閉鏈接*/
40close(sfd);
41
42return0;
43}
推薦閱讀:
????覺(jué)得有用,點(diǎn)贊,分享就是對(duì)小編最大的支持
▼長(zhǎng)按2秒識(shí)別二維碼關(guān)注公眾號(hào)
歡迎把我推薦給你的朋喲
每天進(jìn)步一點(diǎn)點(diǎn),如果有用給點(diǎn)個(gè)贊