初學socket網絡編程

一、服務器端實現:

1.創建socket

#include <sys/types.h>
#include <sys/socket.h>
int socket(int domain, int type, int protocol);

domain是套接字的域(協議簇),常用AF_UNIX(本地套接字)、AF_INET(網絡套接字); 
type套接字類型,決定套接字採用的通信機制;

  • 流套接字(SOCK_STREAM):維持一個有序、可靠、雙向字節流的連接,較大的數據塊會被分解、傳輸、重組。發送數據時不會丟失、重複或亂序到達,其行爲是可預見的。在AF_INET域中是通過TCP/IP連接實現的。
  • 數據報套接字(SOCK_DGRAM):不建立和維持連接,每個數據報作爲一個單獨的網絡消息來傳輸,因而,數據報在傳輸過程中可能會丟失、重複、亂序到達。在AF_INET域中通過UDP/IP連接實現。

儘管SOCK_DGRAM提供無序、不可靠的服務,但從資源角度看,這種套接字開銷小、速度快,因爲它們無須建立和維持網絡連接。因此,數據報套接字適用於對可靠性要求不高、強調通信效率的場合,如網絡視頻通信。

  • AF_UNIX域只支持SOCK_STREAM;
  • AF_INET域支持SOCK_STREAM、SOCK_DGRAM;

protocol指定通信所用的協議,一般由套接字域和類型來決定,一般設爲0,使用默認協議(自動選擇)。

2.套接字命名(綁定)

struct sockaddr_in ad;
ad.sin_family=AF_INET;
ad.sin_port=htons(9999);
inet_aton("192.168.245.145",ad.sin_addr); //ad.sin_addr.s_addr=inet_addr("192.168.245.145"); 兩種地址賦值

命名就是將套接字綁定到一個特定的地址; 
對於AF_UNIX就是將套接字關聯到文件系統的一個路徑名; 
AF_INET就是關聯到一個IP端口號。

#include <sys/socket.h>
int bind(
int socket, //套接字標識符
const struct sockaddr * address, //要綁定的地址
size_t address_len); //地址結構長度
成功返回0,失敗返回-1

注意:需要將一個特定地址結構指針轉換爲通用地址類型(struct sockaddr *).

AF_UNIX域的地址結構定義在頭文件sys/un.h中:

struct sockaddr_un{
sa_family_t sun_family; //sun_family是地址類型,賦值爲AF_UNIX
char sun_path[]; //sun_path用以指定套接字地址,應賦值爲一個路徑(文件)名,規定不超過108字符
}

sa_family_t 是短整數類型;

AF_INET域地址結構定義在頭文件netinet/in.h中:

struct sockaddr_in{
short int sin_family; //AF_INET
unsigned short int sin_port; //端口號
struct in_addr sin_addr; //IP地址
}
 
struct in_addr{
unsigned long int s_addr;
}

新版本的內核中定義sockaddr_in結構體如下(netinet/in.h):

/* Structure describing an Internet socket address. */
struct sockaddr_in
{
__SOCKADDR_COMMON (sin_);
in_port_t sin_port; /* Port number. */
struct in_addr sin_addr; /* Internet address. */
 
/* Pad to size of `struct sockaddr'. */
unsigned char sin_zero[sizeof (struct sockaddr) -
__SOCKADDR_COMMON_SIZE -
sizeof (in_port_t) -
sizeof (struct in_addr)];
};

__SOCKADDR_COMMON (sin_)宏定義如下(bits/sockaddr.h):

#define __SOCKADDR_COMMON(sa_prefix) \
sa_family_t sa_prefix##family //兩個#號表示將其前後字符串連接起來

使用網絡套接字替換文件套接字,需要移除sys/un.h,添加netinet/in.h和arpa/inet.h;

AF(address family)和PF(protocol family)一樣,定義在socket.h中

3.監聽連接

通過調用listen函數在服務套接字socket上監聽客戶端連接,listen函數會創建一個隊列來緩存爲處理的連接;

#include <sys/socket.h>
int listen(int socket, int backlog);
socket是服務套接字標識符
backlog爲連接隊列的最大長度(因爲LInux系統通常會對隊列中的最大連接數有所限制),當隊列中的連接數超過這個值時,後續的連接將被拒絕。
成功返回0,失敗返回-1

4.接收連接

調用accept函數來接收客戶的連接

#include <sys/socket.h>
int accept(
int socket, //server socket
struct sockaddr *address, //存放連接客戶的地址,也可設爲空指針(不需要客戶地址)
size_t *address_len);, //指定客戶地址長度

accept函數會創建一個新套接字來與所接受的客戶進行通信,並返回新套接字描述符號; 
如果監聽隊列中沒有爲處理的連接,accept函數將阻塞,程序暫停執行,直到有客戶連接爲止;當有未處理的客戶連接時,accept函數返回一個新套接字描述符,發生錯誤時返回-1。

二、客戶端實現

1.創建socket 
2.請求連接服務器 
通過調用connect函數連接到服務器進程,在一個爲命名的客戶套接字和服務器套接字之間建立一個連接。

#include <sys/socket.h>
int connect(
int socket, //客戶套接字描述符
const struct sockaddr *address, //指向服務器套接字地址
size_t address_len); //服務器套接字地址結構長度
成功返回0,失敗返回-1

3.數據通信 
4.關閉socket


三、程序實例

1. 用文件套接字實現本地進程通信

//server:A接收
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/un.h>
#include <unistd.h>
 
int main(){
int fd;
int r;
char buf[200];
 
//1.建立socket
fd = socket(AF_UNIX,SOCK_DGRAM,0);
if(fd == -1) printf("socket err:%m\n"),exit(-1);
printf("socket create success\n");
//2.構造本地文件地址
struct sockaddr_un addr={0};
addr.sun_family=AF_UNIX;
memcpy(addr.sun_path,"my.sock",strlen("my.sock"));
//3.把socket綁定在地址上
r = bind(fd,(struct sockaddr*)&addr,sizeof(addr));
if(r==-1) printf("bind err:%m\n"),exit(-1);
printf("地址綁定成功\n");
 
//4.接收數據
bzero(buf,sizeof(buf)); //清空緩衝區buf
r=read(fd,buf,sizeof(buf));
buf[r]='\0';
printf("%s\n",buf);
 
//5.關閉
close(fd);
 
//6.刪除socket文件
unlink("my.sock");
}
//client:B發送數據
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <linux/un.h>
main(){
int fd;
int r;
struct sockaddr_un addr={0};
 
//1.建立socket
fd=socket(AF_UNIX,SOCK_DGRAM,0);
 
//2.連接到指定的地址
addr.sun_family=AF_UNIX;
memcpy(addr.sun_path,"my.sock",strlen("my.sock"));
r=connect(fd,(struct sockaddr*)&addr,sizeof(addr));
 
//3.發送數據
write(fd,"Hello!mylover!",strlen("Hello!mylover!"));
 
//4.關閉
close(fd);
}

2. 用網絡套接字實現網絡進程通信

//server端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
 
int main(){
int fd;
int r;
char buf[200];
 
//1.建立socket
fd = socket(AF_INET,SOCK_DGRAM,0);
if(fd == -1) printf("socket err:%m\n"),exit(-1);
printf("socket create success\n");
//2.構造本地文件地址
struct sockaddr_in addr={0};
addr.sin_family=AF_INET;
addr.sin_port=htons(9999); //host to network,s表示short,htonl 中 l 標誌long,將整數轉換爲網絡字節序
addr.sin_addr.s_addr=inet_addr("192.168.245.137"); //或htonl(inaddr_any) inaddr_any代表任意地址
//3.把socket綁定在地址上
r = bind(fd,(struct sockaddr*)&addr,sizeof(addr));
if(r==-1) printf("bind err:%m\n"),exit(-1);
printf("地址綁定成功\n");
 
//4.接收數據
bzero(buf,sizeof(buf)); //清空緩衝區buf
r=read(fd,buf,sizeof(buf));
buf[r]='\0';
printf("%s\n",buf);
 
//5.關閉
close(fd);
 
//6.刪除socket文件
unlink("my.sock");
}

B程序

//client端
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
main(){
int fd;
int r;
struct sockaddr_in addr={0};
 
//1.建立socket
fd=socket(AF_INET,SOCK_DGRAM,0);
 
//2.連接到指定的地址
addr.sin_family=AF_INET;
addr.sin_port=htons(9999);
addr.sin_addr.s_addr=inet_addr("127.0.0.1");
r=connect(fd,(struct sockaddr*)&addr,sizeof(addr));
 
//3.發送數據
write(fd,"Hello!mylover!",strlen("Hello!mylover!"));
 
//4.關閉
close(fd);
}
發佈了45 篇原創文章 · 獲贊 17 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章