Форум программистов, компьютерный форум, киберфорум
C# для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 5.00/9: Рейтинг темы: голосов - 9, средняя оценка - 5.00
0 / 0 / 0
Регистрация: 19.07.2016
Сообщений: 16

Получение IP собеседников в Steam

19.07.2016, 12:25. Показов 2027. Ответов 11
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Собственно, нужно реализовать метод получения ip собеседников в Steam голосовом чате. Их можно получить с помощью программ типа wireshark, net limiter. Но каким образом это можно сделать в c#? Нужно как-то отсортировать все tcp соединения, чтобы показывало только соединения процесса steam.exe, причем только те, по которым идет обмен данных. Нагуглить ничего не получилось, только это, но по моему это мне не поможет никак.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
IPGlobalProperties IPGproperties = IPGlobalProperties.GetIPGlobalProperties();
 
TcpConnectionInformation[] connections = IPGproperties.GetActiveTcpConnections();
 
foreach (TcpConnectionInformation tcp in connections)
{
            //Displaying TCP connection informations
            StringBuilder sb = new StringBuilder();
            sb.Append("Local endpoint:\t"+tcp.LocalEndPoint.Address.ToString());
            sb.Append("\nRemote endpoint:\t"+ tcp.RemoteEndPoint.Address.ToString());
            sb.Append("\nState:\t\t"+ tcp.State.ToString());
            MessageBox.Show(sb.ToString(),"TCP connection status");
}
0
Лучшие ответы (1)
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
19.07.2016, 12:25
Ответы с готовыми решениями:

Steam API, получение ID пользователя
В общем у меня на сайте есть функция "использовать аватар из Steam", но вот я не очень в нем разобрался, и для пользователей, которые хотят...

Получение списка вещей в инвентаре Steam
Привет! Задачка простая. Есть 2 TextBox и 1 Button. В первый TextBox вводим ссылку на профиль в Steam, и, если профиль открыт, то...

Авторизация по steam протоколу и получение списка игр
Собственно помогите, в какую сторону копать, пытался снифать пакеты которые идут от стима варшарком, но там нечего не понял.

11
146 / 143 / 32
Регистрация: 21.01.2012
Сообщений: 545
20.07.2016, 16:08
KillReal, Таким образом можно получить лишь адрес сервера обмена сообщениями
0
0 / 0 / 0
Регистрация: 19.07.2016
Сообщений: 16
20.07.2016, 16:17  [ТС]
КeBJIaP, Нет, соединение устанавливается напрямую между компьютерами(Только в голосовом чате !Не обычный чат!), как я сказал, адрес можно получить с помощью программ просмотра текущих соединений. Так каким образом это можно реализовать в c#?
0
Эксперт .NET
 Аватар для Usaga
14301 / 9386 / 1353
Регистрация: 21.01.2016
Сообщений: 35,385
21.07.2016, 13:31
KillReal, почему-то мне кажется, что голосовой чат работает через сторонний сервер и не использует прямое соединение между двумя машинами потому, что очень многие клиенты сидят за NAT-ами и тупо недоступны извне.
0
0 / 0 / 0
Регистрация: 19.07.2016
Сообщений: 16
29.07.2016, 12:00  [ТС]
Usaga, Тогда как я получаю ip адрес собеседников через NetLimiter 4 например?
0
Эксперт .NET
 Аватар для Usaga
14301 / 9386 / 1353
Регистрация: 21.01.2016
Сообщений: 35,385
29.07.2016, 12:06
KillReal, скорее всего это адреса их (или провайдера) маршрутизаторов.
0
0 / 0 / 0
Регистрация: 19.07.2016
Сообщений: 16
29.07.2016, 18:46  [ТС]
Usaga, Нет, ip которые я получаю, именно те, которые мои собеседники видят на 2ip.ru и подобных сайтах. Вы собираетесь помочь или так и будете убеждать, что ip адрес собеседника получить невозможно?
0
 Аватар для IamRain
4694 / 2702 / 734
Регистрация: 02.08.2011
Сообщений: 7,233
29.07.2016, 19:46
Лучший ответ Сообщение было отмечено KillReal как решение

Решение

А вот эту статью не смотрели: http://www.codeproject.com/Art... s-on-a-box?
Немного переделанный вариант:
main.cs
Кликните здесь для просмотра всего текста

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
namespace IpHlpApidotnet
{
    public  class IPHelper
    {
        private const int NO_ERROR  = 0;
        private const int MIB_TCP_STATE_CLOSED = 1;
        private const int MIB_TCP_STATE_LISTEN = 2;
        private const int MIB_TCP_STATE_SYN_SENT = 3;
        private const int MIB_TCP_STATE_SYN_RCVD = 4;
        private const int MIB_TCP_STATE_ESTAB = 5;
        private const int MIB_TCP_STATE_FIN_WAIT1 = 6;
        private const int MIB_TCP_STATE_FIN_WAIT2 = 7;
        private const int MIB_TCP_STATE_CLOSE_WAIT = 8;
        private const int MIB_TCP_STATE_CLOSING = 9;
        private const int MIB_TCP_STATE_LAST_ACK = 10;
        private const int MIB_TCP_STATE_TIME_WAIT = 11;
        private const int MIB_TCP_STATE_DELETE_TCB = 12;
 
        
 
        
        /*
         * Tcp Struct
         * */
 
        public IpHlpApidotnet.MIB_TCPTABLE TcpConnexion;
        public IpHlpApidotnet.MIB_TCPSTATS TcpStats;
        public IpHlpApidotnet.MIB_EXTCPTABLE TcpExConnexions;
        
 
        /*
         * Udp Struct
         * */
        public IpHlpApidotnet.MIB_UDPSTATS UdpStats;
        public IpHlpApidotnet.MIB_UDPTABLE UdpConnexion;
        public IpHlpApidotnet.MIB_EXUDPTABLE UdpExConnexion;
        
        
        
 
 
 
        public void GetExTcpConnexions(string processName)
        {
 
            var process = Process.GetProcessesByName(processName).FirstOrDefault();
            if (process != null)
                  GetExTcpConnexions(process.Handle);
        }
 
        public void GetExTcpConnexions(IntPtr processHandle)
        {
            
            // the size of the MIB_EXTCPROW struct =  6*DWORD
            int rowsize = 24;
            int BufferSize = 100000;
            // allocate a dumb memory space in order to retrieve  nb of connexion
            IntPtr lpTable = Marshal.AllocHGlobal(BufferSize);
            //getting infos
            int res = IPHlpAPI32Wrapper.AllocateAndGetTcpExTableFromStack(ref lpTable, true, processHandle,0,2);
            if(res!=NO_ERROR)
            {
                Debug.WriteLine("Erreur : "+IPHlpAPI32Wrapper.GetAPIErrorMessageDescription(res)+" "+res);
                return; // Error. You should handle it
            }
            int CurrentIndex = 0;
            //get the number of entries in the table
            int NumEntries= (int)Marshal.ReadIntPtr(lpTable);
            lpTable = IntPtr.Zero;
            // free allocated space in memory
            Marshal.FreeHGlobal(lpTable);
 
 
 
            ///////////////////
            // calculate the real buffer size nb of entrie * size of the struct for each entrie(24) + the dwNumEntries
            BufferSize = (NumEntries*rowsize)+4;
            // make the struct to hold the resullts
            TcpExConnexions = new IpHlpApidotnet.MIB_EXTCPTABLE();
            // Allocate memory
            lpTable = Marshal.AllocHGlobal(BufferSize);
            res = IPHlpAPI32Wrapper.AllocateAndGetTcpExTableFromStack(ref lpTable, true, processHandle ,0,2);
            if(res!=NO_ERROR)
            {
                Debug.WriteLine("Erreur : "+IPHlpAPI32Wrapper.GetAPIErrorMessageDescription(res) + " " + res);
                return; // Error. You should handle it
            }
            // New pointer of iterating throught the data
            IntPtr current = lpTable;
            CurrentIndex = 0;
            // get the (again) the number of entries
            NumEntries = (int)Marshal.ReadIntPtr(current);
            TcpExConnexions.dwNumEntries =  NumEntries;
            // Make the array of entries
            TcpExConnexions.table = new MIB_EXTCPROW[NumEntries];
            // iterate the pointer of 4 (the size of the DWORD dwNumEntries)
            CurrentIndex+=4;
            current = (IntPtr)((int)current+CurrentIndex);
            // for each entries
            for(int i=0; i< NumEntries;i++)
            {
                
                // The state of the connexion (in string)
                TcpExConnexions.table[i].StrgState = this.convert_state((int)Marshal.ReadIntPtr(current));
                // The state of the connexion (in ID)
                TcpExConnexions.table[i].iState = (int)Marshal.ReadIntPtr(current);
                // iterate the pointer of 4
                current = (IntPtr)((int)current+4);
                // get the local address of the connexion
                UInt32 localAddr = (UInt32)Marshal.ReadIntPtr(current);
                // iterate the pointer of 4
                current = (IntPtr)((int)current+4);
                // get the local port of the connexion
                UInt32 localPort = (UInt32)Marshal.ReadIntPtr(current);
                // iterate the pointer of 4
                current = (IntPtr)((int)current+4);
                // Store the local endpoint in the struct and convertthe port in decimal (ie convert_Port())
                TcpExConnexions.table[i].Local = new IPEndPoint(localAddr,(int)convert_Port(localPort));
                // get the remote address of the connexion
                UInt32 RemoteAddr = (UInt32)Marshal.ReadIntPtr(current);
                // iterate the pointer of 4
                current = (IntPtr)((int)current+4);
                UInt32 RemotePort=0;
                // if the remote address = 0 (0.0.0.0) the remote port is always 0
                // else get the remote port
                if(RemoteAddr!=0)
                {
                    RemotePort = (UInt32)Marshal.ReadIntPtr(current);
                    RemotePort=convert_Port(RemotePort);
                }
                current = (IntPtr)((int)current+4);
                // store the remote endpoint in the struct  and convertthe port in decimal (ie convert_Port())
                TcpExConnexions.table[i].Remote = new IPEndPoint(RemoteAddr,(int)RemotePort);
                // store the process ID
                TcpExConnexions.table[i].dwProcessId = (int)Marshal.ReadIntPtr(current);
                // Store and get the process name in the struct
                TcpExConnexions.table[i].ProcessName = this.get_process_name(TcpExConnexions.table[i].dwProcessId);
                current = (IntPtr)((int)current+4);
        
            }
            // free the buffer
            Marshal.FreeHGlobal(lpTable);
            // re init the pointer
            current = IntPtr.Zero;
        }
 
        
 
        #region Udp Functions
 
        public void GetUdpStats()
        {
            
            UdpStats = new MIB_UDPSTATS();
            IPHlpAPI32Wrapper.GetUdpStatistics(ref UdpStats);
        }
 
        
 
 
        #endregion
 
        #region helper fct
 
        private UInt16 convert_Port(UInt32 dwPort)
        {
            byte[] b = new Byte[2];
            // high weight byte
            b[0] = byte.Parse((dwPort>>8).ToString());
            // low weight byte
            b[1] = byte.Parse((dwPort & 0xFF).ToString());
            return BitConverter.ToUInt16(b,0);
        }
 
 
        private string convert_state(int state)
        {
            string strg_state="";
            switch(state)
            {
                case MIB_TCP_STATE_CLOSED: strg_state = "CLOSED" ;break;
                case MIB_TCP_STATE_LISTEN: strg_state = "LISTEN" ;break;
                case MIB_TCP_STATE_SYN_SENT: strg_state = "SYN_SENT" ;break;
                case MIB_TCP_STATE_SYN_RCVD: strg_state = "SYN_RCVD" ;break;
                case MIB_TCP_STATE_ESTAB: strg_state = "ESTAB" ;break;
                case MIB_TCP_STATE_FIN_WAIT1: strg_state = "FIN_WAIT1" ;break;
                case MIB_TCP_STATE_FIN_WAIT2: strg_state = "FIN_WAIT2" ;break;
                case MIB_TCP_STATE_CLOSE_WAIT: strg_state = "CLOSE_WAIT" ;break;
                case MIB_TCP_STATE_CLOSING: strg_state = "CLOSING" ;break;
                case MIB_TCP_STATE_LAST_ACK: strg_state = "LAST_ACK" ;break;
                case MIB_TCP_STATE_TIME_WAIT: strg_state = "TIME_WAIT" ;break;
                case MIB_TCP_STATE_DELETE_TCB: strg_state = "DELETE_TCB" ;break;
            }
            return strg_state;
        }
 
 
        private string get_process_name(int processID)
        {
            //could be an error here if the process die before we can get his name
            try
            {
                Process p = Process.GetProcessById((int)processID);
                return p.ProcessName;
            }
            catch(Exception ex)
            {
                Debug.WriteLine(ex.Message);
                return "unknown";
            }
                
        }
        #endregion
    }
}



// IPHlpAPI32cs
Кликните здесь для просмотра всего текста

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
namespace IpHlpApidotnet
{
    #region UDP
    
    [StructLayout(LayoutKind.Sequential)]
    public struct MIB_UDPSTATS
    {
        public int dwInDatagrams ;
        public int dwNoPorts ;
        public int dwInErrors ;
        public int dwOutDatagrams ;
        public int dwNumAddrs;
    }
 
    public struct MIB_UDPTABLE 
    {
        public int dwNumEntries;  
        public MIB_UDPROW[] table;
 
    }
 
    public struct MIB_UDPROW 
    {
        public IPEndPoint Local;
    }
 
    public struct MIB_EXUDPTABLE
    {
        public int dwNumEntries;  
        public MIB_EXUDPROW[] table;
 
    }
 
    public struct MIB_EXUDPROW
    {
        public IPEndPoint Local;  
        public int dwProcessId;
        public string ProcessName;
    }
 
    #endregion
 
    #region TCP
    [StructLayout(LayoutKind.Sequential)]
    public struct MIB_TCPSTATS
    {
        public int dwRtoAlgorithm ;
        public int dwRtoMin ;
        public int dwRtoMax ;
        public int dwMaxConn ;
        public int dwActiveOpens ;
        public int dwPassiveOpens ;
        public int dwAttemptFails ;
        public int dwEstabResets ;
        public int dwCurrEstab ;
        public int dwInSegs ;
        public int dwOutSegs ;
        public int dwRetransSegs ;
        public int dwInErrs ;
        public int dwOutRsts ;
        public int dwNumConns ;
    }
 
 
    public struct MIB_TCPTABLE 
    {
        public int dwNumEntries;  
        public MIB_TCPROW[] table;
 
    }
 
    public struct MIB_TCPROW 
    {
        public string StrgState; 
        public int iState;
        public IPEndPoint Local;  
        public IPEndPoint Remote;
    }
 
    public struct MIB_EXTCPTABLE
    {
        public int dwNumEntries;  
        public MIB_EXTCPROW[] table;
 
    }
 
    public struct MIB_EXTCPROW
    {
        public string StrgState;
        public int iState;
        public IPEndPoint Local;  
        public IPEndPoint Remote;
        public int dwProcessId;
        public string ProcessName;
    }
    
 
    
 
    #endregion
 
    
    public class IPHlpAPI32Wrapper
    {
        public const byte NO_ERROR  = 0;
        public const int FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;
        public const int FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
        public const int FORMAT_MESSAGE_FROM_SYSTEM  = 0x00001000;
        public int dwFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | 
            FORMAT_MESSAGE_FROM_SYSTEM | 
            FORMAT_MESSAGE_IGNORE_INSERTS;
 
        
 
        [DllImport("iphlpapi.dll",SetLastError=true)]
        public extern static int GetUdpStatistics (ref MIB_UDPSTATS pStats );
 
        [DllImport("iphlpapi.dll",SetLastError=true)]
        public static extern int GetUdpTable(byte[] UcpTable, out int pdwSize, bool bOrder);
 
        [DllImport("iphlpapi.dll",SetLastError=true)]
        public extern static int GetTcpStatistics (ref MIB_TCPSTATS pStats );
 
        [DllImport("iphlpapi.dll",SetLastError=true)]
        public static extern int GetTcpTable(byte[] pTcpTable, out int pdwSize, bool bOrder);
 
        [DllImport("iphlpapi.dll",SetLastError=true)]
        public extern static int AllocateAndGetTcpExTableFromStack(ref IntPtr pTable, bool bOrder, IntPtr heap ,int zero,int flags );
 
        [DllImport("iphlpapi.dll",SetLastError=true)]
        public extern static int AllocateAndGetUdpExTableFromStack(ref IntPtr pTable, bool bOrder, IntPtr heap,int zero,int flags );
 
        [ DllImport( "kernel32" ,SetLastError=true)]
        private static extern int FormatMessage( int flags, IntPtr source, int messageId,
            int languageId, StringBuilder buffer, int size, IntPtr arguments ); 
 
 
        public static string GetAPIErrorMessageDescription(int ApiErrNumber ) 
        {
            System.Text.StringBuilder sError = new System.Text.StringBuilder(512); 
            int lErrorMessageLength; 
            lErrorMessageLength = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,(IntPtr)0, ApiErrNumber, 0, sError, sError.Capacity, (IntPtr)0) ;
            
            if(lErrorMessageLength > 0)
            { 
                string strgError = sError.ToString();
                strgError=strgError.Substring(0,strgError.Length-2);
                return strgError+" ("+ApiErrNumber.ToString()+")";
            }
            return "none";
 
        }
 
         
    }
}

Не могу протестировать, поскольку не пользуюсь чатом steam. Вообщем, разбирайтесь.
1
0 / 0 / 0
Регистрация: 19.07.2016
Сообщений: 16
29.07.2016, 20:43  [ТС]
IamRain, Спасибо большое! Не видел такой, видимо где-то проглядел. Буду изучать.
0
30.07.2016, 04:54

Не по теме:

KillReal, ну так-то этот 2ip.ru тоже видит "внешний" IP твоих собеседников :D Попроси своих собеседников сравнить адрес полученный с этого сайта и адрес, который выдаёт консольная утилита IPCONFIG. Ты можешь сильно удивиться :)

0
0 / 0 / 0
Регистрация: 19.07.2016
Сообщений: 16
30.07.2016, 16:23  [ТС]
Usaga, Мне нужен внешний ip, который они могут получить через подобные сайты, а не ip, которые в IPCONFIG......
0
 Аватар для IamRain
4694 / 2702 / 734
Регистрация: 02.08.2011
Сообщений: 7,233
30.07.2016, 19:22
Цитата Сообщение от IamRain Посмотреть сообщение
Не могу протестировать, поскольку не пользуюсь чатом steam
Фигню написал, вообщем вот небольшая утилитка на основе указанной статьи.
Вложения
Тип файла: rar ProcessConnections.rar (77.9 Кб, 9 просмотров)
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
30.07.2016, 19:22
Помогаю со студенческими работами здесь

Получение данных об игре в Steam по названию или appid
Есть название игры, например &quot;Rust&quot;, по этому названию необходимо получить данные об игре через Steam API, а конкретно: цену, описание,...

Скрипт случайных собеседников
Здравствуйте,хотелось понять принцип работы скрипта,где например пользователь случайно попадает на другого пользователя,с возможностью...

Написать чат, с количеством возможных собеседников — 3 и больше
Добрый вечер, ув. форумчане. Мне нужно написать чат, с количеством возможных собеседников - 3 и больше(под Винду, не консольное...

В скайпе слова моих собеседников повторяются дважды
Здравствуйте, у меня со скайпом ерунда какая-то меня все хорошо слышат, а я слышу, но все слова моих собеседников повторяются дважды....

Social Club + Steam - привязать аккаунт Steam к аккаунту Social Club
Купил на распродаже Max Payne 3. Сначала он просто отказывался запускаться, поставил режим совместимости с Vista SP2 - запускаться стал, но...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
12
Ответ Создать тему
Новые блоги и статьи
SDL3 для Desktop (MinGW): Создаём пустое окно с нуля для 2D-графики на SDL3, Си и C++
8Observer8 10.03.2026
Содержание блога Финальные проекты на Си и на C++: hello-sdl3-c. zip hello-sdl3-cpp. zip Результат:
Установка CMake и MinGW 13.1 для сборки С и C++ приложений из консоли и из Qt Creator в EXE
8Observer8 10.03.2026
Содержание блога MinGW - это коллекция инструментов для сборки приложений в EXE. CMake - это система сборки приложений. Здесь описаны базовые шаги для старта программирования с помощью CMake и. . .
Как дизайн сайта влияет на конверсию: 7 решений, которые реально повышают заявки
Neotwalker 08.03.2026
Многие до сих пор воспринимают дизайн сайта как “красивую оболочку”. На практике всё иначе: дизайн напрямую влияет на то, оставит человек заявку или уйдёт через несколько секунд. Даже если у вас. . .
Модульная разработка через nuget packages
DevAlt 07.03.2026
Сложившийся в . Net-среде способ разработки чаще всего предполагает монорепозиторий в котором находятся все исходники. При создании нового решения, мы просто добавляем нужные проекты и имеем. . .
Модульный подход на примере F#
DevAlt 06.03.2026
В блоге дяди Боба наткнулся на такое определение: В этой книге («Подход, основанный на вариантах использования») Ивар утверждает, что архитектура программного обеспечения — это структуры,. . .
Управление камерой с помощью скрипта OrbitControls.js на Three.js: Вращение, зум и панорамирование
8Observer8 05.03.2026
Содержание блога Финальная демка в браузере работает на Desktop и мобильных браузерах. Итоговый код: orbit-controls-threejs-js. zip. Сканируйте QR-код на мобильном. Вращайте камеру одним пальцем,. . .
SDL3 для Web (WebAssembly): Синхронизация спрайтов SDL3 и тел Box2D
8Observer8 04.03.2026
Содержание блога Финальная демка в браузере. Итоговый код: finish-sync-physics-sprites-sdl3-c. zip На первой гифке отладочные линии отключены, а на второй включены:. . .
SDL3 для Web (WebAssembly): Идентификация объектов на Box2D v3 - использование userData и событий коллизий
8Observer8 02.03.2026
Содержание блога Финальная демка в браузере. Итоговый код: finish-collision-events-sdl3-c. zip Сканируйте QR-код на мобильном и вы увидите, что появится джойстик для управления главным героем. . . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru