1.从 Linux源码 看 Socket(TCP)的accept
2.求一份unity中Socket-TCP从服务器接收数据方法的代码
3.一文从linux源码看socket的close基本概括
4.从Linux源码看Socket(TCP)的listen及连接队列
5.linux 网络编程Socket(TCP与UDP)
6.å¦ä½ç¼åç®åçsocketç½ç»ç¨åº å¦ä½ç¼ååºäºTCPåè®®çç½ç»ç¨åº
从 Linux源码 看 Socket(TCP)的accept
从 Linux 源码角度探究 Server 端 Socket 的 Accept 过程(基于 Linux 3. 内核),以下是一系列关键步骤的解析。
创建 Server 端 Socket 需依次执行 socket、bind、listen 和 accept 四个步骤。其中,stargan源码分析socket 系统调用创建了一个 SOCK_STREAM 类型的 TCP Socket,其操作函数为 TCP Socket 所对应的 ops。在进行 Accept 时,关键在于理解 Accept 的功能,即创建一个新的 Socket 与对端的 connect Socket 进行连接。
在具体实现中,核心函数 sock->ops->accept 被调用。关注 TCP 实现即 inet_stream_ops->accept,其进一步调用 inet_accept。核心逻辑在于 inet_csk_wait_for_connect,用于管理 Accept 的超时逻辑,避免在超时时惊群现象的发生。
EPOLL 的实现中,"惊群"现象是由水平触发模式下 epoll_wait 重新塞回 ready_list 并唤醒多个等待进程导致的。虽然 epoll_wait 自身在有中断事件触发时不惊群,但水平触发机制仍会造成类似惊群的opendpi 源码效应。解决此问题,通常采用单线程专门处理 accept,如 Reactor 模式。
针对"惊群"问题,Linux 提供了 so_reuseport 参数,允许多个 fd 监听同一端口号,内核中进行负载均衡(Sharding),将 accept 任务分散到不同 Socket 上。这样,可以有效利用多核能力,提升 Socket 分发能力,且线程模型可改为多线程 accept。
在 accept 过程中,accept_queue 是关键成员,用于填充添加待处理的连接。用户线程通过 accept 系统调用从队列中获取对应的 fd。值得注意的是,当用户线程未能及时处理时,内核可能会丢弃三次握手成功的连接,导致某些意外现象。
综上所述,spout源码理解 Linux Socket 的 Accept 过程需要深入源码,关注核心函数与机制,以便优化 Server 端性能,并有效解决"惊群"等问题,提升系统处理能力。
求一份unity中Socket-TCP从服务器接收数据方法的代码
接收的字节使用了protubuf反序列化,处理的时候要注意和服务器发送消息类型、大小定义一致。如果不需要可以直接删除,用服务器发送字符串也是一样
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Net.Sockets;
using System;
using UnityEngine.Networking;
using System.Text;
using Google.Protobuf;
using Pb;
using System.Net;
using System.IO;
namespace Net
{
public class SocketClient
{
#region Public Variables
public static SocketClient Instance { get; private set; }
[Header("Network")]
public string ipAdress = ".0.0.1";
public int port = ;
public float waitingMessagesFrequency = 1;
public bool loggedIn { get; private set; }
public bool FullLog = true;
#endregion
#region Private m_Variables
private TcpClient m_Client;
private NetworkStream m_NetStream = null;
private byte[] m_Buffer = new byte[];
private NetworkStream m_OutStream;
[Tooltip("This value should be >= to Server waitingMessagesFrequency")]
[Min(0)] private float m_DelayedCloseTime = 0.5f;
#endregion
#region Delegate Variables
protected Action OnClientStarted = null; //Delegate triggered when client start
protected Action OnClientClosed = null; //Delegate triggered when client close
#endregion
private void Awake()
{
if (Instance == null)
Instance = this;
}
//Start client and stablish connection with server
public void StartClient()
{
//Early out
if (m_Client != null)
{
ClientLogError($"There is already a runing client on { ipAdress}::{ port}");
return;
}
try
{
//Create new client
m_Client = new TcpClient();
//Set and enable client
m_Client.BeginConnect(ipAdress, port, new AsyncCallback(OnConnect), null);
ClientLogInfo($"Client Started on { ipAdress}::{ port}");
OnClientStarted?.Invoke();
}
catch (SocketException)
{
ClientLogError("Socket Exception: Start Server first");
OnClose();
}
}
private void OnConnect(IAsyncResult asr)
{
ClientLogInfo("Connect Sucessful.");
m_NetStream = m_Client.GetStream();
m_Client.GetStream().BeginRead(m_Buffer, 0, m_Buffer.Length, new AsyncCallback(OnRead), m_Client);
}
#region Receive Message
private void OnRead(IAsyncResult result)
{
OnReceivedMessage(m_Buffer);
NetworkStream stream = m_Client.GetStream();
lock (stream)
{
Array.Clear(m_Buffer, 0, m_Buffer.Length);
m_Client.GetStream().BeginRead(m_Buffer, 0, m_Buffer.Length, new AsyncCallback(OnRead), m_Client);
}
}
private void OnReceivedMessage(byte[] bytes)
{
ByteBuffer buffer = new ByteBuffer(bytes);
OnRecieveMessageDeal(buffer, 0);
}
private void OnRecieveMessageDeal(ByteBuffer buffer, ushort length = 0)
{
// 判断传参length是否为0,如果不为0则代表非首次调用,不再取length值而使用传递的length
ushort nextLength;
if(length != 0)
{
nextLength = length;
}
else
{ // 判断传参length是否为0,如果为0则为首次调用,直接取出length后进行处理
nextLength = buffer.ReadUInt();
}
uint pId = buffer.ReadUInt();
ClientLogInfo("Length:" + nextLength + ".id:" + pId);
byte[] bytes = buffer.ReadBytes(nextLength);
NetLogic(pId, bytes);
// 取出下一个length,如果为0则没有数据了,不为0则递归调用,并且传递已经取出的长度值
nextLength = buffer.ReadUInt();
if (nextLength != 0)
{
OnRecieveMessageDeal(buffer, nextLength);
}
}
#endregion
#region Process
private void NetLogic(uint pid, byte[] bytes)
{
ClientLogInfo("Get Msg Id :" + pid);
if (pid == 1)
{
SyncPid syncPid = SyncPid.Parser.ParseFrom(bytes);
ClientLogInfo("sync pid:"+syncPid.Pid);
}
if (pid == )
{
BroadCast broadCast = BroadCast.Parser.ParseFrom(bytes);
ClientLogInfo("broadCast-pid:" + broadCast.Pid);
ClientLogInfo("broadCast-Tp:" + broadCast.Tp);
ClientLogInfo("broadCast-Position-x:" + broadCast.P.X);
ClientLogInfo("broadCast-Position-y:" + broadCast.P.Y);
ClientLogInfo("broadCast-Position-z:" + broadCast.P.Z);
ClientLogInfo("broadCast-Position-v:" + broadCast.P.V);
}
}
#endregion
#region Send Message
private void WriteMessage(byte[] message)
{
MemoryStream memoryStream2;
MemoryStream memoryStream = memoryStream2 = new MemoryStream();
try
{
memoryStream.Position = 0L;
BinaryWriter binaryWriter = new BinaryWriter(memoryStream);
ushort num = (ushort)message.Length;
binaryWriter.Write(message);
binaryWriter.Flush();
if (m_Client != null && this.m_Client.Connected)
{
byte[] array = memoryStream.ToArray();
m_OutStream.BeginWrite(array, 0, array.Length, new AsyncCallback(OnWrite), null);
}
else
{
ClientLogError("client.connected----->>false");
}
}
finally
{
if (memoryStream2 != null)
{
((IDisposable)memoryStream2).Dispose();
}
}
}
private void OnWrite(IAsyncResult r)
{
try
{
m_OutStream.EndWrite(r);
}
catch (Exception ex)
{
ClientLogError("OnWrite--->>>" + ex.Message);
}
}
public void SendMessage(ByteBuffer buffer)
{
this.SessionSend(buffer.ToBytes());
buffer.Close();
}
private void SessionSend(byte[] bytes)
{
this.WriteMessage(bytes);
}
#endregion
#region Close Client
//Close client connection
public void Close()
{
if (m_Client != null)
{
if (m_Client.Connected)
{
m_Client.Close();
}
m_Client = null;
}
loggedIn = false;
}
public void OnClose()
{
ClientLogError("Client Closed");
//Reset everything to defaults
if (m_Client.Connected)
m_Client.Close();
if (m_Client != null)
m_Client = null;
OnClientClosed?.Invoke();
}
#endregion
#region ClientLog
// Custom Client Log With Text Color
public void ClientLogInfo(string msg)
{
if (FullLog)
{
Debug.Log($"<color=green>Client:</color>{ msg}");
}
}
public void ClientLogWarning(string msg)
{
if (FullLog)
{
Debug.LogWarning($"<color=yellow>Client:</color>{ msg}");
}
}
public void ClientLogError(string msg)
{
if (FullLog)
{
Debug.LogError($"<color=red>Client:</color>{ msg}");
}
}
//Custom Client Log Without Text Color
public void ClientLog(string msg)
{
if (FullLog)
{
Debug.Log($"Client:{ msg}");
}
}
#endregion
}
}
一文从linux源码看socket的close基本概括
理解TCP关闭过程的关键在于四次挥手,这个过程是主动关闭、被动关闭和同时关闭的统一体现。在主动关闭close(fd)的过程中,通过C语言中的runonuithread 源码close(int fd)函数调用系统调用sys_close,进而执行filp_close方法。随后,fput函数处理多进程中的socket引用问题,确保父进程也正确关闭socket。在f_op->release的实现中,我们关注socket与file的关系以及close(fd)调用链。随着状态机的变迁,TCP从FIN_WAIT1变迁至FIN_WAIT2,设置一个TCP_FIN_WAIT2定时器,防止由于对端未回应导致的长时间等待。FIN_WAIT2状态等待对端的FIN,完成最后两次挥手。接收对端FIN后,状态变化至time_wait,原socket资源被回收,并在时间等待超时后从系统中清除。在被动关闭中,接收FIN进入close_wait状态,应用关闭连接时改变状态为last_ack,并发送本端的FIN。被动关闭的cbuttonst 源码后两次挥手后,连接关闭。出现大量close_wait通常与应用检测到对端FIN时未及时关闭有关,解决方法包括调整连接池的参数或加入心跳检测。操作系统通过包活定时器在超时后强制关闭连接。进程退出时会关闭所有文件描述符,再次触发filp_close函数。在Java中,通过重写finalize方法,GC会在释放内存时关闭未被引用的socket,但不可完全依赖GC来管理socket资源,以避免潜在的内存泄露问题。总结,深入理解TCP关闭过程有助于优化网络应用程序的性能和稳定性,同时阅读Linux内核源代码需要耐心和系统性的方法。
从Linux源码看Socket(TCP)的listen及连接队列
了解Linux内核中Socket (TCP)的"listen"及连接队列机制是深入理解网络编程的关键。本文将基于Linux 3.内核版本,从源码角度解析Server端Socket在进行"listen"时的具体实现。
建立Server端Socket需要经历socket、bind、listen、accept四个步骤。本文聚焦于"listen"步骤,深入探讨其内部机理。
通过socket系统调用,我们可以创建一个基于TCP的Socket。这里直接展示了与TCP Socket相关联的操作函数。
接着,我们深入到"listen"系统调用。注意,glibc的INLINE_SYSCALL对返回值进行了封装,仅保留0和-1两种结果,并将错误码的绝对值记录在errno中。其中,backlog参数至关重要,设置不当会引入隐蔽的陷阱。对于Java开发者而言,框架默认backlog值较小(默认),这可能导致微妙的行为差异。
进入内核源码栈,我们发现内核对backlog值进行了调整,限制其不超过内核参数设置的somaxconn值。
核心调用程序为inet_listen。其中,除了fastopen外的逻辑(fastopen将在单独章节深入讨论)最终调用inet_csk_listen_start,将sock链入全局的listen hash表,实现对SYN包的高效处理。
值得注意的是,SO_REUSEPORT特性允许不同Socket监听同一端口,实现内核级的负载均衡。Nginx 1.9.1版本启用此功能后,性能提升3倍。
半连接队列与全连接队列是连接处理中的关键组件。通常提及的sync_queue与accept_queue并非全貌,sync_queue实际上是syn_table,而全连接队列为icsk_accept_queue。在三次握手过程中,这两个队列分别承担着不同角色。
在连接处理中,除了qlen与sk_ack_backlog计数器外,qlen_young计数器用于特定场景下的统计。SYN_ACK的重传定时器在内核中以ms为间隔运行,确保连接建立过程的稳定。
半连接队列的存在是为抵御半连接攻击,避免消耗大量内存资源。通过syn_cookie机制,内核能有效防御此类攻击。
全连接队列的最大长度受到限制,超过somaxconn值的连接会被内核丢弃。若未启用tcp_abort_on_overflow特性,客户端可能在调用时才会察觉到连接被丢弃。启用此特性或增大backlog值是应对这一问题的策略。
backlog参数对半连接队列容量产生影响,导致内核发送cookie校验时出现常见的内存溢出警告。
总结而言,TCP协议在数十年的演进中变得复杂,深入阅读源码成为分析问题的重要途径。本文深入解析了Linux内核中Socket (TCP)的"listen"及连接队列机制,旨在帮助开发者更深入地理解网络编程。
linux 网络编程Socket(TCP与UDP)
TCP/IP协议族主要分为网络层、传输层与应用层。网络层包括IP协议、ICMP协议、ARP协议、RARP协议与BOOTP协议;传输层则有TCP协议与UDP协议;而应用层则有FTP、HTTP、TELNET、SMTP、DNS等协议。
HTTP协议基于请求/响应模型,其底层仍依赖TCP协议。不过,当前有研究探讨基于TCP+UDP混合的HTTP协议。
Socket编程中,基本操作包括:socket()函数用于创建套接字,bind()函数用于绑定套接字与特定地址,listen()和connect()函数分别用于服务器监听连接与客户端建立连接,accept()函数用于接受连接请求,read()与write()等函数用于读取与写入数据。推荐使用recvmsg()/sendmsg()函数,它们是通用的I/O函数,能替代其他函数。
TCP与UDP协议的主要区别在于UDP处理的细节比TCP少。UDP不保证消息被传送到目的地,也不保证数据包的顺序。UDP数据发出去后只能寄希望于抵达。
TCP示例代码中,服务器端使用SOCK_STREAM参数打开TCP连接,客户端示例需要与服务器端交互。
UDP示例代码包含服务器端与客户端,通过UDP套接字进行数据交换。
Linux服务器开发/架构师面试题、学习资料、教学视频与学习路线图,免费分享,有兴趣者可加入学习交流群。
å¦ä½ç¼åç®åçsocketç½ç»ç¨åº å¦ä½ç¼ååºäºTCPåè®®çç½ç»ç¨åº
ããä¸é¢æ¯ä¸ªäººç¨äºä¸ä¸ªåéå·¦å³çæ¶é´ç¼åçç¨åºï¼å¨è¿ç¼åè¿ç¨ä¸ï¼é常éè¦çä¸ç¹å°±æ¯ï¼ è¦ç解 tcpåè®®ç¼åç¨åºçåçï¼å³ç¼åæå¡å¨ç«¯çè¿ç¨ï¼ä»¥åç¼å客æ·ç«¯çè¿ç¨ã åªè¦ææ¡è¿ä¸¤ç¹å°±å¯ä»¥å¾å®¹æç¼ååºæ¥äºï¼ä½æ¯è¦å¿«éç¼ååºè¿ä¸ªç¨åºï¼é£ä¹VC6.0å¼åå·¥å ·éï¼æ好è¦å®è£ ä¸ä¸ªçªèæ件ï¼è¿ä¸ªæ件å¯ä»¥å¿«éæé«ä½ çç¼åç¨åºçæçï¼è¿æä¹è¦å®è£ msdn ææ¡£ï¼è¿æ ·å¨ç¼åè¿ç¨ä¸ï¼éå°å¯¹æ个å½æ°çåæ°æ³ä¸å ¨çæ¶åï¼ä½¿ç¨msdnå°±è½å¿«éå¸®ä½ åå¿äºã åµåµï¼å¦æä½ é£ä¸å¤©å»é¢è¯ä¸å®¶çé¼çå ¬å¸çåï¼å¾æå¯è½å°±æ¯ å¨ç¬è¯å®æä¹åï¼å°±è¦è¿è¡æºè¯äºï¼è¿å°±å®å ¨èæ¥åºä½ ççæ£ç¼ç¨æ°´å¹³äºã è½å¨æçæ¶é´éå®æä¸ä¸ªsocketç½ç»ç¨åºï¼é£ä¹å°±å¯ä»¥ä»¤é¢è¯å®æå°é常满æäºã ä¸è¿ï¼è¿ä¸ªç¨åºï¼è¿æ²¡æè¿æ¥æ°æ®åºï¼ä»¥åå继ç»æäºã
ããå¦æä½ å»é¢è¯ æ·±å³ç§æå é£å®¶ ä¼æè¾¾ éå¢å ¬å¸ç软件工ç¨å¸çåï¼é£ä¹æºè¯é¢ç®å°±æ¯è¿ä¸ªã å½æ¶æå»é¢è¯ï¼é¦å è¿è¡ç¬è¯ï¼é¢è¯å®å¯¹æç¬è¯æ绩æ¯è¾æ»¡æï¼æ以就å«æçä¸æ¥åé¡¿é¥ï¼ä¸åè¿è¡æºè¯ã å½æ¶æåºèå²ä½æ¯Linuxç³»ç»å·¥ç¨å¸Cè¯è¨ï¼ å¯æ¯ç¬è¯é¢ç®ï¼ä¸ä½èæ ¸Cï¼è¿èæ ¸C++ï¼JavaScriptï¼htmlã ææè§å¥½å¥æªï¼å¿éæ³ï¼å¥½åææ¯åºèVC++å¼åé£ä¸ªå²ä½äºã äºæ¯æçå°ä¸åï¼ä»æ¿æ¥æºè¯é¢ç®ä¹æ¶ï¼æçæ£æç½ï¼æç¶æ¯ä»è¦å®ææä»äºVC++å¼åäºï¼é¢ç®å°±æ¯ï¼ç¼ååºäºTCP/IPåè®®ç½ç»ç¨åºï¼å¹¶å®ç°ç®åçè天ç¨åºï¼èä¸è¦è¿æ¥æ°æ®åºã å½æ¶æå失æäºã äºæ¯æå°±æåºï¼æä¸æ³åè¿ä¸ªé¢ç®ï¼å 为ææ¯æ³åºèLinuxç³»ç»Cè¯è¨å¼åçã å°±è¿æ ·å¤±æçèµ°äºã
ããä¸é¢æ¯ä¸ªäººå®å ¨è½è¿è¡ç代ç ï¼
ããæå¡å¨ç«¯æºç ï¼
ãã#include<stdio.h>
#include <Winsock2.h>
#pragma comment (lib,"ws2_.lib")
ããint main()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 2, 2 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
return 0;
}
if ( LOBYTE( wsaData.wVersion ) != 2 ||
HIBYTE( wsaData.wVersion ) != 2 ) {
WSACleanup( );
return 0;
}
ããSOCKET socketServer=socket(AF_INET,SOCK_STREAM,0);
ããSOCKADDR_IN addrServer;
ããaddrServer.sin_family=AF_INET;
ããaddrServer.sin_addr.S_un.S_addr=htonl(INADDR_ANY);
ããaddrServer.sin_port=htons();
ããbind(socketServer, (struct sockaddr *)&addrServer, sizeof(struct sockaddr));
ããlisten(socketServer, 5);
ããSOCKADDR_IN addrClient;
ããint addrLen=sizeof(SOCKADDR_IN);
ããchar sendBuf[];
ããchar recvBuf[];
ããint i=1;
while(1)
{
printf("æå¡å¨ç«¯çå¾ ç¬¬%d个客æ·ç«¯è¿æ¥è¯·æ±...\n", i++);
ããSOCKET newsocketServer=accept(socketServer,(struct sockaddr *)&addrClient, &addrLen);
ããif(newsocketServer!=INVALID_SOCKET)
{
printf("æå¡å¨ç«¯ä¸å®¢æ·ç«¯è¿æ¥æå...\n");
}
ããmemset(sendBuf,0,);
ããsprintf(sendBuf,"Welcome you to come here");
ããsend(newsocketServer, sendBuf, strlen(sendBuf)+1,0);
ããmemset(recvBuf,0,);
ããrecv(newsocketServer,recvBuf,,0);
ããprintf("æå¡å¨ç«¯æ¶å°ä¿¡æ¯:%s\n",recvBuf);
ããclosesocket(newsocketServer);
}
ããWSACleanup();
ããreturn 0;
}
ããæ¤æç« æ¥èªäºä¸ªäººåå®¢ï¼ é¿æµªå客 /wenxianliang@/
ãã客æ·ç«¯æºç ï¼
ãã#include<stdio.h>
#include <Winsock2.h>
#pragma comment (lib,"ws2_.lib")
ããint main()
{
WORD wVersionRequested;
WSADATA wsaData;
int err;
wVersionRequested = MAKEWORD( 2, 2 );
err = WSAStartup( wVersionRequested, &wsaData );
if ( err != 0 ) {
return 0;
}
if ( LOBYTE( wsaData.wVersion ) != 2 ||
HIBYTE( wsaData.wVersion ) != 2 ) {
WSACleanup( );
return 0;
}
SOCKET socketClient=socket(AF_INET,SOCK_STREAM,0);
SOCKADDR_IN addrServer;
addrServer.sin_family=AF_INET;
addrServer.sin_addr.S_un.S_addr=inet_addr(".0.0.1");
addrServer.sin_port=htons();
char sendBuf[];
char recvBuf[];
ããprintf("客æ·ç«¯åæå¡å¨ç«¯è¿æ¥è¯·æ±...\n");
ããint Isconnect=connect(socketClient, (struct sockaddr *)&addrServer, sizeof(struct sockaddr));
ããif(Isconnect!=0)
{
printf("客æ·ç«¯æ æ³è¿æ¥æå¡å¨ç«¯...\n");
ããreturn 0;
}
ããprintf("客æ·ç«¯å·²æåè¿æ¥æå¡å¨ç«¯...\n");
ããmemset(recvBuf,0,);
recv(socketClient,recvBuf,,0);
ããprintf("客æ·ç«¯æ¶å°ä¿¡æ¯:%s\n",recvBuf);
ããmemset(sendBuf,0,);
sprintf(sendBuf,"Hello , I am Mr Wen !");
send(socketClient, sendBuf, strlen(sendBuf)+1,0);
closesocket(socketClient);
WSACleanup();
return 0;
}
2024-11-30 10:33
2024-11-30 10:31
2024-11-30 10:21
2024-11-30 09:29
2024-11-30 08:49
2024-11-30 07:59