socket通信原理c语言,Socket通信原理

原标题:Socket通信原理

一、Socket通信简介

Android与服务器的通信方式主要有两种:

Http通信

Socket通信

两者的最大差异在于:

Http连接使用的是“请求-响应方式”,即在请求时建立连接通道,当客户端向服务器发送请求后,服务端才能向客户端返回数据。

Socket通信则是在双方建立连接后,可以直接进行数据的传输,在连接时可实现信息的主动推送,而不需要每次由客户端向服务器发送请求。

那么,什么是socket?

socket又称套接字,在程序内部提供了与外界通信的端口,即端口通信。

通过建立socket连接,可为通信双方的数据传输提供通道。socket的主要特点有数据丢失率低,使用简单且易于移植。

1、什么是Socket

socket是一种抽象层,应用程序通过它来发送和接受数据,使用Socket可以将应用程序添加到网络中,与处于同一网络中的其他应用程序进行通信。

简单来说,Socket提供了程序内部与外界通信的端口并为通信双方提供数据传输通道。

2、Socket分类

根据不同的底层协议,Socket的实现是多样化的。在这主要介绍TCP/IP协议簇当中主要的Socket类型为流套接字(streamsocket)和数据报套接字(datagramsocket)。

流套接字将TCP作为其端对端协议,提供了一个可信赖的字节流服务。

数据报嵌套字使用UDP协议,提供数据打包发送数据。

二、Socket基本通信模型

8b5fd9950d6086749f39cc326f1211c8.png

1、TCP通信模型

684673f6a0836589952607146e0dda17.png

2、UDP通信模型

620408995ff8b2cac513964a5b626b29.png

三、Socket基本实现原理1、基于TCP协议的Socket

服务端首先声明一个ServerSocket对象并且指定端口号,然后调用Serversocket的accept方法接受客户端的数据。

accept方法在没有数据进行接受时处于堵塞状态。(Socket socket = serversocket.accept),一旦接受数据,通过inputstream读取接受的数据。

客户端创建一个Socket对象,执行服务器端的ip地址和端口号(Socket socket = new Socket("172.168.10.108", 8080);),通过inputstream读取数据,获取服务器发出的数据(OutputStream outputstream = socket.getOutputStream;),最后将要发送的数据写入到outputstream即可进行TCP协议的socket数据传输。

2、基于UDP协议的数据传输

服务器端首先创建一个DatagramSocket对象,并且指定监听端口。接下来创建一个空的DatagramSocket对象用于接收数据(byte data[] = new byte[1024]; DatagramSocket packet = new DatagramSocket(data, data.length);),使用DatagramSocket的receive方法接受客户端发送的数据,receive与serversocket的accept方法类似,在没有数据进行接受时处于堵塞状态。

客户端也创建个DatagramSocket对象,并且指定监听的端口。接下来创建一个InetAddress对象,这个对象类似于一个网络的发送地址(InetAddress serveraddress = InetAddress.getByName("172.168.1.120"))。定义要发送的一个字符串,创建一个DatagramPacket对象,并指定要将该数据包发送到网络对应的那个地址和端口号,最后使用DatagramSocket的对象的send发送数据。

( Stringstr = "hello"; bytedata[] = str.getByte; DatagramPacket packet = newDatagramPacket(data, data.length, serveraddress, 4567); socket.send(packet);)

四、android实现socket简单通信1、使用TCP协议通信

android端实现:

protected voidconnectServerWithTCPSocket {

Socket socket;

try{ // 创建一个Socket对象,并指定服务端的IP及端口号

socket = newSocket( "192.168.1.32", 1989

// 创建一个InputStream用户读取要发送的文件。

InputStream inputStream = newFileInputStream( "e://a.txt");

// 获取Socket的OutputStream对象用于发送数据。

OutputStream outputStream = socket.getOutputStream;

// 创建一个byte类型的buffer字节数组,用于存放读取的本地文件

byte buffer[] = newbyte[ 4* 1024];

inttemp = 0;

// 循环读取文件

while((temp = inputStream.read(buffer)) != -1

// 把数据写入到OuputStream对象中

outputStream.write(buffer, 0, temp);

}

// 发送读取的数据到服务端

outputStream.flush;

/ ** 或创建一个报文,使用BufferedWriter写入,看你的需求 **/

// String socketData = "[2143213;21343fjks;213]";

// BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(

// socket.getOutputStream));

// writer.write(socketData.replace("n", " ") + "n");

// writer.flush;

/ ************************************************/

} catch(UnknownHostException e) {

e.printStackTrace;

} catch(IOException e) {

e.printStackTrace;

}

}

服务器端简单实现

publicvoidServerReceviedByTcp( ){

// 声明一个ServerSocket对象

ServerSocket serverSocket = null;

try{

// 创建一个ServerSocket对象,并让这个Socket在1989端口监听

serverSocket = newServerSocket( 1989);

// 调用ServerSocket的accept方法,接受客户端所发送的请求,

// 如果客户端没有发送数据,那么该线程就停滞不继续

Socket socket = serverSocket.accept;

// 从Socket当中得到InputStream对象

InputStream inputStream = socket.getInputStream;

bytebuffer[] = newbyte[ 1024* 4];

inttemp = 0;

// 从InputStream当中读取客户端所发送的数据

while((temp = inputStream.read(buffer)) != -1) {

System. out.println( newString(buffer, 0, temp));

}

serverSocket.close;

} catch(IOException e) {

e.printStackTrace;

}

}

2、使用UDP协议通信

客户端发送数据实现:

protectedvoidconnectServerWithUDPSocket( ){

DatagramSocket socket;

try{

//创建DatagramSocket对象并指定一个端口号,注意,如果客户端需要接收服务器的返回数据,

//还需要使用这个端口号来receive,所以一定要记住

socket = newDatagramSocket( 1985);

//使用InetAddress(Inet4Address).getByName把IP地址转换为网络地址

InetAddress serverAddress = InetAddress.getByName( "192.168.1.32");

//Inet4Address serverAddress = (Inet4Address) Inet4Address.getByName("192.168.1.32");

String str = "[2143213;21343fjks;213]"; //设置要发送的报文

bytedata[] = str.getBytes; //把字符串str字符串转换为字节数组

//创建一个DatagramPacket对象,用于发送数据。

//参数一:要发送的数据 参数二:数据的长度

//参数三:服务端的网络地址 参数四:服务器端端口号

DatagramPacket packet = newDatagramPacket(data, data.length ,serverAddress , 10025

socket.send(packet); //把数据发送到服务端。

} catch(SocketException e) {

e.printStackTrace;

} catch(UnknownHostException e) {

e.printStackTrace;

} catch(IOException e) {

e.printStackTrace;

}

}

客户端接收服务器返回的数据

publicvoidReceiveServerSocketData( ){

DatagramSocket socket;

try{

//实例化的端口号要和发送时的socket一致,否则收不到data

socket = newDatagramSocket( 1985);

bytedata[] = newbyte[ 4* 1024];

//参数一:要接受的data 参数二:data的长度

DatagramPacket packet = newDatagramPacket(data, data.length);

socket.receive(packet);

//把接收到的data转换为String字符串

String result = newString(packet.getData, packet.getOffset,

packet.getLength);

socket.close; //不使用了记得要关闭

System. out.println( "the number of reveived Socket is :"+ flag

+ "udpData:"+ result);

} catch(SocketException e) {

e.printStackTrace;

} catch(IOException e) {

e.printStackTrace;

}

}

服务器接收客户端实现:

publicvoidServerReceviedByUdp( ){

//创建一个DatagramSocket对象,并指定监听端口。(UDP使用DatagramSocket)

DatagramSocket socket;

try{

socket = newDatagramSocket( 10025);

//创建一个byte类型的数组,用于存放接收到得数据

bytedata[] = newbyte[ 4* 1024];

//创建一个DatagramPacket对象,并指定DatagramPacket对象的大小

DatagramPacket packet = newDatagramPacket(data,data.length);

//读取接收到得数据

socket.receive(packet);

//把客户端发送的数据转换为字符串。

//使用三个参数的String方法。参数一:数据包 参数二:起始位置 参数三:数据包长

String result = newString(packet.getData,packet.getOffset ,packet.getLength);

} catch(SocketException e) {

e.printStackTrace;

} catch(IOException e) {

e.printStackTrace;

}

}

五、总结

使用UDP方式,android端和服务器端接收可以看出,其实android端和服务器端的发送和接受大相径庭,只要端口号正确,相互通信就没有问题,TCP使用的是流的方式发送,UDP是以包的形式发送。

补充1、ServerSocket.accept方法底层源码

查看这部分代码主要是为了查看accept底层源码实现阻塞等待的原理。

publicSocket acceptthrowsIOException{

if(isClosed)

thrownewSocketException( "Socket is closed");

if(!isBound)

thrownewSocketException( "Socket is not bound yet");

Socket s = newSocket((SocketImpl) null);

implAccept(s);

returns;

}

在accept方法中调用implAccept方法

protectedfinalvoidimplAccept(Socket s)throwsIOException{

SocketImpl si = null;

try{

if(s.impl == null)

s.setImpl;

else{

s.impl.reset;

}

si = s.impl;

s.impl = null;

si.address = newInetAddress;

si.fd = newFileDeor;

//核心代码

getImpl.accept(si);

......

} catch(IOException e) {

......

}

s.impl = si;

s.postAccept;

}

而后调用PlainSocketImpl类中的accept方法

protectedsynchronized voidaccept( SocketImpl s) throws IOException{

if(s instanceof PlainSocketImpl) {

// pass in the real impl not the wrapper.

SocketImpl delegate= ((PlainSocketImpl)s).impl;

delegate.address = newInetAddress;

delegate.fd = newFileDeor;

// 对应代码

impl.accept( delegate);

// set fd to delegate's fd to be compatible with older releases

s.fd = delegate.fd;

} else{

// 对应代码

impl.accept(s);

}

}

此处再调用抽象类abstracPlainSocketImpl类中的accept方法

protectedvoidaccept(SocketImpl s)throwsIOException{

acquireFD;

try{

socketAccept(s);

} finally{

releaseFD;

}

}

其中acquireFD方法的代码如下:

/*

* "Acquires" and returns the FileDeor for this impl

*

* A corresponding releaseFD is required to "release" the

* FileDeor.

*/

//“获取”并返回这个impl的文件描述符需要一个相应的releaseFD来“释放”文件描述符。

FileDeor acquireFD{

synchronized(fdLock) {

fdUseCount++;

returnfd;

}

}

而后再执行socketAccept方法

voidsocketAccept( SocketImpl s) throws IOException{

intnativefd = checkAndReturnNativeFD;

if(s == null)

thrownewNullPointerException( "socket is null");

intnewfd = -1;

InetSocketAddress[] isaa = newInetSocketAddress[ 1];

//等待阻塞代码

if(timeout <= 0) {

newfd = accept0(nativefd, isaa);

} else{

configureBlocking(nativefd, false);

try{

waitForNewConnection(nativefd, timeout);

newfd = accept0(nativefd, isaa);

if(newfd != -1) {

configureBlocking(newfd, true);

}

} finally{

configureBlocking(nativefd, true);

}

}

/* Update (SocketImpl)s' fd '*/

fdAccess. set(s.fd, newfd);

/* Update socketImpls remote port, address and localport */

InetSocketAddress isa = isaa[ 0];

s.port = isa.getPort;

s.address = isa.getAddress;

s.localport = localport;

}

该部分即为无请求时的阻塞代码块,逐一查看accetp0方法、configureBlocking方法等发现这些代码使用native代码实现,提高效率。

因而具体没有找到accept方法的阻塞机理

个人感觉,configureBlocking方法时等待阻塞的调用的方法,而accept0方法是响应请求的方法。

(该部分纯属个人猜测,可能是错误的,因而希望大家指教)2、OutputStream.flush方法底层源码

flush方法很简单,就是刷新此输出流并强制写出任何已缓冲的输出字节

/**

* Flushes this output stream and forces any buffered output bytes

* to be written out. The general contract of flush is

* that calling it is an indication that, if any bytes previously

* written have been buffered by the implementation of the output

* stream, such bytes should immediately be written to their

* intended destination.

*

* If the intended destination of this stream is an abstraction provided by

* the underlying operating system, for example a file, then flushing the

* stream guarantees only that bytes previously written to the stream are

* passed to the operating system for writing; it does not guarantee that

* they are actually written to a physical device such as a disk drive.

*

* The flush method of OutputStream does nothing.

*

* @exceptionIOException if an I/O error occurs.

*/

publicvoidflushthrowsIOException{

}

最后附上Java测试socket,查看其底层源码实现机制,因为一直尝试网络请求没成功,只能debug一步一步查看底层实现机制。

感兴趣可以自己尝试下,查看具体实现原理。

importjava.io.*;

importjava.net.*;

publicclasssocketTest{

publicstaticvoidmain(String[] args)throwsUnknownHostException, IOException{

Socket socket = setSocket;

if(socket == null) System.out.println( "socket is null");

InputStream inputStream = newFileInputStream( "d://haha.txt");

OutputStream outputStream = socket.getOutputStream;

bytebuffer[] = newbyte[ 4* 1024];

inttemp = 0;

while((temp = inputStream.read(buffer)) != - 1) {

outputStream.write(buffer, 0, temp);

}

outputStream.flush;

listener;

}

publicstaticSocket setSocketthrowsIOException{

String ip = "127.0.0.5";

intport = 8080;

Socket socket = newSocket;

//设置最长等待时间

socket.setSoTimeout( 8000);

//进行连接请求

socket.connect( newInetSocketAddress(ip, port));

returnsocket;

}

//服务端监听方法

publicstaticvoidlistenerthrowsIOException{

//正常情况下,此处的"9999"和客户端中的端口号port需要相同,才能完成网络请求,

//因为测试不成功,但是又想查看实现原理,因而才这样做!!!

ServerSocket server = newServerSocket( 9999);

Socket socket = null;

inti = 0;

while( true){

i++;

socket = server.accept; //这也是个阻塞的方法来的

System.out.println( "有"+ i + "个用户连接了服务器");

newThread( newsocketTest.new ServerDoThread(socket)).start;

}

}

classServerDoThreadimplementsRunnable{

Socket socket;

InputStream inputStream;

publicServerDoThread(Socket socket){

this.socket = socket;

try{

this.inputStream = socket.getInputStream;

} catch(IOException e) {

e.printStackTrace;

}

}

@Override

publicvoidrun{

bytebuffer[] = newbyte[ 1024* 4];

inttemp = 0;

try{

while((temp = inputStream.read(buffer)) != - 1) {

System.out.println( newString(buffer, 0, temp));

}

} catch(IOException e) {

// TODO Auto-generated catch block

e.printStackTrace;

}

}

}

}

来源:Socket 通信原理(Android客户端和服务器以TCP&&UDP方式互通)

作者:Agoni_Soul

链接:https://juejin.im/post/5df853d6f265da33e2290113 返回搜狐,查看更多

责任编辑:

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
TCP Socket通信是一种常见的网络通信方式,可以在C语言中使用socket库来实现。下面是一个简单的TCP Socket通信的C语言代码示例: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <arpa/inet.h> #define MAX_BUFFER_SIZE 1024 int main() { int serverSocket, clientSocket; struct sockaddr_in serverAddr, clientAddr; socklen_t addrLen = sizeof(struct sockaddr_in); char buffer[MAX_BUFFER_SIZE]; // 创建服务器端Socket serverSocket = socket(AF_INET, SOCK_STREAM, 0); if (serverSocket == -1) { perror("socket creation failed"); exit(EXIT_FAILURE); } // 设置服务器地址和端口 serverAddr.sin_family = AF_INET; serverAddr.sin_addr.s_addr = INADDR_ANY; serverAddr.sin_port = htons(8080); // 绑定服务器Socket到指定地址和端口 if (bind(serverSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) == -1) { perror("bind failed"); exit(EXIT_FAILURE); } // 监听连接请求 if (listen(serverSocket, 5) == -1) { perror("listen failed"); exit(EXIT_FAILURE); } printf("Server listening on port 8080...\n"); // 接受客户端连接请求 clientSocket = accept(serverSocket, (struct sockaddr *)&clientAddr, &addrLen); if (clientSocket == -1) { perror("accept failed"); exit(EXIT_FAILURE); } printf("Client connected: %s:%d\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port)); // 接收和发送数据 while (1) { // 接收客户端发送的数据 memset(buffer, 0, sizeof(buffer)); if (recv(clientSocket, buffer, sizeof(buffer), 0) == -1) { perror("recv failed"); exit(EXIT_FAILURE); } printf("Received from client: %s\n", buffer); // 发送响应数据给客户端 if (send(clientSocket, buffer, strlen(buffer), 0) == -1) { perror("send failed"); exit(EXIT_FAILURE); } } // 关闭连接 close(clientSocket); close(serverSocket); return 0; } ``` 这段代码实现了一个简单的TCP服务器,它会监听8080端口,并接受客户端的连接请求。一旦连接建立,服务器会接收客户端发送的数据,并将接收到的数据原样发送回客户端。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值