以下內(nèi)容分別為TCP 與 UDP編程,內(nèi)容有相似或者重合部分,可根據(jù)流程 相互對照學習,都已經(jīng)附上源碼。
TCP編程
TCP 服務器端_Server :
**1.**socket 創(chuàng)建 tcp套接字 (監(jiān)聽的套接字)
int iSocketServer = socket(AF_INET,SOCK_STREAM,0);
2、IPv4套接字地址結(jié)構
#include <netinet/in.h>
struct sockaddr_in{
unsigned short sin_family; //2 字節(jié) 協(xié)議AF_INET
unsigned short sin_port; //2字節(jié) 端口
struct in_addr sin_addr; //4字節(jié) IP地址(32位無符號整數(shù))
unsigned char sin_zero[8]; //8字節(jié) 全寫0
}
struct in_addr:
struct in_addr{
in_addr_t s_addr;
}
如果使用 Internet 所以 sin_family 一般為 AF_INET。
? sin_addr 設置為 INADDR_ANY 表示可以和任何的主機通信。
? sin_port 是要監(jiān)聽的端口號。
定義IPv4地址結(jié)構,存放 本機信息
#define Server_port 8888
struct sockaddr_in tSocketServerAddr;
tSocketServerAddr.sin_family = AF_INET;
tSocketServerAddr.sin_port = htons(Server_port);/*host to net,short*/
tSocketServerAddr.sin_addr.s_ad`dr = INADDR_ANY;可以和任何的主機通信
memset(tSocketServerAddr.sin_zero , 0, 8);
3、兩種地址結(jié)構使用場合
struct sockaddr_in 是IPv4地址結(jié)構(存放客戶端、服務器的地址信息(協(xié)議,port,IP))
struct sockaddr 是通用地址結(jié)構,不是存放數(shù)據(jù),只是socket api類型轉(zhuǎn)換
sockaddr定義 見下方4.bind介紹 中
4.bind給服務器的綁定固定的port、IP地址信息**
給iSocketServer套接字bind綁定一個固定的地址信息
bind 函數(shù)
int bind(int sockfd, struct sockaddr *my_addr, int addrlen);
從函數(shù)用于將地址綁定到一個套接字。
? sockfd 是由 socket 函數(shù)調(diào)用返回的文件描述符。
? my_addr 是一個指向 sockaddr 的指針。
? addrlen 是 sockaddr 結(jié)構的長度。
? bind 將本地的端口同 socket 返回的文件描述符捆綁在一起.成功是返回 0,
失敗的情況和 socket 一樣。
***注: ***sockaddr 的定義(通用地址結(jié)構):
struct sockaddr{
unisgned short as_family;
char sa_data[14];
};
iRet= bind(iSocketServer, (const struct sockaddr*)&tSocketServerAddr, sizeof(struct sockaddr));
**注意:**bind的函數(shù)原型的第二個參數(shù),是通用地址結(jié)構struct sockaddr ,而我們定義結(jié)構體地址的時候,一般選擇IPv4結(jié)構體地址 ,見2.ipv4套接字地址結(jié)構 ,例:struct sockaddr_in tSocketServerAddr;
所以當bind()中 ,我們想填入tSocketServerAddr的結(jié)構體地址,需要在前面加入強制類型轉(zhuǎn)換(const struct sockaddr*) 將其轉(zhuǎn)換為通用地址結(jié)構struct sockaddr。
3、兩種地址結(jié)構使用場合
struct sockaddr_in 是IPv4地址結(jié)構(存放客戶端、服務器的地址信息(協(xié)議,port,IP))
struct sockaddr 是通用地址結(jié)構,不是存放數(shù)據(jù),只是socket api類型轉(zhuǎn)換
3.listen監(jiān)聽 并且創(chuàng)建連接隊列
? listen 監(jiān)聽 :等待客戶端的連接到來,經(jīng)過三次握手(底層自動執(zhí)行),將客戶端放入連接隊列
#define BACKLOG 10
iRet = listen(iSocketServer,BACKLOG);
功能:
? 1、將監(jiān)聽套接字由主動變成被動(是在server和client兩端 套接字名字一樣的情況下,我們這里 設定的 server端的套接字名字是iSocketServer,所以已經(jīng)是被動。
? 2、為改套接字創(chuàng)建連接隊列,隊列長度就是BACKLOG
? 返回值:成功為0,失敗為-1
? 連接隊列,分成完成連接 和未完成連接的狀態(tài),分別對應,完成三次握手/第1、 2次握手的狀態(tài)
*4.accep 提取客戶端的鏈接
此內(nèi)容放在while(1)循環(huán)里面,表示持續(xù)等待客戶端連接
iAddrLen=sizeof(struct sockaddr);
iSocketClient= accept(iSocketServer, ( struct sockaddr *)&tSocketClientAddr, &iAddrLen); //強轉(zhuǎn)是因為socket適用于ipv4、ipv6,通用地址結(jié)構強轉(zhuǎn)?
連接成功client的話,打印client的信息
printf("get connect from client %d: %s\n",iClientNum, inet_ntoa(tSocketClientAddr.sin_addr));
iClientNum表示 連接客戶端的個數(shù) ,然后inet_ntoa(tSocketClientAddr.sin_addr)是客戶端的ip地址 (192.168.xx.xx)要打印出來
然后調(diào)用fork()函數(shù)對應多個連接的客戶端開啟多個子進程
5.(send發(fā)送) recv 接收客戶端的消息**
tcp中recv的注意點:
int len =recv(已連接套接字(注意不是監(jiān)聽套接字),buf,sizeof(buf),0);
len 表示recv 收到的實際字節(jié)數(shù)
如果len為0 ,表示客戶端已經(jīng)斷開連接,-1表示 讀取數(shù)據(jù)出錯
tcp中send的注意點(C代碼放在了client里):
int iSendLen = send(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0);
如果返回值iSendLen >0表示發(fā)送成功 ,即實際發(fā)送的字節(jié)數(shù)
如果 為-1 表示發(fā)送失敗
注意:不能發(fā)送0長度報文 ,因為recv收到0長度 會斷開連接 (但是udp允許)
iRecvLen = recv(iSocketClient,ucRecvBuf,999 ,0) ;
{
ucRecvBuf[iRecvLen]='\0'; //收到的消息加結(jié)束符
printf("Get Msg from Client %d:%s\n",iClientNum,ucRecvBuf);//打印從which客戶端發(fā)送的消息
}
6.close關閉所有套接字
源碼閱讀
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
/* socket
* bind
* listen
* accept
* send/recv
*/
#define Server_port 8888
#define BACKLOG 10
int main(int argc,char**argv)
{
int iSocketServer;
int iSocketClient;
struct sockaddr_in tSocketServerAddr;
struct sockaddr_in tSocketClientAddr;
int iRet;
int iAddrLen;
int iRecvLen;
unsigned char ucRecvBuf[1000];
int iClientNum = -1;
signal(SIGCHLD,SIG_IGN);
iSocketServer = socket(AF_INET,SOCK_STREAM,0);
if(-1==iSocketServer)
{
printf("socket error!");
return -1;
}
tSocketServerAddr.sin_family = AF_INET;
tSocketServerAddr.sin_port = htons(Server_port);/*host to net,short*/
tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
memset(tSocketServerAddr.sin_zero , 0, 8);
iRet= bind(iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
if(iRet==-1)
{
printf("bind error!\n");
return -1;
}
iRet = listen(iSocketServer,BACKLOG);
if(iRet==-1)
{
printf("listen error!\n");
return -1;
}
while (1)
{
iAddrLen=sizeof(struct sockaddr);
iSocketClient= accept(iSocketServer, ( struct sockaddr *)&tSocketClientAddr, &iAddrLen);
if(iSocketClient!=-1)
{
iClientNum++;
printf("get connect from client %d: %s\n",iClientNum, inet_ntoa(tSocketClientAddr.sin_addr));
if(!fork())
{//子進程的源碼
while(1)
{//接收客戶端發(fā)來的數(shù)據(jù)并顯示出來
iRecvLen = recv(iSocketClient,ucRecvBuf,999 ,0) ;
if (iRecvLen<=0)
{
close(iSocketClient);
return -1;
}
else
{
ucRecvBuf[iRecvLen]='\0';
printf("Get Msg from Client %d: %s\n",iClientNum,ucRecvBuf);
}
}
}
}
}
close(iSocketServer);
return 0;
}
TCP 客戶端_Client :
1.創(chuàng)建套接字
int iSocketClient=socket(AF_INET, SOCK_STREAM, 0);
2.connect
tSocketServerAddr.sin_family = AF_INET;
tSocketServerAddr.sin_port = htons(SERVER_PORT); /* host to net, short */
//tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
if(inet_aton(argv[1],&tSocketServerAddr.sin_addr)==0)//輸入ip成功
/*
int inet_aton(const char *cp,struct in_addr *inp)
函數(shù)里面 a 代表 ascii,n 代表network. 第一個函數(shù)表示將a.b.c.d的IP轉(zhuǎn)換為32位的IP,存儲在 inp指針里面.
第二個是將32位IP轉(zhuǎn)換為a.b.c.d的格式.
*/
{
printf("invalid server_ip\n");
return -1;
}
//argv[1] 就是我們運行程序時候 ./client <ip>的 <ip>
memset(tSocketServerAddr.sin_zero, 0, 8);
上面是常規(guī)的對服務器端的連接信息的信息聲明
連接服務器
iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr ,sizeof(struct sockaddr));
iSocketClient :發(fā)起連接的 套接字
tSocketServerAddr:服務器的地址 , sizeof(struct sockaddr):服務器的地址長度
返回值:成功為0,失敗為-1
如果客戶端和服務器通信,必須使用connect 事先建立連接
連接成功后,使用while(1)進入循環(huán)準備發(fā)消息
3.send發(fā)送請求 (recv接收應答)
iSendLen = send(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0);
參數(shù):
iSocketClient:客戶端的套接字
ucSendBuf:發(fā)送的消息
strlen(ucSendBuf):信息的長度
flags ==0
返回值:成功返回發(fā)送的字節(jié)數(shù),失敗返回-1 ,不可以發(fā)送0字節(jié)的
4.close關閉套接字
源碼閱讀:
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
/* socket
* connect
* send/recv
*/
int main(int argc ,char**argv)
{
int iSocketClient ;
struct sockaddr_in tSocketServerAddr ;
int iRet;
unsigned char ucSendBuf[1000];
int iSendLen;
if (argc!=2)
{
prinft("Usage:\n");
printf("%s<server_ip>\n",argv[0]);
return -1;
}
iSocketClient=socket(AF_INET, SOCK_STREAM, 0);
tSocketServerAddr.sin_family = AF_INET;
tSocketServerAddr.sin_port = htons(SERVER_PORT); /* host to net, short */
//tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
if(inet_aton(argv[1],&tSocketServerAddr.sin_addr)==0)
{
printf("invalid server_ip\n");
return -1;
}
memset(tSocketServerAddr.sin_zero, 0, 8);
?
iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr ,sizeof(struct sockaddr));
if(-1==iRet)
{
printf("connect error\n");
return -1;
}
while(1)
{
if(fgets(ucSendBuf,999,stdin))
{
iSendLen = send(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0);
if(iSendLen<=0)
{
close(iSocketClient);
return -1;
}
}
}
return 0;
}
UDP編程
1、特點和應用:
UDP特點:
1.相比TCP速度快 (因為TCP要建立連接,而UDP不需要)
2.簡單的請求、應答應用程序可以使用UDP
3.對于海量數(shù)據(jù)傳輸不應該使用UDP
4.廣播和多播應用必須使用UDP(廣播不需要連接)
UDP*應用:
DNS(域名解析)、NFS(網(wǎng)絡文件系統(tǒng))、RTP(流媒體
)**
2、網(wǎng)絡通信需要解決三大問題(應用層)
協(xié)議、端口(port)、IP地址
socket套接字是一個特殊的文件描述符,可以使用open、write、read、close進行網(wǎng)絡通信,通過socket函數(shù)調(diào)用得到這個網(wǎng)絡通信的文件描述符(套接字)
3.UDP的編程架構
4.socket編程的API
1、socket創(chuàng)建通信的套接字
int iSocketServer;
iSocketServer = socket(AF_INET,SOCK_DGRAM,0);
AF_INET 代表ipv4 , AF_INET6代表ipv6
type類型:SOCK_DGRAM(UDP的套接字)、SOCK_STREAM(TCP的套接字),SOCK_RAW(原始套接字)
protocol協(xié)議類別:(0、IPPROTO_TCP\IPPROTO_UDP)
返回值:>0 創(chuàng)建的文件描述符 (套接字),<0創(chuàng)建失敗
struct sockaddr_in 是IPv4地址結(jié)構(存放客戶端、服務器的地址信息(協(xié)議,port,IP))
struct sockaddr 是通用地址結(jié)構,不是存放數(shù)據(jù),只是socket api類型轉(zhuǎn)換
2、IPv4套接字地址結(jié)構
#include <netinet/in.h>
struct sockaddr_in{
unsigned short sin_family; //2 字節(jié) 協(xié)議AF_INET
unsigned short sin_port; //2字節(jié) 端口
struct in_addr sin_addr; //4字節(jié) IP地址(32位無符號整數(shù))
unsigned char sin_zero[8]; //8字節(jié) 全寫0
}
struct in_addr:
struct in_addr{
in_addr_t s_addr;
}
如果使用 Internet 所以 sin_family 一般為 AF_INET。
? sin_addr 設置為 INADDR_ANY 表示可以和任何的主機通信。
? sin_port 是要監(jiān)聽的端口號。
? bind 將本地的端口同 socket 返回的文件描述符捆綁在一起.成功是返回 0,
失敗的情況和 socket 一樣。
tSocketServerAddr.sin_family = AF_INET;
tSocketServerAddr.sin_port = htons(Server_port);/*host to net,short*/
tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;//可以和任何的主機通信
3、sendto()
ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,
const struct sockaddr *dest_addr, socklen_t addrlen);
? sendto 和 send 相似,區(qū)別在于 sendto 允許在無連接的套接字上指定一個
目標地址。
? dest_addr 表示目地機的 IP 地址和端口號信息,
? addrlen 常常被賦值為 sizeof ( struct sockaddr)。
? sendto 函數(shù)也返回實際發(fā)送的數(shù)據(jù)字節(jié)長度或在出現(xiàn)發(fā)送錯誤時返回-1。
iSendLen = sendto(iSocketClient, ucSendBuf , strlen(ucSendBuf) , 0, (const struct sockaddr *)&tSocketServerAddr , sizeof(struct sockaddr));
**4.recvfrom()**接收消息
如果udp不發(fā)數(shù)據(jù)前就要接收消息,必須對udp套接字進行綁定
recvfrom 函數(shù)
ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
? recvfrom 通常用于無連接套接字,因為此函數(shù)可以獲得發(fā)送者的地址。
?socket:udp套接字,接收消息端 (服務器端)
?buffer:用來存放消息的空間起始地址 ,len 是buf的長度,flags = 0 即可
? src_addr 是一個 struct sockaddr 類型的變量,該變量保存源機的 IP 地
址及端口號。 (發(fā)送者的ipv4地址信息)
? addrlen 常置為 sizeof ( struct sockaddr)。
返回值:
成功:返回實際收到的字節(jié)數(shù) 失?。悍祷?1
iAddrLen = sizeof(struct sockaddr);
iRecvLen = recvfrom(iSocketServer, ucRecvBuf, 999, 0, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);
成功操作打印文章來源:http://www.zghlxwxcb.cn/news/detail-775842.html
if (iRecvLen > 0)
{
ucRecvBuf[iRecvLen] = '\0';
printf("Get Msg From %s : %s\n", inet_ntoa(tSocketClientAddr.sin_addr), ucRecvBuf);
}
5.源碼閱讀
Server.c
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
/* socket
* bind
* send/recv
*/
#define Server_port 8888
int main(int argc,char**argv)
{
int iSocketServer;
int iSocketClient;
struct sockaddr_in tSocketServerAddr;
struct sockaddr_in tSocketClientAddr;
int iRet;
int iAddrLen;
int iRecvLen;
unsigned char ucRecvBuf[1000];
int iClientNum = -1;
iSocketServer = socket(AF_INET,SOCK_DGRAM,0);
if(-1==iSocketServer)
{
printf("socket error!");
return -1;
}
tSocketServerAddr.sin_family = AF_INET;
tSocketServerAddr.sin_port = htons(Server_port);/*host to net,short*/
tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
memset(tSocketServerAddr.sin_zero , 0, 8);
iRet= bind(iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
if(iRet==-1)
{
printf("bind error!\n");
return -1;
}
while (1)
{
iAddrLen = sizeof(struct sockaddr);
iRecvLen = recvfrom(iSocketServer, ucRecvBuf, 999, 0, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);
if (iRecvLen > 0)
{
ucRecvBuf[iRecvLen] = '\0';
printf("Get Msg From %s : %s\n", inet_ntoa(tSocketClientAddr.sin_addr), ucRecvBuf);
}
}
close(iSocketServer);
return 0;
}
client.c
#include <sys/types.h> /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
/* socket
* connect
* send/recv
*/
#define SERVER_PORT 8888
int main(int argc, char **argv)
{
int iSocketClient;
struct sockaddr_in tSocketServerAddr;
int iRet;
unsigned char ucSendBuf[1000];
int iSendLen;
if (argc != 2)
{
printf("Usage:\n");
printf("%s <server_ip>\n", argv[0]);
return -1;
}
iSocketClient = socket(AF_INET, SOCK_DGRAM, 0);
tSocketServerAddr.sin_family = AF_INET;
tSocketServerAddr.sin_port = htons(SERVER_PORT); /* host to net, short */
//tSocketServerAddr.sin_addr.s_addr = INADDR_ANY;
if (0 == inet_aton(argv[1], &tSocketServerAddr.sin_addr))
{
printf("invalid server_ip\n");
return -1;
}
memset(tSocketServerAddr.sin_zero, 0, 8);
#if 0
iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
if (-1 == iRet)
{
printf("connect error!\n");
return -1;
}
#endif
while (1)
{
if (fgets(ucSendBuf, 999, stdin))
{
#if 0
iSendLen = send(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0);
#else
iSendLen = sendto(iSocketClient, ucSendBuf , strlen(ucSendBuf) , 0, (const struct sockaddr *)&tSocketServerAddr , sizeof(struct sockaddr));
#endif
if (iSendLen <= 0)
{
close(iSocketClient);
return -1;
}
}
}
return 0;
}
#參考:
1.韋東山linux編程
2.千鋒教育物聯(lián)網(wǎng)文章來源地址http://www.zghlxwxcb.cn/news/detail-775842.html
到了這里,關于初學記錄【linux應用】 TCP/UDP 網(wǎng)絡編程 C語言的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!