前言
为了更方便地开发网络应用程序,美国伯克利大学在UNIX上推出了一种应用程序访问通信协议的操作系统调用接字(Socket)。 Socket的出现,使得程序员可以很方便地访问 TCP/IP,从而开发各种网络应用程序。后来套接字被引进到 Windows等操作系统,成为开发网络应用程序的有效工具。
一、socket
1.简介
- socket(套接字)是一种通信机制,它是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,从所处的地位来讲,套接字上联应用进程,下联网络协议栈,是应用程序通过网络协议进行通信的接口,是应用程序与网络协议栈进行交互的接口。
- socket用于描述IP地址和端口,应用程序通常通过“套接字”向网络发出请求或者应答网络请求。
2.分类
- 流套接字(SOCK_STREAM)
使用TCP协议提供面相连接、可靠的传输服务。该服务将保证数据能够实现无差错、无重复送,并按顺序接收。 - 数据报套接字(SOCK_DGRAM)
使用UDP提供无连接、不可靠的传输服务。该服务并不能保证数据传输的可靠性,数据有可能在传输过程中丢失或出现数据重复,且无法保证顺序地接收到数据。 - 原始套接字(SOCK_RAW)
原始套接字可以读写内核没有处理的IP数据包,而流套接字只能读取TCP协议的数据,数据报套接字只能读取UDP协议的数据。因此,如果要访问其他协议发送的数据必须使用原始套接字 。原始套接字主要用于一些协议的开发,可以进行比较底层的操作。
二、socket工作原理
- 服务端初始化socket,绑定端口,监听端口,等待客户端连接
- 客户端初始化socket,建立连接
- 客户端连接建立成功后,向服务端发送数据
- 服务端收到数据后向客户端回复响应数据
- 最后关闭连接,交互结束
三、代码示例
1.客户端
#include <stdio.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define PORT 23
#define ADDR "127.0.0.1"
int main()
{
int iSocketFD = 0;
unsigned int iRemoteAddr = 0;
struct sockaddr_in stRemoteAddr = { 0 };
char buf[4096] = {0};
// 创建socket
iSocketFD = socket(AF_INET, SOCK_STREAM, 0);
if (0 > iSocketFD) {
printf("create socket failed\n");
return 0;
}
// 初始化地址信息
stRemoteAddr.sin_family = AF_INET;
stRemoteAddr.sin_port = htons(PORT);
inet_pton(AF_INET, ADDR, &iRemoteAddr);
stRemoteAddr.sin_addr.s_addr = iRemoteAddr;
// 与服务端建立连接
if (0 > connect(iSocketFD, (void *)&stRemoteAddr, sizeof(stRemoteAddr))) {
printf("connect failed\n");
} else {
printf("connect success\n");
send(iSocketFD, "this is client msg!", sizeof("this is client msg!"), 0);
recv(iSocketFD, buf, sizeof(buf), 0);
printf("Received:%s\n", buf);
}
// 关闭socket
close(iSocketFD);
return 0;
}
2.服务端
#include <stdio.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#define PORT 23
#define BACKLOG 5
#define ADDR "127.0.0.1"
int main()
{
int iSocketFD = 0;
int iNewSocketfd = 0;
char buf[4096] = {0}; //
unsigned int iLocalAddr;
struct sockaddr_in stLocalAddr = {0};
struct sockaddr_in stRemoteAddr = {0};
socklen_t socklen = 0;
// 创建socket
iSocketFD = socket(AF_INET, SOCK_STREAM, 0);
if (0 > iSocketFD) {
printf("create socket failed\n");
return 0;
}
// 初始化地址信息
stLocalAddr.sin_family = AF_INET;
stLocalAddr.sin_port = htons(PORT);
inet_pton(AF_INET, ADDR, &iLocalAddr);
stLocalAddr.sin_addr.s_addr=iLocalAddr;
// 绑定地址和socket
if (0 > bind(iSocketFD, (void *)&stLocalAddr, sizeof(stLocalAddr))) {
printf("bind failed\n");
return 0;
}
// 监听socket
if (0 > listen(iSocketFD, BACKLOG)) {
printf("listen failed\n");
return 0;
}
// 等待客户端连接
iNewSocketfd = accept(iSocketFD, (void *)&stRemoteAddr, &socklen);
if (0 > iNewSocketfd) {
printf("accept failed\n");
return 0;
}else{
printf("accept success\n");
send(iNewSocketfd, "this is server msg!", sizeof("this is server msg!"), 0);
recv(iNewSocketfd, buf, sizeof(buf), 0);
printf("Received: %s\n", buf);
}
// 关闭socket
close(iNewSocketfd);
close(iSocketFD);
return 0;
}
四、socket编程中常用函数及说明
- socket():创建一个套接字,返回一个套接字描述符。
- bind():将一个套接字与一个本地地址绑定。
- listen():将一个套接字设置为被动模式,等待客户端连接。
- accept():接受客户端的连接请求,返回一个新的套接字描述符,用于与客户端通信。
- connect():与服务器建立连接。
- send():发送数据。
- recv():接收数据。
- close():关闭套接字。
- htons():将主机字节序转换为网络字节序。
- ntohs():将网络字节序转换为主机字节序。
- inet_addr():将点分十进制的IP地址转换为32位的网络字节序整数。
- inet_ntoa():将32位的网络字节序整数转换为点分十进制的IP地址。
- FD_CLR()函数:从文件描述符集中清除一个文件描述符。
- FD_ISSET()函数:判断一个文件描述符是否在文件描述符集中。
- FD_SET()函数:将一个文件描述符添加到文件描述符集中。
- FD_ZERO()函数:将文件描述符集清空。
五、问题总结
- 服务端使用socket()创建和fd和accept()创建的fd有什么区别
socket()创建的fd用来监听客户端连接请求的套接字,只负责接收连接请求,不负责数据传输。
accept()创建的fd与客户端建立了连接,用于实际的数据传输。
总结
socket编程是较为常用的网络编程方式,它可以实现最简单的跨主机通信,本文简单介绍了c语言socket的使用,并提供了一些简单易懂的代码,希望对您有所帮助。