TCP之套接字socket编程

一、socket套接字

“IP地址 + 端口号 ”就称为socket。在TCP协议里,建立连接的两个进程各自有一个socket标识,这两个socket pair就唯一标识一个连接,描述网络连接的一对一关系。

Linux的基本哲学就是“一切皆文件”,其实socket就是一种特殊的文件,是“open—write/read—close”模式的一种实现。

二、socket的基本操作

1.socket()函数

socket函数对应于普通文件的打开操作。普通文件的打开操作返回一个文件描述字,而socket()用于创建一个socket描述符,它唯一标识一个socket,把它作为参数,通过它来进行一些读写操作。

当我们调用socket创建一个socket时,返回的socket描述字它存在于协议族空间中,但没有一个具体的地址。如果想要给它赋值一个地址,就必须调用bind()函数,否则就当调用connect()、listen()时系统会自动随机分配一个端口。

int socket(int domain, int type, int protocol);

domain:即协议域(协议族)。常用的协议族有,AF_INET、AF_INET6等等。协议族决定了socket的地址类型,在通信中必须采用对应的地址,如AF_INET决定了要用ipv4地址(32位)与端口号(16位)的组合。

type:指定socket类型,这里使用SOCK_STREAM。

protocol:指定协议。当proyocol为0时,会自动选择type类型对应的默认协议。

2.bind()函数

bind()函数把一个地址族中的特定地址赋给socket,也就是将给描述字绑定一个名字。通常服务器在启动的时候都会绑定一个众所周知的地址(ip地址+端口号),用于提供服务,客户端就可以通过它来接连服务器;但是客户端就不用指定,由系统自动分配一个端口号和自身的ip地址组合。这就是为什么通常服务器端在listen之前会调用bind(),而客户端就不会调用,而是在connect()时由系统随机生成一个。

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

sockfd:即socket描述字,它是通过socket()函数创建了唯一标识一个socket。

addr:一个const struct sockaddr *指针,指向要绑定给sockfd的协议地址。

addrlen:地址的长度。

这个地址结构根据地址创建socket时的地址协议族的不同而不同。

ipv4对应的是:

struct sockaddr_in 
{
    sa_family_t    sin_family; /* address family: AF_INET */
    in_port_t      sin_port;   /* port in network byte order */
    struct in_addr sin_addr;   /* internet address */
};/* Internet address. */

struct in_addr 
{
    uint32_t       s_addr;     /* address in network byte order */
};

注意:网络字节序。机器中数据的存储方式有大端(高位存在低地址,低位存在高地址)和小端(高位存在高地址,低位存在低地址)之分,同理,对于网络中传输的数据流同样有大小端之分,因此TCP/IP协议规定:网络数据流应采用大端字节序,即低地址高字节。

3.listen()、connect()函数

如果作为一个服务器,在调用socket()、bind()之后就会调用listen()来监听这个socket,如果客户端这时调用connect()发出连接请求,服务器端就会接收到这个请求(建立与TCP服务器的连接)。

socket()函数创建的socket默认是一个主动类型的,listen()函数将socket变为被动类型的,等待客户的连接请求。

int listen(int sockfd, int backlog);

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

listen()函数

sockfd:为要监听的socket描述字

backlog:为相应socket可以排队的最大连接个数

connect()函数

sockfd:为客户端的socket描述字

addr:为服务器的socket地址

addrlen:为socket地址的长度。

4.accept()函数

服务器端依次调用socket()、bind()、listen()之后,就会监听指定的socket地址了。客户端依次调用socket()、connect()之后就向服务器发送了一个连接请求。服务器监听到这个请求之后,就会调用accept()函数取接收请求,这样连接就建立好了。

如果accpet成功,那么其返回值是由内核自动生成的一个全新的描述字,代表与返回客户的TCP连接。

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

sockfd:为服务器的socket描述字

addr:为指向struct sockaddr *的指针,用于返回客户端的协议地址

addrlen:为协议地址的长度。

注意:accept的第一个参数为服务器的socket描述字,是服务器开始调用socket()函数生成的,称为监听socket描述字;而accept函数返回的是已连接的socket描述字。一个服务器通常通常仅仅只创建一个监听socket描述字,它在该服务器的生命周期内一直存在。内核为每个由服务器进程接受的客户连接创建了一个已连接socket描述字,当服务器完成了对某个客户的服务,相应的已连接socket描述字就被关闭。

三、编码实现

服务器端 server.c

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

#define _BACKLOG_ 5

static void usage(const char* proc)
{
	printf("Usage:%s [ip] [port]\n",proc);
}

int main(int argc,char* argv[])
{
	if(argc != 3)
	{
		usage(argv[0]);
		return 1;
	}
	int size;
	int len;
	char* client_ip;
	int client_port;

	char* ip = argv[1];
	int port =atoi(argv[2]);
	int listenfd = socket(AF_INET, SOCK_STREAM, 0 );
	if(listenfd < 0)
	{
		perror("socket");
		return 2;
	}

	struct sockaddr_in local;
	local.sin_family = AF_INET;
	local.sin_port = htons(port);
	local.sin_addr.s_addr =inet_addr(ip);
	if(bind(listenfd, (struct sockaddr*)&local, sizeof(local)) < 0)
	{
		perror("bind");
		return 3;
	}

	if(listen(listenfd, _BACKLOG_) < 0)
	{
		perror("listen");
		return 4;
	}

	printf("bind and listen success,wait accept...\n");

	while(1)
	{
		struct sockaddr_in client;
		len = sizeof(client);
		client_ip = inet_ntoa(client.sin_addr);
		client_port = ntohs(client.sin_port);

		int socket = accept(listenfd,(struct sockaddr*)&client, &len);
		if(socket < 0)
		{
			perror("accept");
			continue;
		}

		char buf[1024];
		while(1)
		{
			memset(buf, ‘\0‘, sizeof(buf));
			size = read(socket, buf, sizeof(buf)-1);

			if(size == 0)
			{
				printf("client closed...");
				return 6;
			}
			else if(size > 0)
			{
				printf("[client_ip:%s] [client port:%d]:# %s\n",client_ip,client_port,buf);
				continue;
			}
			else
			{
				perror("read");
				break;
			}
		}
	}
	return 0;
}

客户端 client.c

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

static void usage(char* proc)
{
	printf("%s [ip] [port]\n", proc);
}

int main(int argc, char* argv[])
{
	if(argc != 3)
	{
		usage(argv[0]);
		return 1;
	}

	char* ip = argv[1];
	int port = atoi(argv[2]);

	int sock = socket(AF_INET, SOCK_STREAM, 0);
	if(sock < 0)
	{
		perror("socket");
		return 2;
	}

	struct sockaddr_in server;
	server.sin_family = AF_INET;
	server.sin_port = htons(port);
	server.sin_addr.s_addr = inet_addr(ip);

	if(connect(sock, (struct sockaddr*)&server, sizeof(server)) < 0)
	{
		perror("connect");
		return 3;
	}

	char buf[1024];
	while(1)
	{
		memset(buf, ‘\0‘, sizeof(buf));
		gets(buf);
		if(write(sock, buf, sizeof(buf)) < 0)
		{
			perror("write");
			continue;
		}
	}
	return 0;
}

结果:

时间: 2024-10-13 14:23:12

TCP之套接字socket编程的相关文章

Node.js开发入门—套接字(socket)编程

Node.js的net模块提供了socket编程接口,方便我们利用较为底层的套接字接口来实现应用协议.这次我们看一个简单的回显服务器示例,包括服务端和客户端的代码. 使用JavaScript也可以进行套接字编程,哈哈,这酸爽! 代码 分服务器和客户端两部分来说吧. echoServer代码分析 echoServer.js: var net = require("net"); // server is an instance of net.Server // sock is an ins

多客户登录(基于TCP的流式套接字Socket编程)

1.序列化对象 package com.ljb.app.socket; import java.io.Serializable; /**  * 用户类(实现序列化)  * @author LJB  * @version 2015年3月12日  */ public class User implements Serializable{  private String name;  private String password;    public String getName() {   ret

[python] 网络编程之套接字Socket、TCP和UDP通信实例

很早以前研究过C#和C++的网络通信,参考我的文章: C#网络编程之Tcp实现客户端和服务器聊天 C#网络编程之套接字编程基础知识 C#网络编程之使用Socket类Send.Receive方法的同步通讯 Python网络编程也类似.同时最近找工作笔试面试考察Socket套接字.TCP\UDP区别比较多,所以这篇文章主要精简了<Python核心编程(第二版)>第16章内容.内容包括:服务器和客户端架构.套接字Socket.TCP\UDP通信实例和常见笔试考题. 最后希望文章对你有所帮助,如果有不

专题七.网络编程之套接字Socket、TCP和UDP通信实例

https://blog.csdn.net/Eastmount/article/details/48909861 找工作笔试面试考察Socket套接字.TCP\UDP区别比较多,所以这篇文章主要精简了<Python核心编程(第二版)>第16章内容.内容包括:服务器和客户端架构.套接字Socket.TCP\UDP通信实例和常见笔试考题. https://www.cnblogs.com/alex3714/articles/5227251.html 1.Socket语法及相关 2.SocketSer

【Java】Java网络编程菜鸟进阶:TCP和套接字入门

Java网络编程菜鸟进阶:TCP和套接字入门 JDK 提供了对 TCP(Transmission Control Protocol,传输控制协议)和 UDP(User Datagram Protocol,用户数据报协议)这两个数据传输协议的支持.本文开始探讨 TCP. TCP 基础知识 在“服务器-客户端”这种架构中,服务器和客户端各自维护一个端点,两个端点需要通过网络进行数据交换.TCP 为这种需求提供了一种可靠的流式连接,流式的意思是传出和收到的数据都是连续的字节,没有对数据量进行大小限制.

网络编程 套接字socket 及 粘包

网络编程 套接字socket 及 粘包 sockt 初识 五层协议 : 从传输层包括传输层以下 , 都是操作系统帮我们封装的各种head socket套接字充当的就是内置模块的角色 socket 套接字,它存在于传输层与应用层之间的抽象层 避免你学习各层的接口以及协议的使用, socket已经封装好了所有的接口 . 直接使用这些接口或者方法即可 , 使用起来方便,提升开发效率 socket 就是一个模块 , 通过使用学习模块提供的功能 , 建立客户端与服务端的通信 套接字的工作流程(基于TCP和

网络编程之套接字socket

目录 socket套接字 引子 为何学习socket一定要先学习互联网协议 socket是什么 套接字类型 基于文件类型的套接字家族 基于网络类型的套接字家族 套接字工作流程 基于TCP的套接字 简单通信 加上链接循环与通信循环 基于UDP的套接字 UDP的套接字下的简单通信 UDP协议支持并发 粘包现象 什么是粘包 两种情况下会发生粘包 解决粘包问题的处理方法 简单方法(不推荐使用) 牛逼方法(利用struct模块打包报头) socketserver模块(实现并发) socketserver模

基于TCP的客户端、服务器端socket编程

一.实验目的 理解tcp传输客户端服务器端通信流程 二.实验平台 MAC OS 三.实验内容 编写TCP服务器套接字程序,程序运行时服务器等待客户的连接,一旦连接成功,则显示客户的IP地址.端口号,并向客户端发送字符串. 四.实验原理 使用TCP套接字编程可以实现基于TCP/IP协议的面向连接的通信,它分为服务器端和客户端两部分,其主要实现过程如下 服务器端代码: 1 #include "iostream" 2 #include "netdb.h" 3 #inclu

套接字—Socket

网络编程就不得不提大名鼎鼎的套接字-Socket 一,什么是Socket 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个Socket.Socket的英文原意是"插座",通常称之为套接字,来描述IP地址和端口,是一个通信链的句柄,用来实现不同虚拟机或者计算机之间的通信. 在Internet上的主机一般运行了多个服务软件,同时提供几种服务.每种服务都打开一个Socket,并绑定到一个端口上,与不同客户端的不同服务对应着不同的Socket,这样实现了与多个服务器