VC天下 » Linux

Socket简介

Socket,这是一个老生常谈的话题,我只是梳理一下其相关知识点。
Socket is a port。在Linux中,网络编程是通过Socket接口来完成的。Socket接口是一种特殊的I/O,也是一种文件描述符。
一个Socket 用一个半相关描述: {协议,本地地址,本地端口}。
通过IP可以建立网络中两个设备之间的连接。通过Sockets使用TCP/UDP服务来建立网络中两个应用程序或进程之间的连接。

Socket系统调用包括:socket()、bind()、listen()、accept()、connect()、send()/sendto()、recv()/recvfrom()、read()、write()、close()、shutdown()。

connect()、recv()、send()、read()、write()、都属于阻塞性函数,若资源没有准备好,调用该函数的进程将进入睡眠状态,无法处理I/O多路复用的情况。
fcntl()和select()函数可以用来解决多路复用问题。fcntl()可以实现非阻塞I/O或信号驱动I/O;select()对CPU资源的利用率更高,功能更加强大。

TCP协议中,Socket的服务端和客户端交互:

UDP协议中,Socket的服务端和客户端交互:

一个简单的Linux TCP-Socket编程模型:
请包含头文件:stdio.h、sys/types.h、sys/socket.h、netinet/in.h、arpa/inet.h
server端:tcp_server.c

#include
#include
#include
#include
#include

#define BUFSIZE 100

int main(int argc, char *argv[])
{
int sockfd;
int fd;
int len;
struct sockaddr_in server_addr; //server网络地址结构体
struct sockaddr_in client_addr; //client网络地址结构体
int sin_size;
char buf[BUFSIZE];
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET; //设置为IP通信
server_addr.sin_addr.s_addr = INADDR_ANY; //服务器IP地址---允许连接到所有本地地址上
server_addr.sin_port = htons(8000); //server port

//get a socket fd TCP类型
if( (sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0) { perror("socket"); return -1; } //绑定 if( bind(sockfd, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) < 0) { perror("bind"); return -1; } //监听连接请求----监听队列长度为6 listen(sockfd, 6); sin_size = sizeof(struct sockaddr_in); //wait for client if( (fd = accept(sockfd, (struct sockaddr *)&client_addr, &sin_size)) < 0) { perror("accept"); return -1; } printf("accept client %s \n", inet_ntoa(client_addr.sin_addr)); len = send(fd, "Welcome to my server\n", 21, 0); //连接成功后,发送欢迎消息 //接收客户端的数据并将其发送给客户端--recv返回接收到的字节数,sand返回发送的字节数 while( (len = recv(fd, buf, BUFSIZE, 0)) > 0)
{
buf[len] = '\0';
printf("%s\n", buf);
if(send(fd, buf, len, 0) < 0) { perror("write"); return -1; } } close(fd); close(sockfd); return 0; }

client端:tcp_server.c

#include
#include
#include
#include
#include

#define BUFSIZE 100
int main(int argc, char *argv[])
{
int sockfd;
int len;
struct sockaddr_in client_addr;
char buf[BUFSIZE];
memset(&client_addr, 0, sizeof(client_addr));//数据初始化--清零
client_addr.sin_family = AF_INET; //设置为IP通信
client_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //服务器IP地址
client_addr.sin_port = htons(8000); //服务器端口号

//创建客户端套接字
if( (sockfd = socket(PF_INET, SOCK_STREAM, 0)) < 0) { perror("socket"); return -1; } //将套接字绑定到服务器的网络地址上 if(connect(sockfd, (struct sockaddr *)&client_addr, sizeof(struct sockaddr)) < 0) { perror("connect"); return -1; } printf("connected to server\n"); len = recv(sockfd, buf, BUFSIZE, 0);//接收服务器端信息 buf[len] = '\0'; //循环的发送接收信息并打印接收信息--recv返回接收到的字节数,sand返回发送的字节数 while(1) { printf("Enter string to send:\n"); scanf("%s", buf); if(!strcmp(buf, "quit")) { break; } len = send(sockfd, buf, strlen(buf), 0); len = recv(sockfd, buf, BUFSIZE, 0); buf[len] = '\0'; printf("received: %s", buf); } close(sockfd); return 0; }

下面则为一个简单的Linux UDP-Socket编程模型:
server端:udp_server.c

#include
#include
#include
#include
#include

#define BUFSIZE 100

int main(int argc, char *argv[])
{
int sockfd;
int fd;
int len;
struct sockaddr_in server_addr;//服务器网络地址结构体
struct sockaddr_in client_addr;//客户端网络地址结构体
int sin_size;
char buf[BUFSIZE];//数据传送的缓冲区
memset(&server_addr, 0, sizeof(server_addr));//数据传送的缓冲区
server_addr.sin_family = AF_INET; //设置为IP通信
server_addr.sin_addr.s_addr = INADDR_ANY; //服务器IP地址--允许连接到所有本地地址上
server_addr.sin_port = htons(8088); //服务器端口号
//创建服务器端套接字--IPv4协议,面向无连接通信,UDP协议
if((sockfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket"); return -1; } //将套接字绑定到服务器的网络地址上 if(bind(sockfd, (struct sockaddr*)&server_addr, sizeof(struct sockaddr)) < 0) { perror("bind"); return -1; } printf("socket begin receive...\n"); //接收客户端的数据并将其发送给客户端--recvfrom是无连接的 if((len = recvfrom(sockfd, buf, BUFSIZE, 0, (struct sockaddr*)&client_addr, &sin_size)) < 0) { perror("recvfrom"); return -1; } printf("received packet from %s:\n", inet_ntoa(client_addr.sin_addr)); buf[len] = '\0'; printf("contents: %s\n", buf); close(sockfd); return 0; }

client端:udp_client.c

#include
#include
#include
#include
#include

#define BUFSIZE 100
int main(int argc, char *argv[])
{
int sockfd;
int len;
int sin_size;
struct sockaddr_in client_addr;
char buf[BUFSIZE];
memset(&client_addr, 0, sizeof(client_addr));//数据初始化--清零
client_addr.sin_family = AF_INET; //设置为IP通信
client_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); //服务器IP地址
client_addr.sin_port = htons(8088); //服务器端口号

//创建客户端套接字,面向无连接通信,UDP协议
if( (sockfd = socket(PF_INET, SOCK_DGRAM, 0)) < 0) { perror("socket"); return -1; } strcpy(buf, "I am a udp messager!"); sin_size = sizeof(struct sockaddr_in); //send to server if((len = sendto(sockfd, buf, strlen(buf), 0, (struct sockaddr *)&client_addr, sizeof(struct sockaddr))) < 0) { perror("sendto"); return -1; } printf("sending: '%s'\n", buf); close(sockfd); return 0; }

Socket API相关说明:
因为Socket对象是一类特殊的文件,所以可以用Linux系统I/O系统调用read和write函数,来读/写Socket对象。这两个函数对Socket的读写操作默认是以阻塞的方式进行的。
对Socket调用write()行为与将第4个参数flags设置为0的send()的行为完全相同。
通信的类型有本机通信(AF_UNIX/AF_LOCAL)和网络通信(AF_INET)。
端口号的选择:小于1024的端口号是系统所保留的,用户不能随便使用。
在TCP-Socket中,客户端也可以选择监听(listen),但一般不这样做。

One thought on “Socket简介

发表评论