网络socket编程(c语言)
一.socket通信簡(jiǎn)介
Socket是對(duì)TCP/IP協(xié)議的封裝,Socket本身并不是協(xié)議,而是一個(gè)調(diào)用接口(API),通過Socket,我們才能使用TCP/IP協(xié)議,主要利用三元組【ip地址,協(xié)議,端口】。
socket起源于Unix,而Unix/Linux基本哲學(xué)之一就是“一切皆文件”,都可以用“打開open –> 讀寫write/read –> 關(guān)閉close”模式來操作。
Socket()函數(shù)返回一個(gè)整型的Socket描述符,隨后的連接建立、數(shù)據(jù)傳輸?shù)炔僮鞫际峭ㄟ^該Socket實(shí)現(xiàn)的。Socket是應(yīng)用層與TCP/IP協(xié)議族通信的中間軟件抽象層。
二.網(wǎng)絡(luò)socket客戶端和服務(wù)器端連接過程如下:
1.1 socket()
int socket(int domain,int type, int protocol);
返回值: 成功:返回指向新創(chuàng)建的socket的文件描述符,失敗:返回-1。
domain:即協(xié)議域,又稱為協(xié)議族(family)。常用的協(xié)議族有,AF_INET、AF_INET6、AF_LOCAL(或稱AF_UNIX,Unix域socket)、AF_ROUTE等等。協(xié)議族決定了socket的地址類型,在通信中必須采用對(duì)應(yīng)的地址,如AF_INET決定了要用ipv4地址(32位的)與端口號(hào)(16位的)的組合、AF_UNIX決定了要用一個(gè)絕對(duì)路徑名作為地址。
type:創(chuàng)建的套接字的類型,常用SOCK_STREAM(流式套接字),SOCK_DGRAM(數(shù)據(jù)報(bào)套接字)
protocol: 傳0 表示使用默認(rèn)協(xié)議。
1.2 bind()
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
返回值:函數(shù)執(zhí)行成功返回0,否則返回-1, 并設(shè)置錯(cuò)誤代碼。
sockfd:需要綁定的套接字文件描述符。
addr:存入網(wǎng)絡(luò)類型,網(wǎng)絡(luò)地址和端口號(hào)的結(jié)構(gòu)體。
addrlen:addr結(jié)構(gòu)體的長(zhǎng)度。
ipv4使用的結(jié)構(gòu)體是struct sockaddr_in類型,所以綁定時(shí)需要強(qiáng)制類型轉(zhuǎn)換成struct sockaddr類型
struct sockaddr_in
{
sa_family_t sin_family; /* 2 bytes address family, AF_xxx such as AF_INET /
in_port_t sin_port; / 2 bytes port*/
struct in_addr sin_addr; /* 4 bytes IPv4 address*/
unsigned char sin_zero[8]; /* 8 bytes unused padding data, always set be zero */
};
struct sockaddr
{
sa_family_t sa_family; /* 2 bytes address family, AF_xxx /
char sa_data[14]; / 14 bytes of protocol address */
}
在使用bind時(shí)常用的兩個(gè)函數(shù):htons和htonl,在將一個(gè)地址綁定到socket的時(shí)候,先將主機(jī)字節(jié)序轉(zhuǎn)換成為網(wǎng)絡(luò)字節(jié)序
serv_addr.sin_port = htons(LISTEN_PORT);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
IP地址“127.0.0.1”這是點(diǎn)分十進(jìn)制形式的字符串形式,而在結(jié)構(gòu)體struct sockaddr_in 中IP地址是以32位(即4字節(jié)整形類型)數(shù)據(jù)保存的,這時(shí)我們可以調(diào)用 inet_aton() 函數(shù)將點(diǎn)分十進(jìn)制字符串轉(zhuǎn)換成 32位整形類型
1.3 listen()
int listen(int sockfd,int backlog);
返回值:成功返回0,失敗返回-1。
sockfd: socket文件描述符
backlog: 排隊(duì)建立3次握手隊(duì)列和剛剛建立3次握手隊(duì)列的鏈接數(shù)和
1.4 accept()
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
屬性相同的連接套接字,并為這個(gè)套接字分配一個(gè)文件描述符,然后以這個(gè)描述符返回
返回值:若成功則返回一個(gè)非負(fù)整數(shù)標(biāo)識(shí)這個(gè)連接套接字,發(fā)生錯(cuò)誤時(shí)返回-1。
sockfd:一個(gè)正在用于監(jiān)聽功能下的套接字的文件描述符。
addr:用于儲(chǔ)存接受到的客戶端的網(wǎng)絡(luò)信息的結(jié)構(gòu)體(參考bind下的使用)
addrlen:addr結(jié)構(gòu)體長(zhǎng)度
1.5 connect()
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
sockfd: 客戶端的socket()創(chuàng)建的描述字
addr: 要連接的服務(wù)器的socket地址信息,這里面包含有服務(wù)器的IP地址和端口等信息
addrlen: socket地址的長(zhǎng)度
客戶端server.c程序編寫
/********************************************************************************** Copyright: (C) 2021 jiaoer237* All rights reserved.** Filename: socket_server.c* Description: This file * * Version: 1.0.0(11/21/2021)* Author: yanp <2405204881@qq.com>* ChangeLog: 1, Release initial version on "11/21/2021 01:59:13 PM"* ********************************************************************************/ #include <stdio.h> #include <string.h> #include <errno.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h>#define PORT 8899 #define BACKLOG 13int socket_server_init(char *listen_ip,int listen_port);int main() {int listen_fd,clien_fd=-1;struct sockaddr_in cli_addr;socklen_t cliaddr_len;char buf[1024];int rv=-1;listen_fd=socket_server_init(NULL,PORT);/*初始化socket函數(shù)*/while(1){printf("\nstart waiting and accept new client connect...\n");clien_fd=accept(listen_fd,(struct sockaddr*)&cli_addr,&cliaddr_len);/*accept調(diào)用*/if(clien_fd<0){printf("accept new client failure:%s\n",strerror(errno));return -1;}printf("accept new client[%s:%d] with fd [%d]\n",inet_ntoa(cli_addr.sin_addr),ntohs(cli_addr.sin_port),clien_fd);memset(buf,0,sizeof(buf));rv=read(clien_fd,buf,sizeof(buf));if(rv<0){printf("read data from socket[%d] failure:%s\n",clien_fd,strerror(errno));close(clien_fd);continue;}else if(0==rv){printf("read data from socket[%d] failure:%s\n",clien_fd,strerror(errno));close(clien_fd);continue;}printf("read %d data from server client [%d] and echo it back:'%s'\n",rv,clien_fd,buf);if(write(clien_fd,buf,rv)<0){printf("write %d bytes data back to client[%d] failure:%s\n",rv,clien_fd,strerror(errno));close(clien_fd);}}return 0; }int socket_server_init(char *listen_ip,int listen_port) {int listenfd;struct sockaddr_in servaddr;if((listenfd=socket(AF_INET,SOCK_STREAM,0))<0)/*創(chuàng)建socket描述符*/{printf("socket_server to create a TCP socket fd failure:[%s]\n",strerror(errno));return -1;}printf("create a tcp socket fd[%d] success\n",listenfd);int on=1; if((setsockopt(listenfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on)))<0) /*讓端口號(hào)能夠立即重復(fù)使用*/{printf("setsockopt failure:%s",strerror(errno));return -2;}memset(&servaddr,0,sizeof(servaddr));servaddr.sin_family=AF_INET;servaddr.sin_port=htons(PORT);if(!listen_ip)/*加入傳入IP地址則監(jiān)聽指定ip,否則監(jiān)聽所有ip*/{servaddr.sin_addr.s_addr=htonl(INADDR_ANY);}else{servaddr.sin_addr.s_addr=htonl(listen_port);}if(bind(listenfd,(struct sockaddr *)&servaddr,sizeof(servaddr))<0)/*綁定端口號(hào)和ip*/{printf("socket[%d] bind on port[%d] for ip address failure:%s\n",listenfd,listen_port,strerror(errno));return -2;}printf("socket[%d] bind on port[%d] for ip address success\n",listenfd,listen_port);listen(listenfd,BACKLOG);return listenfd; }服務(wù)器端client.c程序編寫
/********************************************************************************** Copyright: (C) 2021 jiaoer237* All rights reserved.** Filename: socket_client.c* Description: This file * * Version: 1.0.0(11/21/2021)* Author: yanp <2405204881@qq.com>* ChangeLog: 1, Release initial version on "11/21/2021 08:49:25 PM"* ********************************************************************************/ #include <stdio.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h>#define SERVER_PORT 8899 #define MSG_STR "Hello yanp, Unix Network Program World!" #define SERVER_IP "192.168.1.120"int main(int argc,char **argv) {int conn_fd = -1;int rv = -1;char buf[1024];struct sockaddr_in serv_addr;conn_fd=socket(AF_INET,SOCK_STREAM,0);/*socket創(chuàng)建客戶端的描述符*/if(conn_fd<0){printf("create client socket failure:%s\n",strerror(errno));return -1;}memset(&serv_addr, 0, sizeof(serv_addr));serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(SERVER_PORT);inet_aton(SERVER_IP,&serv_addr.sin_addr);/*將點(diǎn)分十進(jìn)制轉(zhuǎn)換成32位整型類型*/if(connect(conn_fd,(struct sockaddr *)&serv_addr,sizeof(serv_addr))<0)/*連接服務(wù)器*/{printf("client[%d] connect to server[%s:%d] failure:%s\n",conn_fd,SERVER_IP,SERVER_PORT,strerror(errno));return -1;}if(write(conn_fd,MSG_STR,strlen(MSG_STR))<0){printf("write data to server[%s,%d] failure:%s\n",SERVER_IP,SERVER_PORT,strerror(errno));return -2;}memset(buf,0,sizeof(buf));rv=read(conn_fd,buf,sizeof(buf));if(rv<0){printf("read data from server failure:%s\n",strerror(errno));return -3;}else if(rv==0){printf("client connetc to server get disconnect\n");return -4;}printf("read %d bytes from server:'%s'\n",rv,buf);return 0; }
客戶端連接服務(wù)器之后進(jìn)行讀寫操作
總結(jié)
以上是生活随笔為你收集整理的网络socket编程(c语言)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 进程间的通信IPC(无名管道和命名管道)
- 下一篇: IPC 共享内存和 消息队列(发送、