Linux下C语言socket编程

socket网络编程服务端和客户端


一、流程图

在这里插入图片描述

具体工作原理暂不分析。大致:
1.服务端:开始运行,阻塞accept(),等待客户端连接请求。
2.客户端:开始运行,发送连接请求给服务端,服务端收到后,建立连接。连接成功。
3.客户端:发送数据。服务端:接收数据,回到阻塞状态
4.连接结束。

二、具体代码

1.服务端

#include <sys/socket.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <sqlite3.h>
#include <arpa/inet.h>

#define BACKLOG 10
#define BUFFSIZE 4096
#define LISTEN_PORT 8889

int main(int argc,char *argv[])
{
	int rv = -1;
	int listen_fd = -1;
	int client_fd = -1;
	char buf[BUFFSIZE];
	struct sockaddr_in svr_addr;
	struct sockaddr_in client_addr;
	socklen_t cliaddr_len;

	listen_fd = socket(AF_INET, SOCK_STREAM, 0);		//定义套接字
	
	if(listen_fd < 0)
	{
		printf("creat socket failure:%s\n",strerror(errno));
		return -1;
	}

	printf("socket create listen_fd[%d]\n",listen_fd);
	
	memset(&svr_addr, 0, sizeof(svr_addr));		//先把sur_addr对应的内存空间清0
	svr_addr.sin_family = AF_INET;
	svr_addr.sin_port = htons(LISTEN_PORT);
	svr_addr.sin_addr.s_addr = htonl(INADDR_ANY);

	rv = bind(listen_fd, (struct sockaddr *)&svr_addr, sizeof(svr_addr));		//bind端口

	if(rv < 0)
	{
		printf("bind error:%s\n",strerror(errno));
		return -2;
	}
	else
	{
		printf("bind success\n");
	}

	listen(listen_fd, BACKLOG);			
	printf("star listen to port[%d]\n", LISTEN_PORT);
	while(1)
	{
		printf("waiting for new client connection...\n");
		client_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &cliaddr_len);	
		//阻塞,等待客户端连接请求
		
		if(client_fd < 0)
		{
			printf("accept new socket error:%s\n",strerror(errno));
			return -3;
		}
		printf("Accpet new client [%s:%d] with fd [%d]\n",inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), client_fd);

		memset(buf, 0, sizeof(buf));
		rv = read(client_fd, buf, sizeof(buf));		
		if(rv < 0)
		{
			printf("read data from client socket [%d] error:%s\n", client_fd, strerror(errno));
			close(client_fd);
			continue;
		}

		else if(rv == 0)
		{
			printf("client socket [%d] disconnection\n", client_fd);
			close(client_fd);
			continue;
		}
		printf("read %d bytes data from client[%d]:%s\n", rv, client_fd, buf);
		rv = write(client_fd, buf, rv);			
		if(rv < 0)
		{
			printf("write error:%s\n",strerror(errno));
			close(client_fd);
		}

		sleep(5);
		close(client_fd);
	}

	close(listen_fd);	
	return 0;
}

结果:

waiting for new client connection...
tianyujie@icloud-1st:~$ ./sev 
socket create listen_fd[3]
bind success
star listen to port[8889]
waiting for new client connection...

2.客户端

代码如下

#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>

#define SVR_IP "xxxxxxxx"//这里填写自己所连服务器的公网IP 或者127.0.0.1本机互联
#define SVR_PORT 8889
#define MSG "Here are client, call server!! call server!"

int main(int argc, char **argv)
{
	int conn_fd = -1;
	int rv = -1;
	char buf[4096];
	struct sockaddr_in svr_addr;

	conn_fd = socket(AF_INET, SOCK_STREAM, 0);

	if(conn_fd < 0)
	{
		printf("creat socket error:%s\n",strerror(errno));
		return -1;
	}

	memset(&svr_addr, 0, sizeof(svr_addr));
	svr_addr.sin_family = AF_INET;
	svr_addr.sin_port = htons(SVR_PORT);
	inet_aton(SVR_IP,&svr_addr.sin_addr);
	
	rv = connect(conn_fd, (struct sockaddr*)&svr_addr, sizeof(svr_addr));
	if(rv < 0)
	{
		printf("connect to server :%s:%d error:%s\n", SVR_IP, SVR_PORT, strerror(errno));
		return 0;
	}

	rv = write(conn_fd, MSG, strlen(MSG));
	if(rv < 0)
	{
		printf("connect to server [%s:%d] error:%s",SVR_IP, SVR_PORT, strerror(errno));
		goto cleanap;
	}
	
	printf("send [%s] success\n", MSG);
	rv = read(conn_fd, buf,sizeof(buf));
	
	if(rv < 0)
	{
		printf("read from server error:%s\n",strerror(errno));
		goto cleanap;
	}

	printf("read success:%s\n" ,buf);
cleanap:
	close(conn_fd);
	
}

结果:
在这里插入图片描述

服务端接收消息:在这里插入图片描述

函数分析

1.socket()

int socket(int af, int type, int protocol);
socket()用于创建一个socket描述符。这个socket描述字跟文件描述字一样,后续的操作都有用到它,把它作为参数,通过它来进行一些读写操作。创建socket的时候,也可以指定不同的参数创建不同的socket描述符。

参数:

af 为地址族(Address Family),也就是 IP 地址类型,常用的有 AF_INET 和 AF_INET6。AF 是“Address Family”的简写,INET是“Inetnet”的简写。AF_INET 表示 IPv4 地址,例如 127.0.0.1;AF_INET6 表示 IPv6 地址,例如 1030::C9B4:FF12:48AA:1A2B。

type 为数据传输方式/套接字类型,常用的有 SOCK_STREAM 和 SOCK_DGRAM

protocol 表示传输协议,常用的有 IPPROTO_TCP 和 IPPTOTO_UDP,分别表示 TCP 传输协议和 UDP 传输协议。当protocol为0时,会自动选择type类型对应的默认协议。

sockaddr
通用套接字 sockaddr 类型定义:
typedef unsigned short int sa_family_t;

struct sockaddr {
sa_family_t sa_family; /* 2 bytes address family, AF_xxx /
char sa_data[14]; /
14 bytes of protocol address */
}

在这里插入图片描述

ipv4对应的是sockaddr_in类型定义:
typedef unsigned short sa_family_t;
typedef uint16_t in_port_t;

struct in_addr {
uint32_t s_addr;
};

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*/
/* Pad to size of `struct sockaddr’. /
unsigned char sin_zero[8]; /
8 bytes unused padding data, always set be zero */
};
在这里插入图片描述

2.htons()

在将一个地址绑定到socket的时候,先将主机字节序转换成为网络字节序,

serv_addr.sin_port = htons(LISTEN_PORT);
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);

3.inet_aton()

将一个字符串IP地址转换为一个32位的网络序列IP地址
int inet_aton(const char string, struct in_addraddr);
参数描述:
1 输入参数string包含ASCII表示的IP地址。
2 输出参数addr是将要用新的IP地址更新的结构。
返回值:
如果这个函数成功,函数的返回值非零,如果输入地址不正确则会返回零。使用这个函数并没有错误码存放在errno中,所以它的值会被忽略。

4.connect()和bind()

int connect(int sock, struct sockaddr *serv_addr, socklen_t addrlen);
sock 为 socket 文件描述符,serv_addr为 sockaddr 结构体变量的指针,addrlen 为 addr 变量的大小,可由 sizeof() 计算得出

int bind(int sock, struct sockaddr *addr, socklen_t addrlen);
sock 为 socket 文件描述符,addr 为 sockaddr 结构体变量的指针,addrlen 为 addr 变量的大小,可由 sizeof() 计算得出

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值